MinIMU-9 AHRS code

I was relieved to see that other people were experiencing the same problems as I have. I misunderstood how the calibration program was meant to be used, but things distinctly improved when I calibrated properly. Despite the improvement, the underlying issues I had were still present and unacceptable, but I was able to quickly attribute that to the initial orientation of my device. I assumed the initialization routine would work under any orientation…my mistake. After correcting this the orientation estimate seems much more reliable.

My application requires that I mount the IMU at an angle perpendicular to the ground due to spatial limitations on my board. I’ve just flipped it on its end so the header pins are at the bottom and the mounting screw hole is at the top. Based on the silkscreen picture, this makes Z my roll, Y my pitch, and X my Yaw. The “SENSOR_SIGN[9]” line seems helpful if you want to flip pitch, roll, or yaw the unit half a rotation, but not quarter rotations. Is there any way to initialize the unit such that it accepts my orientation while initializing? I’m guessing that I would have to use some trigonometric proportions in place of the positive and negative 1’s, but I’m not sure if changing this array from an int to a float would pose any serious complications.

Complications aside, it was sort of cool to see the angle readings “spill” into one another as a linear combination because the basis vectors were defined off axis!

Anyway, thanks for reading this far into my long winded question!

Chad, I’m glad to hear you got an improvement with that change. By the way, we can’t take too much credit for the AHRS code; it mostly came from sf9domahrs, which is based on ArduIMU, and I only modified it to work with our MinIMU-9.

burkleypatterson, I think the easiest way to swap the axes around might be to edit I2C.pde. For example, I think the code for reading the gyro (starting on line 52) could be changed to this for your setup:

  AN[0] = gyro.g.z; // roll
  AN[1] = gyro.g.y; // pitch
  AN[2] = gyro.g.x; // yaw

and the same should be done for the rest of the sensors.

- Kevin

Thanks for your help! That seems to have mostly done the trick. I have a couple of concerns though.

Now when I open the serial monitor, the yaw reading begins at zero and slowly ticks its way up to a larger value, where it stabilizes. Everything else sits happily at zero.

The reading responds fairly consistently to rotation with little drift, but not linearly relative to its absolute angle. When I rotate it 90 degrees it only registers a rotation of about 60 degrees, except in a specific absolute angle range. When I reach this range, a 90 degree rotation is output as 180 degrees. In total a full rotation still yields 360 degrees (restoring to the original value), but that’s not the case on smaller scales. When I performed the initial calibration the unit was about a foot away from my macbook pro, but when noticed this inconsistency I was using a 10’ usb on the other side of the room. Could a skewed calibration be responsible for both of these problems?

I think the yaw movement at the beginning is normal as the AHRS adjusts to the north heading measured by the magnetometer (if I remember right, it happens when the IMU is used with the board parallel to the ground too). It might be possible to rewrite the program to eliminate that movement, but I have not looked into it.

It’s certainly worth trying to recalibrate the magnetometer a few more times to see if you get better (or different) results. For the best results, you should try to keep the IMU away from anything that could influence the magnetometer (large metal objects, high currents, etc).

- Kevin

I re-calibrated the magnetometer and I noticed a distinct improvement. I have yet to do a rigorous test, but it is certainly improved.

I will look into eliminating the initial yaw movement out of curiosity, but it is not detrimental to my application. I imagine it would be as simple as introducing an additional offset.

I noticed that after changing the axes in the I2C script to accomodate my orientation the yaw reading would eventually arrive upon the right value, but when rotated it would swing wildly in the opposite direction. I quickly realized that this was because the gyro and magnetometer were “disagreeing” about the direction of increasing yaw. I think this was because my orientation swapped the parity of the existing coordinate system. For anyone interested, I was able to easily solve this problem by modifying the axis definitions:

// X axis pointing forward
// Y axis pointing to the right
// Z axis pointing up
int SENSOR_SIGN[9] = {1,1,-1,-1,-1,1,1,1,-1}; //Correct directions x,y,z - gyro, accelerometer, magnetometer

Someone mentioned dynamic calibration in an earlier post. Have you known anyone to do this? It would be very nice to implement, but from what I understand of the calibration process this is not practical/ possible in most applications. I think that if someone were to run the calibration continuously the settings would be tainted as soon as an adverse magnetic field (or just ferrous material) was introduced, which defeats the purpose. If the calibration was performed on intervals, the readings would also be unreliable unless the system somehow allowed the IMU to swing around wildly for a few seconds. The only other solution I can imagine is some kind of fixed external reference for the mag to compare its readings to, but this would require a different calibration routine and a project of its own.

Today I updated my Arduino IDE to version 1.0. The AHRS libraries have dependencies on WProgram.h which has been renamed to Arduino.h. Additionally, the commands send() and receive() in the Wire library have been replaced with write() and read(). After making these changes everything worked as before.

Another point of interest…
I implemented a mode filter from the arduino playground to weed out some of the outliers produced by electronic noise. It adds readings to a 20 element array, deleting the last entry and shifting everything to make room for a new one. A function sorts the elements and returns the mode. I was filling the arrays with values from the roll pitch and yaw variables, and then reassigning the variables with the filtered output for printing. This made the program very unhappy, at least as far as the yaw reading was concerned. The yaw estimation jumped around wildly, unless the unit was pointed towards “zero yaw.”

I found out that this was because the heading updated slower than I was reassigning the values, so the program performed calculations based on the filtered values. This didn’t seem so bad at first, but it turned out to be very problematic.

In the first few moments the program thought the yaw was zero and continued to use zero in the calculations until the heading was updated. After a new value was added to the matrix, it would re-calculate the mode, and again return zero because the matrix was mostly populated with zeros. Sometimes after the array filled with enough “real” data the yaw would jump to the right value, but the artificial values would quickly take over and pull the estimation back down. In a sort of tug-of-war beween the near-zero artificial values and the real heading estimation, the output would swing between zero and the heading.

I was able to solve this problem by returning the filtered result to a new variable rather than reassigning the old variables.

Hopefully this story will help someone else who’s experimenting with filtering!

I was fiddling around with the sensor some more to get a better feel for its capabilities and I noticed that the roll reading misbehaves when the sensor is pitched to the neighborhood of 90 degrees. When the pitch is zero the roll is consistent and seems accurate, but when I pitch it past 70 degrees or so, the roll begins to increase by tens of degrees. Under these conditions the reading for roll still changes in a consistent manner, but it is way off and very sensitive.

My first thought was that the modifications I made to the code was to blame, so I uploaded the original code and I still noticed the problem.

I have a mac, so I haven’t been able to get the python program running, but I assume it works without the 3D model rolling around like crazy when it’s pitched on end. Is this a known limitation, a problem on my end, or not a problem at all and just me failing to interpret the data correctly?

My application requires accurate sensing of pitch and roll over a range of just a few degrees (tens at the most), so this question doesn’t come with any real urgency.

Thanks!

I tried doing what you described with a MinIMU-9 AHRS and saw the same thing (roll angle changing quickly); I think it is normal. I think what you are seeing is something called gimbal lock, which will happen with any representation of an heading that uses 3 angles (Euler angles), although I am not completely sure. The accuracy overall heading does not seem to really be affected; it is just that the angles used to represent that heading can swing wildly when you do certain rotations.

It is a lot easier to see what is happening by watching the visualization program, so that would probably be the best way for you to understand it. We do not have much experience working with Macs, but we do have a Mac here, so we might be able to offer you some (limited) help trying to get the Python program to work. What have you tried so far, and where are you running into trouble?

- Kevin

help me. I try calibrate minIMU9 with folder example library LSM303.

this is result form example calibate.

M min X: -513 Y: -4096 Z: -384 M max X: 293 Y: 85 Z: 372
M min X: -513 Y: -4096 Z: -384 M max X: 293 Y: 85 Z: 372
M min X: -513 Y: -4096 Z: -384 M max X: 293 Y: 85 Z: 372
M min X: -513 Y: -4096 Z: -384 M max X: 293 Y: 85 Z: 372
M min X: -513 Y: -4096 Z: -384 M max X: 293 Y: 85 Z: 372

the value min Y is -4096 to big and fix. min Y: Not change when roll pitch yaw 360 with board (begin value -4096 and not to change). pleas help me

thank you

(I’m not good at English langauge.)

I can not speak english langauge. I will present with vdo. compass axis Y is not a correct.

youtu.be/UalRL3i9PjQ

Hello, ponggoa.

The reading of -4096 means that the magnetometer reading is overflowing, as discussed in the FAQ on the product page. If you reduce the gain of the magnetometer, it should solve the problem, and the FAQ gives a line of code that demonstrates how to do this.

- Kevin

thank you. my problem is solve. I have a question about axis Pitch. An axis Pitch not a turn 360 degree. I use AHRS axis roll (red line) can turn 360 degree but axis pitch (green line) can turn 180 degree. Is this a valid.

regards.

Yes, that is normal; the AHRS program represents roll as an angle in the range of -180 to +180 degrees, while pitch is represented in the range of -90 to +90 degrees. If you increase the pitch of the IMU past 90 degrees (vertical), it flips the yaw angle 180 degrees and starts decreasing the pitch angle.

- Kevin

Hi Everyone, just joined the forums!

my question not directly concern the ahrs code, but only the LSM303 library.

In the code, after have read the accel value, the code make a right shift over 4 bits because these bits are meaningless. And if i don’t suppress these 4 bits the IMU does’nt work correctly.

But in the datasheet i’ve not find anything about these 4 bits. the datasheet says the 16 bits (for each axis) are significant.

has anyone an explanation about that ?

Thanks

Yves

Hello, Yves.

Could you tell me what page of the datasheet says that all 16 bits are significant? It might help to also provide a quote of the sentence you are looking at.

–David

Hi David,

the datasheet says that the datas are given with 16 significant bits at the first page in “features”, where it’s only said “16 bit data output”, without other precision over mag or accel datas.

In the section concerning the accel output, the datasheet only says (§7.1.9) : “X-axis acceleration data. The value is expressed in 2’s complement”, same thing with Y and Z axis without any precision about resolution.

In an other way , if you look at the description of the temperature of the magnetometer (§7.2.9), where the temperature is only given on 12bit, it’s clearly expressed in tablea 86 : “Temperature data (8LSB/deg - 12-bit resolution). The value is expressed as 2’s complement.” And in the datasheet the 4 LSB of register TEMP_OUT_L_M have no name.

After these things i don’t understand the 4 bits right shift (>> 4) that we find in this part of code of the LSM303 library

"
byte xla = Wire.read();
byte xha = Wire.read();
byte yla = Wire.read();
byte yha = Wire.read();
byte zla = Wire.read();
byte zha = Wire.read();

// combine high and low bytes, then shift right to discard lowest 4 bits (which are meaningless)
// GCC performs an arithmetic right shift for signed negative numbers, but this code will not work
// if you port it to a compiler that does a logical right shift instead.
a.x = ((int16_t)(xha << 8 | xla)) >> 4;
a.y = ((int16_t)(yha << 8 | yla)) >> 4;
a.z = ((int16_t)(zha << 8 | zla)) >> 4;
"

Am i clear about this question ? (please sorry my bad writing !)

thanks you for your help

Thank you for the information. I looked in the LSM303DLHC datasheet and I cannot find any indication that the accelerometer data only has 12 bits. However, if you look in the datasheet for the older version of that chip (LSM303DLH), in Table 3, third row, it says “12 bit representation” in reference to the acceleration data. We probably tested the LSM303DLHC at some point and verified that it behaves like the LSM303DLH and those lower 4 bits don’t have any data in them. You can, of course, test it yourself.

If you look at the LSM303DLHC datasheet, Table 3, third row, it says that when the FS bits are set to 00, you get 1 mg/LSB of sensitivity. That means that if you hold the accelerometer still with the Z axis pointing up, you should see a value of 1000 on the Z axis accelerometer reading. If you try running our code without doing the right shift, you would actually see a value of 16000 or -16000, so that is a pretty convincing way that we can know that shifting right is the correct thing to do.

In general, ignoring some lower bits of a sensor reading will not cause any inherent problems: it just means that we might be doing our calculations with less precision than is possible.

–David

David,

thank for your informations and explainations.

I’ve tried to run the “serial” example given in the lsm303 librarie, whitout the 4 lsb right shift. And the result is on the z axis the value of +/-4096. So the right shift is necessary of course.

I’m just supprised that it’s not said clearly in the datasheet.

So, every things is good and work well…

thank you for your help

Yves

I have been working with this code for a little while, but I have a few questions. Is the Yaw outputting data relative to its heading? If it is, is there a way to make it reference its starting position like the pitch and roll? the project I’m working on does not depend on a compass heading, but rather a relative position. Also, when I turn it 90 degrees (yaw) it only seems to account for about 60-70 degrees. I’ve done the calibration. Another question, what would it take to make the output of all 3 directions to be 0-360?

Thanks in advance

Hello.

I am not sure I understand what you mean when you ask “Is the Yaw outputting data relative to its heading?” Could you please clarify?

Also, something seems to be wrong if you are getting only 60-70 degrees when you expect 90. Could you please tell us more about your setup? Could you post a photo of your entire setup, including all the connections?

- Jeremy