9DOF IMU for heading of rover robot

I am a robotics beginner developing a differential drive outdoor rover type robot and looking into navigation systems. I am researching 9DOF IMUs because they can provide heading calculation and thought this could be used to steer the robot. I have tried to use a BNO055 and ran into several difficulties. I am wondering if there are other more suitable IMUs that don't have these difficulties, or are there standard easy to use methods to deal with them. I'd rather have an IMU that does sensor fusion on the board than have to do it in an MCU, but if necessary I will try to implement it if these difficulties can be mitigated:

  1. The sensor won't calibrate unless rotated in 3D. The robot is large and cannot be rotated in 3D, and I can't deal with having to remove and calibrate the sensor everytime it is power cycled.
  2. The sensor 'autocalibrates' in the background and may come in and out of calibration at any time, when it is out of calibration it appears to use gyro relative orientation instead of absolute orientation, when it regains calibration the heading value may instantaneously shift by a large amount which is not useable behavior.
  3. Even when a calibration is saved and recalled to the sensor within seconds begins autocalibrating again and the magnetometer is out of calibration. It won't calibrate if kept in the xy-plane.

The BNO055 is very old and the calibration has never worked well.

Unfortunately, to get heading from a magnetometer, calibration is absolutely required. They don't work out of the box, and magnetic fields in the local environment must be taken into account.

Fortunately, for a large robot, calibration in the horizontal plane can work well. Simply rotate the robot through 360 degrees about the vertical axis, and correct the (X, Y raw data) points to lie on a circle centered on the origin.

Matlab/GNU Octave code for 2D calibration:

% File: magcal_2d.m
% This MATLAB program calculates the calibration parameters for a 2D magnetometer.
% As is customary, data for one or two complete revolutions of the magnetometer
% should be collected while held level, points closely spaced if possible.
% S. James Remington 8/2013
% uses published Matlab function EllipseDirectFit.m
% http://www.mathworks.com/matlabcentral/fileexchange/22684-ellipse-fit-direct-method
%
% First step: collect data and produce a CSV file (comma separated values) of
% magnetometer X and Y values (can be raw). No column headings!
%
% Second: execute magcal_2d.m
%
% This work was inspired by the 3D procedure described in:
% http://sailboatinstruments.blogspot.com/2011/08/improved-magnetometer-calibration.html
%

% no column headings allowed!
Book1 = csvread('mag2d_raw.csv');

A = EllipseDirectFit(Book1);

% modified coefficients of equation for ellipse
% from http://mathworld.wolfram.com/Ellipse.html

a = A(1);
b = A(2)/2;
c = A(3);
d = A(4)/2;
f = A(5)/2;
g = A(6);

% X0, Y0 offset (centroid of ellipse)

x0 = (c*d - b*f)/(b^2 - a*c);
y0 = (a*f - b*d)/(b^2 - a*c);

% semimajor and semiminor axes

numer = 2*(a*f*f+c*d*d+g*b*b-2*b*d*f-a*c*g);
denom1 = (b*b-a*c)*( sqrt((a-c)^2 + 4*b*b) - (a+c));
denom2 = (b*b-a*c)*(-sqrt((a-c)^2 + 4*b*b) - (a+c));
a_axis = sqrt(numer/denom1);
b_axis = sqrt(numer/denom2);

% angle of ellipse semimajor axis wrt X-axis

if (a < c) theta =        0.5*acotd((a-c)/(2*b));
else       theta = 90.0 + 0.5*acotd((a-c)/(2*b));
end

s=sprintf('x0 = %5.2f y0 = %5.2f a = %5.2f, b= %5.2f, theta(d) = %4.1f', ...
         x0,y0,a_axis,b_axis, theta);
disp(s);

% rotation matrix to align semimajor axis to X-axis

ct=cosd(theta);
st=sind(theta);
R = [ct st; -st ct];
xy0 = [x0 y0];

%rescale vector, correct for difference in magnetometer X & Y gains

scale = [b_axis/a_axis,1];

% final result: matrix to align ellipse axes wrt coordinates system, normalize X and Y gains and rotate back.

Q = R^-1*([scale(1) 0; 0, scale(2)]*R);

% correct the input data

xy = ( Q*(Book1-xy0)' )';

csvwrite('corrected_data.csv',xy);

% replot scaled data
figure;
axis equal;
scatter(Book1(:,1),Book1(:,2));
hold on;
scatter(xy(:,1),xy(:,2))
legend('raw','corrected');


disp(' ');
disp('scaled rotation matrix and vector to apply: Q*(XY-XY0)');
s = sprintf('(%6.4f %6.4f)*(X - (%5.1f))',Q(1,1),Q(1,2),x0);
disp(s);
s = sprintf('(%6.4f %6.4f)*(Y - (%5.1f))',Q(2,1),Q(2,2),y0);
disp(s);

Both the above Matlab/GNU Octave code, as well as a Python implementation are posted on github.

Never heard of that IMU, there are others, and some boards have IMUS on board. Some calibration will always be needed; I don't know of any way around it. Have a look at this info https://www.pishop.ca/product/adafruit-lsm6dsox-lis3mdl-precision-9-dof-imu-stemma-qt-qwiic/

Hello!
Would you mind to share if you have solved your problem and what IMU did you decide to use?

Sorry for the very late reply. I have an update from Halloween. I first tried the BNO055 and tried the sensor fusion mode and I had problems because it required calibration every time you powered it on. It also had problems communicating with the Due over I2C because it uses clock stretching which the Due cant handle, even the SoftI2C libraries I eventually tried did not do it. I was able to have it calibrate and save the calibration to the mcu EEPROM, and then recall it and apply it to the imu after power cycles; but it would start auto-calibrating within a minute and suddenly the reported heading would jump by a large unpredictable amount like more than 45deg sometimes. I am unable to turn the imu in 3 axes once its installed to calibrate because its a large robot and doing this is a major inconvenience since during testing I am power cycling the robot often. Then I tried a BNO085 due to the encouraging product description from the Adafruit website. I think it was doing the same auto calibration thing using the magnetometer, but it has a mode called RVC mode which just uses gyros and accelerometer I believe, no magnetometer. This mode worked pretty well. I didnt have time to delve into the depths of magnetometer calibration theory and try to make that work before Halloween. I am a hobbiest and looking for the easiest solution that works good enough without diving too deep due to the lack of time. With RVC mode I had the robot driving in a rectangle about 10ft x 5ft fairly accurately. However the heading slowly drifted. After each pass the path had drifted about 2” or maybe a degree or less, but that added up over multiple passes. I just wanted the robot only to move in a rectangle for Halloween. So I compensated for the drift by adjusting the target/setpoint angle by a few degrees a few times along its path. At the turns I was having the robot stop, and then perform a 90deg zero point turn. This worked well on the actual occasion of use; it was accurate to within a few inches along its path. I had to occasionally adjust it by physically moving it a bit but not more than once every 10min. I used Yellowjacket geared motors from GoBuilda and a RoboClaw 2x7A motor controller from BasicMicro. The RoboClaw worked great it was worth the price, it communicates by serial to the robot Due mcu. The RoboClaw does distance calculations from the motor encoders internally and transmits them to the mcu. During straights the motor speeds were controlled continuously from a PID control loop on the heading. After tuning it drove very straight and stable. The RoboClaw also performs automatic acceleration and deceleration per serial commands. Zero point turns were also controlled with a PID loop using the IMU heading, continuously updating motor speeds. It took some time to do the tuning. Maybe a week to get it all working good enough. It only goes a few feet per second so it wasnt too dangerous although I hadnt had time to program safety features, which I should have, because under PID control without safeties it will keep increasing motor power to overcome any disturbances in its path! I might post a video someday unfortunately I didnt record it then. Hope that's useful information!

The BNO055 works fine as a 9DOF, 3D orientation sensor if you turn off the built in sensor fusion and read out the raw data, as input to standard, open source IMU code. The sensor needs to be calibrated only once, in its final resting place.

Mahony filter code for the BNO055 is posted on Github.