I am hoping for Kevin Watson to chime in on this one. I believe he was around way back when.
I have a BEI gyro I am trying to use for a VEX robot. I wrote a program for it, but it seems to be in accurate. I am using a 10 bit ADC. with a 1khz sampling frequency. I have simply written a (de)accumulator program. This just cuts the ADC reading from 10 to 8 bits, and either adds or subtracts the reading depending on the reading. The problem is that it seems to be very inaccurate. I could be my program, but it doesn’t seem to be. I turn the gyro slowly, 90 degrees, and then back 90 degrees. it will return to a range of +/-40.
Not degrees, these are just raw values from the ADC!
I have a defined dead band of about .2 volts at the neutral position. When I turn I make sure the voltage output is outside of the dead band. I check this using a DMM. The other weird problem is the dead band is at 2 volts instead of 2.5 volts, which is what it should be.
The analog reference is about 5.1 volts.
If it matters at all I am using at ATMEGA16 microcontroller, and programming it using a STK500 dev board.
Hey John. Here is my $.02.
To use a gyro with Vex, I just integrated Kevin’s Gyro code into the 2007 default FVC code. I run the ADC sample at 1600Hz with a 32 samples per update, giving me 50 averaged samples per second. It really works fairly well.
Now as for your ATMega16, I might be able to help, a little.:rolleyes:
I just wrote code for my Arduino that samples a 2005 KOP Gyro at 800Hz, 4 samples per update. It is based on Kevin’s gyro code as well but adapted for the Arduino. As you might be aware, the Arduino runs on an ATMega168. I will gladly share this code with you. In fact, I’ll just paste it in here now so people can chew on it for a while.
unsigned int latency;
unsigned char timerLoadValue;
int GyroPin = 5;
volatile int val;
volatile int gyroBias;
int bias_acum;
volatile long gyro_angle;
#define GYRO_READY 13 //Arduino pin to toggle in timer ISR
#define SAMPLES_PER_UPDATE 4 // # of Gyro samples to average before updating accum
#define DEADBAND 6
#define GYRO_GAIN_CAL 1000/1000
volatile unsigned char sample_counter = 0; // Used in Timer ISR
volatile int sample_accum = 0; //hold sample sum until correct # samples aquired
#define TIMER_CLOCK_FREQ 125000.0 //125KHz for /128 prescale from 16MHz
/*Setup Timer2.***********************************************
Configures the ATMega168 8-Bit Timer2 to generate an interrupt
at the specified frequency.
Returns the timer load value which must be loaded into TCNT2
inside your ISR routine.
See the example usage below.
***********************************************************/
unsigned char SetupTimer2(float timeoutFrequency)//Called once from Setup()
{
unsigned char result; //The timer load value.
//Calculate the timer load value
result=(int)((257.0-(TIMER_CLOCK_FREQ/timeoutFrequency))+0.5);//The 257 really should be 256
//but I get better results with 257.
//Timer2 Settings: Timer Prescaler /128, mode 0
//Timer clock = 16MHz/128 = 125Khz or 8us
//The /128 prescale gives us a good range to work with
//so we just hard code this for now.
TCCR2A = 0;
TCCR2B = 1<<CS22 | 0<<CS21 | 1<<CS20;
//Timer2 Overflow Interrupt Enable
TIMSK2 = 1<<TOIE2;
//load the timer for its first cycle
TCNT2=result;
return(result);
}
/*************************************************
Timer ISR to grag Gyro value at rate
determined in SetuoTimer2
**************************************************/
ISR(TIMER2_OVF_vect)
{
//Toggle the IO pin to the other state.
//digitalWrite(TOGGLE_IO,!digitalRead(TOGGLE_IO));
sample_accum += analogRead(GyroPin);
sample_counter ++;
if (sample_counter == SAMPLES_PER_UPDATE)
{
val = (sample_accum/SAMPLES_PER_UPDATE)-gyroBias;
if (val < -DEADBAND || val > DEADBAND)
{
gyro_angle += (long)val;
}
sample_accum = 0;
sample_counter = 0;
}
//Capture the current timer value. This is how much error we
//have due to interrupt latency and the work in this function
latency=TCNT2;
//Reload the timer and correct for latency.
TCNT2=(latency + timerLoadValue);
}
/**************************************
Get_Gyro_Angle converts data from raw measured value to decimal format and
returns gyro angle in tenths of a degree.
*******************************************/
long Get_Gyro_Angle (void)
{
long temp_gyro_angle;
TIMSK2 = 0<<TOIE2;
temp_gyro_angle = gyro_angle*10L/512L;
TIMSK2 = 1<<TOIE2;
temp_gyro_angle = temp_gyro_angle*GYRO_GAIN_CAL;
return (temp_gyro_angle);
}
/********************************************************
1)Set the pin mode for LED indication the Gyro is ready.
2)Set up Serial baud rate and initialize communication.
3)Calculate the Gyro Bias value.
4)Preconfigure the timer for Gyro measurement.
*********************************************************/
void setup(void)
{
//Set the pin we want the ISR to toggle for output.
pinMode(GYRO_READY,OUTPUT);
//Start up the serial port
Serial.begin(115200);
delay (2000); // allow gyro to power up and stabalize
for(int i=0; i<=29;i++) // Collect 30 samples of Gyro output whilw stationary.
//The average will become the value for the BIAS.
{
bias_acum += analogRead(GyroPin);
delay(100);
}
gyroBias = (bias_acum/30); //analogRead(PotPin);
//Signal the program start
digitalWrite (GYRO_READY, HIGH);
//Start the timer and get the timer reload value.
timerLoadValue=SetupTimer2(800); // Gyro Sample rate is 800Hz
}
void loop(void) {
Serial.print("Gyro value is ");
Serial.println((int)Get_Gyro_Angle());
}
I guess the part I am really interested in is this, because this is what I am having troubles with:
/*************************************************
Timer ISR to grag Gyro value at rate
determined in SetuoTimer2
**************************************************/
ISR(TIMER2_OVF_vect)
{
//Toggle the IO pin to the other state.
//digitalWrite(TOGGLE_IO,!digitalRead(TOGGLE_IO));
sample_accum += analogRead(GyroPin);
sample_counter ++;
if (sample_counter == SAMPLES_PER_UPDATE)
{
val = (sample_accum/SAMPLES_PER_UPDATE)-gyroBias;
if (val < -DEADBAND || val > DEADBAND)
{
gyro_angle += (long)val;
}
sample_accum = 0;
sample_counter = 0;
}
//Capture the current timer value. This is how much error we
//have due to interrupt latency and the work in this function
latency=TCNT2;
//Reload the timer and correct for latency.
TCNT2=(latency + timerLoadValue);
}
What why are you toggling a pin?
The latency part is just accounting for the time spent processing?
Other then those 2 things my ISR is basically the same.
I cant really post the code now because I am at work, but I can give a brief pseudo coding of it.
get adc reading
if it is less then dead band
then subtract from position
if it is greater then dead band
then add to position
else do nothing
send position over USART
I know I am technically using an accumulated rate but lets assume I am using a Riemann sum with a coefficient of 1.
Thanks,
anon96464947
EDIT: I think when I get home I will try to eliminate the dead band, and see if that will help any.
Well, that is what happens when you have compiled bits and pieces of different code, from various times during the creation process together and forget to remove the commented out sections you don’t use anymore.
If you will notice, those two lines are commented out. I had originally used them to toggle an LED on and off so I could measure the rate at which I was generating the ISR. I just attached an o-scope to the LED pins to do the measuring and verification. Once I proved to my self I actually made good assumptions, I commented those lines out. Sorry for the confusion.
The latency part is exactly as you described. It adjusts for the various time spent in the ISR and keeps the sample rate consistent. I suppose I could have streamlined it by doing this.
What is the not moving voltage on your gyro end up being? (Your using a BEI right?) I know it is supposed to be 2.5, but mine is 2.1 volts. That just seems a littles too far off to just be a tolerance issue.
Honestly, I don’t know what my offset, or “bias”, is for this gyro. But as I recall it is near 2.5vdc. In the code below, I do a 30 sample average of the gyro while it is stationary during initialization to determine what my “bias” is. 30 samples takes 3 seconds.
for(int i=0; i<=29;i++) // Collect 30 samples of Gyro output while stationary.
//The average will become the value for the BIAS.
{
bias_acum += analogRead(GyroPin);
delay(100);
}
gyroBias = (bias_acum/30);
The gyro I am using is a Analog Devices ADXRS150 equivalent. In fact the chip is a 22304, but the specs are the same as the 150. It came in the 2005 KOP.
Ok, so I have spent a few hours playing with this. calculating the bias is working well. I added a tolerance constant. This works very well. One thing I noticed is because of my awkward neutral value the linearity of this is well…not linear any more so I had to add in scaling values because it will be mostly consistent in either direction, but the mV/deg/sec isn’t the same for above and below the neutral value. It is more or less decent for getting in the ballpark. But I am beginning to think just by the behavior of the device that it is broken in some way. I know it is a VERY old gyro that i got from my team at least 2 years ago. So I am going to see what it is going to take to get a new working gyro from somewhere. This way I won’t be programming in circles.
Hey John,
I wouldn’t jump to the conclusion that your Gyro is bad yet. Let me explain why. I have been playing with my Gyro and Arduino set up most of the day. I seem to be stuck on virtually the same situation that you seem to be seeing. Simply put, when I rotate my Gyro 180° in a positive direction, I will get approximately 180° read out from the software. When I rotate it in a negative direction I do not get the same -180°. So when I rotate it forward 180 and then backward 180° I always end up with a total somewhere near five to 10°. If I continue to rotate the Gyro back-and-forth 180° I continue to increase my total about 10° positive every repetition.
What I seem to be seeing it a nonlinearity of the output when rotating clockwise and counterclockwise. I have observed this behavior in the past with Analog Device gyros, both the 150 and 300° percent conversion. Although, I have never seen it to this extent. I do know when this particular chip that I have had this problem in the past, but because I was using it for prototyping I wasn’t too concerned about it. It may just be a characteristic of this particular chip.
When building the navigation system for our robot this year, we used an Analog Devices’ 300° per second gyro. It did exhibit this behavior but too much lesser extent. Because the only plan on using the Gyro for returning the robot in one direction, we did not feel that it was worth worrying about. Besides the air we would get with less than 1°.
So what I’m looking for now is a way to compensate for what appears to be the nonlinearity of this Gyro.
I found a way to compensate for the non linearity of the gyro but, the problem is that the gyro isn’t consistent. I will turn it 90 degrees on way, then back to the starting position and it will read 0. But then when reset the system and try the same test it will be off by about 20 degrees. I then reset the system again, and its off by 5 degrees, and then without event reseting the system I turn it 90 degrees and it only changes the position by about 15 degrees, i turn it back to the starting position and it goes -90 degrees from where it was at.
I see absolutely no consistency in this device. It will work fine, but then when the controller is reset it will work totally difference.
I have scaling factors in to change the readings so they are equal from one direction to another, but that only solves the linearity problem.
I have figured there is something wrong with it this whole time simply because the neutral voltage is 2.1 volts. That just seems too far from the spec’ed 2.5 volts to be acceptable.
I can’t argue with your logic. It does sound like something is a little screwy with that gyro.
BTW, is there a chance you can share your scaling code to compensate for the non-linearity. For some reason, mine doesn’t seem to work.
Well, my code, ( I am at work now ), is simply a scaling factor. as far as linearity I was talking about from positive to negative, so crossing the neutral value it was non linear, so -90 degrees/sec for 1 sec wouldn’t read the same as 90 degrees/see for 1 sec.
I realized I had no “base” code to even drive the bot around, so I decided to work on that first. I am doing the 2 drive motors off of the 16 bit Timer1. Timer0 I decided to make an “event” timer. This way I can say do this after x seconds, or whatever and it will be very easy to do. The resolution is .01 seconds, which should be fine for my needs. The accuracy should be 0.0128% assuming the internal clock has 0% error (pffft! yea right!) and not counting any latency. But this should all be fine for this application. I wrote some simple motor functions to stop, both, stop individually, and set the speeds either together or individually.
I mounted my bread board to the top of the bot, it is a very nice set-up now.
The only problem I am having is that with how the motors are on there, one is turning CW while the other is turning CCW to go forward. So it curves. To solve this, I think I am gonna try to somehow mount the motors both facing the same direction. This should solve the problem.
I can share the plans of the bot and my code if you would like. I assume you can almost just use it with little to no modification from what I have.
Anything you would like to post up would be great. I’m sure I am not the only one who might benefit from it.
My ultimate goal is to use the Arduino, or some other variant of the ATMega chips, to build a single board to both read in two+ quadrature encoders and a gyro and be able to provide distance and orientation from a single serial stream. I might use I2C but I will definitely have a simple RX/TX source.
BTW, as a test, I have removed the guts from a ball mouse and will be using this as a source for a test set of Quadrature encoders. Hey, it was cheap at $5 for a pair of quad’s. Now I just need to interface them mechanically.