Notifications
Clear all

Zero out IMU

14 Posts
3 Users
1 Likes
3,817 Views
logwagon
(@logwagon)
Member
Joined: 5 years ago
Posts: 14
Topic starter  

Good morning everyone,  

I have a project for a mobile application that will be using an IMU to measure rotation in degrees per second (dps).  The device will be mounted off axis (not flat) and typically will be started while the vehicle is already in motion.  (not unlike any smart phone based IMU that you might dig out of your pocket).  

All the motion measurements are based on relative motion.  I really don't care about up or down just "dps" at any giving time around any axis.  I'd like to 'calibrate' the IMU at all zeros at startup.

My question for this forum is:  Is there a magic bit of code that will tell the IMU, "Dude, don't worry about it.  Just take it from here?"  

I'm not really concerned about gyro drift.  The numbers needed are pretty coarse.  I'm working with a nano and 6050.

 

Thank you,

Logwagon


   
Quote
jscottbee
(@jscottbee)
Member
Joined: 5 years ago
Posts: 107
 

Hmmm, just write a 0x00 to reg 0x6b and start reading the data hoping for the best? 🙂

You could build a test rig and do calibrations whilst moving in a car. Do it several times and take the mean calibration.

I would start by just reading it raw out of the box.

Hope this helps.

Scott

 

 


   
ReplyQuote
logwagon
(@logwagon)
Member
Joined: 5 years ago
Posts: 14
Topic starter  

Thanks Scott,  I was actually going to rig up something on my riding mower to test it.  I found a great sketch at electronoobs.  They had a lesson and example that identified 3 gyro axis raw output but only reported out on x and y.  I tweaked it a bit and got the z stuff too.  It seems to do exactly what I need so far.  Now I'm going to add the 'stopwatch' element to the sketch, then add the serial print of the new timer, lcd 1602 to monitor etc etc.   By the time the nano 33 ble comes in I'll hope to figure out a debugger with BT access (at least that's the plan).

Jeff


   
ReplyQuote
logwagon
(@logwagon)
Member
Joined: 5 years ago
Posts: 14
Topic starter  

@jscottbee It turns out to be much easier than I thought.  The 6050 outputs raw data natively.  If it's at rest it reports zero(ish). Then depending on the sensitivity settings the raw numbers correspond to dps.  Just what I needed.  If it's moving at startup it doesn't matter.  When it stops moving the number is 0 +/- the offset.  

Now I have to get my head around millis().  Speaking of millis... when the error report is telling you that millis is not defined and an hour of hair tugging later you realize you spelled it millies (with an 'e').

 

Jeff


   
jscottbee reacted
ReplyQuote
Mandy
(@amanda)
Member
Joined: 5 years ago
Posts: 74
 
Posted by: logwagon

 They had a lesson and example that identified 3 gyro axis raw output but only reported out on x and y.  I tweaked it a bit and got the z stuff too.

I don't know if this will help but I'm pulling the data from the 6050 with the following code:

void getGyro() {
short int gyroXout = (wiringPiI2CReadReg8(i2cGyro, 0x3B) << 8) + wiringPiI2CReadReg8(i2cGyro, 0x3C);
short int gyroYout = (wiringPiI2CReadReg8(i2cGyro, 0x3D) << 8) + wiringPiI2CReadReg8(i2cGyro, 0x3E);
short int gyroZout = (wiringPiI2CReadReg8(i2cGyro, 0x3F) << 8) + wiringPiI2CReadReg8(i2cGyro, 0x40);
short int temperatureGyro = (wiringPiI2CReadReg8(i2cGyro, 0x41) << 8) + wiringPiI2CReadReg8(i2cGyro, 0x42);
short int accelXout = (wiringPiI2CReadReg8(i2cGyro, 0x43) << 8) + wiringPiI2CReadReg8(i2cGyro, 0x44);
short int accelYout = (wiringPiI2CReadReg8(i2cGyro, 0x45) << 8) + wiringPiI2CReadReg8(i2cGyro, 0x46);
short int accelZout = (wiringPiI2CReadReg8(i2cGyro, 0x47) << 8) + wiringPiI2CReadReg8(i2cGyro, 0x48);
// Code to evaluate the data.
}

These are all the registers you can read data from (apart from the settings). You can request data up to every 6mS, any more and I was getting strange results.

I've found that I have a problem with background noise on all the readings.  You can calibrate to remove it but it seems to drift with temperature so although the robot is stationary and showing as not moving it will start to think is is sliding across the floor after 20 min or so.

This post was modified 5 years ago by Mandy

The 600 series had rubber skin. We spotted them easy, but these are new. They look human... sweat, bad breath, everything. Very hard to spot.


   
ReplyQuote
logwagon
(@logwagon)
Member
Joined: 5 years ago
Posts: 14
Topic starter  

@Amanda Thanks Mandy.  The bit of code I stole borrowed from somewhere (electronoobs or jook brokking, I can't recall) is:

 

Wire.beginTransmission(0x68); //begin, Send the slave adress (in this case 68)
Wire.write(0x43); //First adress of the Gyro data
Wire.endTransmission(false);
Wire.requestFrom(0x68, 6, true); //We ask for just 4 registers

Gyr_rawX = Wire.read() << 8 | Wire.read(); //Once again we shif and sum
Gyr_rawY = Wire.read() << 8 | Wire.read();
Gyr_rawZ = Wire.read() << 8 | Wire.read();
/*Now in order to obtain the gyro data in degrees/seconds we have to divide first
the raw value by 32.8 because that's the value that the datasheet gives us for a 1000dps range*/
/*---X---*/
Gyr_rawX = (Gyr_rawX / 32.8) - 1; //- Gyro_raw_error_x;
/*---Y---*/
Gyr_rawY = (Gyr_rawY / 32.8) + 3; //- Gyro_raw_error_y;
/*--z---*/
Gyr_rawZ = (Gyr_rawZ / 32.8) + 1.3;//- Gyro_raw_error_z;

The original code was using this sketch as a spirit level and only requested 4 registers from the 6050.  As a guess, I changed it to 6 presuming the next two would give me 'z'.   

I let it run untouched for a couple minutes and noted the offsets (-1, +3, +1.3), then added them to the formula. (No idea how bit shifting works so I just went with it.)

The Gyr_rawX etc stayed at zero without drift for at least the 2 hours I let it sit.

So far so good.

 

Jeff

 

This post was modified 5 years ago by logwagon

   
ReplyQuote
Mandy
(@amanda)
Member
Joined: 5 years ago
Posts: 74
 
Posted by: logwagon

(No idea how bit shifting works so I just went with it.)

It took me a while before I understood bit-shifting so I'll post here for everyone who has not got it.

We read two registers from the device (6050 in this case), these each return a single unsigned byte (8 bit)(sometimes referred to as a char variable).  The first byte is the high byte and the second is the low byte of a two byte (16 bit) number.

register a = 0x1F = 00011111 = 31
register b = 0xF3 = 11110011 = 243

We need to put the two bytes into a 16 bit number so we add the first byte (a):

answer = answer + a = 0x001F = 0000000000011111 = 31

we now shift the bits left by eight and we get:

answer << 8 = 0x1F00 = 0001111100000000 = 7936

we now just need to add register b and we have:

answer = answer + b = 0x1FF3 = 0001111111110011 = 8179

Job done.

Another way to do it would be:

answer = (a * 256) + b = 8179

But this takes more clock cycles.

The 600 series had rubber skin. We spotted them easy, but these are new. They look human... sweat, bad breath, everything. Very hard to spot.


   
ReplyQuote
logwagon
(@logwagon)
Member
Joined: 5 years ago
Posts: 14
Topic starter  

@Amanda Thank you for taking your valuable time to respond.  It's still as clear as mud.  Let's say I understand the math involved...  What is the purpose of shifting left by eight?  Are not the numbers as they occur pre-shifted not sufficient to do whatever the sensor needs?


   
ReplyQuote
Mandy
(@amanda)
Member
Joined: 5 years ago
Posts: 74
 
Posted by: logwagon

@Amanda Thank you for taking your valuable time to respond.  It's still as clear as mud.  Let's say I understand the math involved...  What is the purpose of shifting left by eight?  Are not the numbers as they occur pre-shifted not sufficient to do whatever the sensor needs?

The tails of old tell stories of dark magic long forgotten in the age of man so I may be wrong about this but ... its all down to how long it takes.

A shift takes one clock cycle.  Add two numbers together also takes one clock cycle.  So when we do

ans = (a << 8) + b;

 we do 8 shifts + 1 add = 9 clock cycles.  if we do

ans = (a * 256) + b;

To do a multiply the processor adds "a" on to "ans" 256 times, so 256 adds + 1 add = 257 clock cycles.

I think that was the reason but others may know better.

 

This post was modified 5 years ago by Mandy

The 600 series had rubber skin. We spotted them easy, but these are new. They look human... sweat, bad breath, everything. Very hard to spot.


   
ReplyQuote
logwagon
(@logwagon)
Member
Joined: 5 years ago
Posts: 14
Topic starter  

@Amanda Ahh. More efficient use of the clock.  Very good.  I get it.  Now I'll look into why the 6050 needs that shift to operate. Or does it?


   
ReplyQuote
Mandy
(@amanda)
Member
Joined: 5 years ago
Posts: 74
 
Posted by: logwagon

@Amanda Ahh. More efficient use of the clock.  Very good.  I get it.  Now I'll look into why the 6050 needs that shift to operate. Or does it?

The 6050 does not need the shift, you do.  The 6050 will operate once you power it up, it then loads the information gathered into pairs of memory locations.  Now this is where I have a problem because your magic runes are different to my magic runes so I will try to write in yours.

Try running this and see if it making sense.

#include <Wire.h>

void setup() {
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); // start serial for output

Wire.beginTransmission(0x68); //to begin we set the 6050 adress (in this case 68hex)
Wire.write(0x43); //Set the pointer to the first adress of the Gyro data (in this case 43hex)
Wire.endTransmission(false); // stop transmitting to the 6050

Wire.requestFrom(0x68, 2, true); //We ask for just 2 registers this time so we will only read the raw X data

char a = Wire.read(); // read the contents of register 0x43 and save as a byte
Serial.print("Data at location 0x43 (the high byte) = ");
Serial.println(a); // print the data so we can see what is going on

char b = Wire.read(); // read the contents of register 0x44 and save as a byte
Serial.print("Data at location 0x44 (the low byte) = ");
Serial.println(b); // print the data so we can see what is going on

Serial.println("As you see the above data is of little use to us so we do the next bit");

Serial.print("X axis data using shift : ");
float value = (a << 8) + b;
Serial.println(value);

Serial.print("X axis data using multiply : ");
value = (a * 256) + b;
Serial.println(value);

}  
This post was modified 5 years ago by Mandy

The 600 series had rubber skin. We spotted them easy, but these are new. They look human... sweat, bad breath, everything. Very hard to spot.


   
ReplyQuote
logwagon
(@logwagon)
Member
Joined: 5 years ago
Posts: 14
Topic starter  

@amanda

The sketch wouldn't compile.  I think it's looking for a void loop()

 

J


   
ReplyQuote
logwagon
(@logwagon)
Member
Joined: 5 years ago
Posts: 14
Topic starter  

I think I may have figured something out.  Maybe.

One of the registers I'm working with in a completely different project has a default value of 00000001

If I want to change it to 00000010 is this where I shift << 8 ?

Grasping at straws.


   
ReplyQuote
logwagon
(@logwagon)
Member
Joined: 5 years ago
Posts: 14
Topic starter  

Okay. I was close.  If I want to change 00000001 to 00000010 I shift <<1.  It's coming.  Give me time.  


   
ReplyQuote