Chief Delphi

Chief Delphi (http://www.chiefdelphi.com/forums/index.php)
-   Programming (http://www.chiefdelphi.com/forums/forumdisplay.php?f=51)
-   -   Gyro Repeatability, Expected Drift ? (http://www.chiefdelphi.com/forums/showthread.php?t=65732)

eugenebrooks 13-03-2008 03:23

Re: Gyro Repeatability, Expected Drift ?
 
Quote:

Originally Posted by de_ (Post 717267)
Thanks everyone.

Dr Brooks:
- would greatly appreciate your code spinnets.
- what gyro chip is your team using ?
- given you calculate the bias in the pits and hard code it, am I correct that the bias is individual chip by chip specific and is temperature specific but once in a stable temperature, it can be calced and hardcoded for the duration of the regional ?

Kevin, thanks for the clear coverage on the deadband. I can see why you do it. We are looking closely at the Sparkfun unit. We are just waiting for clarification as to whether using it is as simple as connecting the 5v, grd and rate to AI 1. Unfortunately due to our location, it will cost us over $60 in shipping alone :(

Sorry for the delay. Trying to hold down a job and also get ready
for the San Jose Regional keeps you busy.

We are using the SparkFun mounting of the ADXRS150. We also
have one of the IC style mountings from digikey on our test mule,
but we bought the SparkFun rendition this year to avoid etching
a PC board during a busy build period.

All you have to hook up on the SparkFun unit is 5v, signal, and gnd.
The other outputs support advanced compensation methods.

The biggest effect on the bias is the temperature. The temperature
of the autoshop where we build and test the robot is different than
that at a regional, so one has to recalibrate the bias if one is not using
temperature compensation. There is also power supply voltage
compensation, but the 5 volt supply should be pretty well regulated
unless there are large loads placed on it.

I have not had any time to package any code up, but here are
some nippets on how we do things. Note the conditional compilation
for the timer interrupt handler. One calculates the integral,
the other calculates the sum of 256 samples. To avoid doing
many sums and averaging, you can do 2560 and then divide by
10, rounding.

/* Initialize_Timer_1() sets up the timer used to
control the time integration for the rotational rate gyro.
Called from User_Initialization() in user_routines.c.
Refer to Kevin Watson's "frc_interrupts" code for the
details and additional examples of other timers. Kevin
also has cached copies of the PIC Microcontroller documentation
on his web site at http://kevin.org/frc/. If you contemplate
any adjustments you will need to study this documentation carefully.
The only changes we recommend to this routine are to select
different prescaler values in order to slow down the interrupt
rate if desired. Out of respect for Nyquist, you should not drop
the interrupt rate below twice the bandwidth of the gyro.
*/
void Initialize_Timer_1(void)
{
/* Initialize the 16 bit timer register.
*/
TMR1L = 0x00;
TMR1H = 0x00;

/* Set the prescaler for a 10 MHz rate. With the interrupt happening on the
overflow of a 16 bit counter, the interrupt rate is 10MHz / 2^16, or 152.59 Hz.
The timer interrupt rate can be reduced by a factor of 2, 4, or 8 by picking
one of the other prescaler values.
*/
T1CONbits.T1CKPS0 = 0; // T1CSP1 T1CSP0
T1CONbits.T1CKPS1 = 0; // 0 0 1:1 prescaler (clock=10MHz/each tick=100ns)
// 0 1 1:2 prescaler (clock=5MHz/each tick=200ns)
// 1 0 1:4 prescaler (clock=2.5MHz/each tick=400ns)
// 1 1 1:8 prescaler (clock=1.25MHz/each tick=800ns)
//
T1CONbits.T1OSCEN = 0; /* Leave the timer 1 internal oscillator disaled. */
T1CONbits.TMR1CS = 0; /* Use the internal clock. */
T1CONbits.RD16 = 1; /* Do timer 1 register operations in one 16 bit access. */
IPR1bits.TMR1IP = 0; /* Set interrupt priority to low. */
PIR1bits.TMR1IF = 0; /* Clear any existing interrupt. */
PIE1bits.TMR1IE = 1; /* Enable interrupt on overflow, a transition of the counter from FFFF -> 0. */
T1CONbits.TMR1ON = 1; /* Enable the timer. */
}

static volatile signed long GyroIntegral = 0; // in 256ths
static volatile unsigned char GyroIntegralSentinel = 0;
static volatile unsigned long OldGyroValue = 0;
static volatile int AverageCycleCount = 0;

/* The value for GyroAverage is obtained by compiling the code
to sample and produce the average upon the trigger pull. You
sample the average a number of times and average the results to
produce the value that is hard coded here.
*/
static volatile unsigned long GyroAverage = 131419; // in 256ths

/* Timer_1_Int_Handler() handles that interrupt based calculation
of the average of the gyro signal. A sentinel is used to inform
the interrupt handler that the gyro integral is being zeroed by
user code.
*/
#define GYROINPUT rc_ana_in01
#ifdef GYROAVERAGE
void Timer_1_Int_Handler(void)
{
if(GyroIntegralSentinel == 0) {
if(AverageCycleCount < 256) {
GyroIntegral += Get_Analog_Value(GYROINPUT);
AverageCycleCount += 1;
}
}
}
#else
void Timer_1_Int_Handler(void) {
if(GyroIntegralSentinel == 0) {
unsigned long NewGyroValue = Get_Analog_Value(GYROINPUT);
GyroIntegral += ((OldGyroValue + NewGyroValue) << 7) - GyroAverage;
OldGyroValue = NewGyroValue;
}
else {
OldGyroValue = Get_Analog_Value(GYROINPUT);
}
}
#endif

/* GetGyroIntegral() returns the current value of the time integral
of the rotational rate gyro. As interrupts are active, the value read
is reliable if two successive reads produce the same value.
*/
signed long GetGyroIntegral(void) {
signed long Old, New;
Old = GyroIntegral;
while(Old != (New = GyroIntegral)) {
Old = New;
}
#ifdef GYROAVERAGE
return Old;
#else
return Old / 10000L;
#endif
}

/* ZeroGyroIntegral() zeros the average of the rotational rate
gyro signal that is calculated via the timer interrupt.
A sentinel is used to inform the interrupt handler that the
values are in the process of being zeroed by user code.
*/
void ZeroGyroIntegral(void) {
GyroIntegralSentinel = 1;
GyroIntegral = 0;
AverageCycleCount = 0;
GyroIntegralSentinel = 0;
}


The interrupt handler setup is what you would expect:
#pragma interruptlow InterruptHandlerLow save=PROD,section("MATH_DATA"),section(".tmpdata")

void InterruptHandlerLow ()
{
unsigned char int_byte;
if (PIR1bits.TMR1IF && PIE1bits.TMR1IE) { /* Timer 1 */
PIR1bits.TMR1IF = 0;
Timer_1_Int_Handler();
}
else if (INTCON3bits.INT2IF && INTCON3bits.INT2IE) { /* The INT2 pin is RB2/DIG I/O 1. */
INTCON3bits.INT2IF = 0;
Int_1_Handler();
}

The setup of the timer gets put in User_Initialization


Initialize_Timer_1();

Putdata(&txdata); /* DO NOT CHANGE! */


And, finally, some code in the main packet loop allows you
to print the value of sum, or integral, and reset it

First, a useful function for printing longs
#define RLEN 10 /* Maximum decimal digits for an unsigned long. */

/* The function puln(unsigned long i, unsigned char n)
prints "i" in decimal format, using leading zeros to fill
out a minimum field width of "n" characters.
*/
void puln(unsigned long i, unsigned char n) {
unsigned char result[RLEN]; /* Stores the result digits, as numbers. */
unsigned char reversed[RLEN]; /* Storage for the digits as they come off. */
char rpos; /* Index for reversed array. */
char rindex; /* Index for result array. */
int j;

/* The minimum field width is one.
*/
if(n == 0) {
n = 1;
}
/* Use remainder and divide in a loop to peel
off the digits, least significant first.
*/
rpos = 0;
while(i > 0) {
reversed[rpos] = i % 10;
rpos += 1;
i /= 10;
}
/* At this point the digits are in the reversed
array and rpos records the count. If rpos is zero,
the argument was zero. We must first place the
required number of leading zeros in the result.
*/
rindex = 0;
while((rindex + rpos) < n) {
result[rindex] = 0;
rindex += 1;
}
/* Fill in the digits, reversing the order.
*/
while(rpos > 0) {
result[rindex] = reversed[rpos-1];
rindex += 1;
rpos -= 1;
}
/* You gotta just love the fact that printf() on the
RC can't handle a character string in data memory!
Don't forget the cast to (int) for the RC...
*/
for(j = 0; j < rindex; j += 1) {
printf("%d", (int)result[j]);
}
}

/* Print a signed long.
*/
void pl(long i) {
if(i < 0) {
printf("-");
i = -i;
}
puln(i, 1);
printf("\r");
}

/* Print an unsigned long.
*/
void pul(unsigned long i) {
puln(i, 1);
printf("\r");
}

/* The function void pfp(long i, unsigned char bits, unsigned char digits)
prints a binary fixed point value in decimal format. The arguments are:
i, the integer holding the fixed point value; bits, the number of bits to
place after the binary pont; and digits, the number of digits after the
decimal point to print.
*/
void pfp(long i, unsigned char bits, unsigned char digits) {
long intpart;
long mask;
long powten;
unsigned char j;

/* If the argument is negative, print the sign and work
with the absolute value so that shifts give us a divide.
*/
if(i < 0) {
printf("-");
i = -i;
}

/* Shift the argument down to obtain the integer
part, and turn the result over to puln() to handle.
*/
intpart = i >> bits;
puln(intpart,1);

printf(".");

/* Construct the mask for the fractional part.
*/
mask = 0; /* All zeros. */
mask = ~mask; /* All ones. */
mask <<= bits; /* Shift in bits zeros. */
mask = ~mask; /* Flip to get bits ones. */

/* Use the mask to obtain the fractional part.
*/
i &= mask;

/* Convert the fraction to a decimal fraction.
*/
powten = 1;
for(j = 1; j <= digits; j += 1) {
i *= 10;
powten *= 10;
}
i >>= bits;

/* At this point, we print the decimal
fraction with the correct number of digits
using leading zeros.
*/
puln(i, digits);

/* And finally, the optional newline.
*/
printf("\r");
}


And then using it to print the sum, or integral
#ifdef NOTDEF
if(p3_sw_trig == 1) {
printf("trig pressed\r");
ZeroGyroIntegral();
}
if(counter % 40 == 0) {
printf("Gyro Sum = ");
pl(GetGyroIntegral());
}
#endif

I never define NOTDEF by convention, and if I want the
code in I change ifdef to ifndef

In this case, if the named trigger is pressed, the gyrointegral,
or the sum is zeroed. In the case of the integral, you can then watch
the drift. It is an integer in roughly tenths of a degree.
In the case of the sum, it steadily increases until the count
in the interrupt handler is reached, and then you hard code
the number into the integrator. We usually use the average
of 10 or 20 samples, and compute the average with a calculator.
This can be more automatically done by changing 256 to 2560
in the timer interrupt handler and then dividing the result by 10.
You could take it to its limit by using 25600 samples and dividing
by 100, rounding the result.

It is all pretty straight forward. Check the drift perhaps a couple
of times a day, and if the drift gets to be too much for you,
redo the calibration. A "big" drift is perhaps one degree, or
at most two, during the 15 second autonomous period.
By recalibration you can reduce the drift to a tenth of a degree
or so.


I have had time to look for the Amtel oversampling documentation
and look at it this evening. The goal of that approach is to improve
the resolution of the direct gyro signal by oversampling, using the
noise to produce a higher resolution average. In essense, we
are playing the same game with the integration approach laid
out above and also suggested many times by Chris Hibner.
One need not oversample so much to exploit the technique when
what you want is the integral, and in fact there is a point of diminishing
returns due to the increased correlation between successive points.

If what I wanted was the best possible direct rotation rate signal,
I would use the Amtel approach that Kevin has coded, but since what
I want is the integral, the strategy shown above is, in my view, the best one
and it does not require one to burden the PIC processor with a high
interrupt rate.

Gotta get some sleep,
it will be a long day tomorrow.

Eugene

de_ 14-03-2008 15:50

Re: Gyro Repeatability, Expected Drift ?
 
Thanks for the outstanding heads up on this stuff.
I waited one day and now both Sparkfun and Digikey went out of stock on the ADXRS300 stuff. But Digikey is due back soon.

Our Robot (in quarantine) currently has the 2008 gyro. A test robot I believe has the 2007 and I am testing at home with which is now clear to me to be the 2006 gyro. It sounds like the 2007 and 2006 have twice the max rate of the 2008 so maybe we should use them on the robot. However, it is the 2006 that is having the repeatabilitity issues on a compass board.


All times are GMT -5. The time now is 13:28.

Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.
Copyright © Chief Delphi