Correcting the Balboa magnetometer

Balboa appears to be an extremely well engineered robot – nice job, folks!

However, a few tests, shown below, reveal that the magnetometer is subject to severe hard iron distortion, perhaps due to the vicinity of the battery casings (I’m using alkaline AAs in my early tests and haven’t tried any others yet). Fortunately, this situation can be fixed.

The basic test is simple: just hold the robot in the air and wave it around, covering as many 3D points as possible, while outputting the raw magnetometer readings over the serial port. Ideally, the magnetometer readings should map to the surface of a sphere centered on the origin. Here is what I got, showing a pronounced ellipsoid remarkably far from being centered on the origin (two different viewpoints shown):mag3_raw2

The fact that the data DO map to a nice ellipsoid is very promising, though!

Most people try to correct magnetometer readings by finding a set of offsets and scale factors, using a simple axial min/max approach. But in this case, the major axis of the ellipsoid is pointing from one corner of the box diagonally to the other, which is impossible to correct by this method. I tried, though, using the Arduino program Calibrate2, attached in a zip file. The result is shown below, showing that the ellipsoid is now centered and less oblong, but these data would be useless to make a compass.

Linear algebra to the rescue! I used the approach described in this excellent blog article:

Sailboat Instruments: Improved magnetometer calibration (Part 1)

The author describes magneto, a fair sized C program that runs on a PC or a Mac (I use the Code::Blocks IDE on Win7). For convenience, I’ve collected all of the basic parts of magneto, described in the sailboatinstruments link, into one file that can be compiled and run on a desktop. See attached zip file.

This approach fits an ellipsoid to the data, avoiding statistical problems associated with the min/max approach, rotates the ellipsoid to align with the coordinate axes, scales the axial dimensions to a sphere, and rotates back. The result is a set of offsets and a nine element matrix correction that must be applied to the raw data.

The magneto program output for the data set plotted above is as follows, and includes data initialization statements that can be incorporated into your code:
mag3_output.txt (693 Bytes)

The corrected data are as close to a perfect sphere as you can expect!

With these corrected data, it is possible to create a compass program with basic accuracy +/- 2 degrees or so, which is fine for navigating the Balboa around the room – providing that no metal objects are nearby to distort the compass readings. Who knows, though, that might increase the fun!

I’ve attached an Arduino program LIS3DLM_compass that demonstrates how to do the correction and make a compass, assuming that the Balboa is horizontally oriented.

To use these programs to calibrate your magnetometer, first use the Arduino IDE to upload the program Calibrate2 to Balboa. Instead of using the Arduino serial monitor to visualize the program output, use a terminal program like TeraTerm, capable of creating a log file, and use .csv as the file extension. Turn the robot about in 3D until the raw data collection process terminates with some scale and offset factors. Close and edit the resulting .csv file so that only the raw magnetometer data are included. Then, compile, link and run magneto as an interactive console program, entering the above .csv filename as input. I suggest to enter 1000 for Hm. Magneto publishes correction data which can then be incorporated into the source code for another program, like the included LIS3MDL_compass.

When I get around to it, I’ll post a program showing the Balboa driving a course defined by a set of magnetometer headings, and also, a tilt compensated compass that makes use of the accelerometer.

By the way: there is another, very sophisticated method of mapping magnetometer or other data to the unit sphere which will run on any Arduino: Gauss-Newton mapping. It works much better than the min/max axial method, and the linked implementation also yields 6 independent parameters. However, to correct for a tilted ellipsoid as encountered here requires the 9 independent parameters returned by magneto. In any case, this is a one-off job and there is no reason to burden the Arduino with such a task.

Happy to entertain questions or comments! (127.4 KB)



Thanks for the informative write-up! It’s nice to see the magnetometer distortion visualized and how much this calibration method helps. We’ve added this forum post as a recommended link on the Balboa robot product page, as we think it will be useful to other customers (and I’m sure it will be helpful in other magnetometer applications too).


Hi Jim
Great contribution. Using it on other magnetometers. What are you using for your 3D scatterplots?

I happened to use Mathematica for the scatterplots, but MATLAB works just as well.

As suggested earlier, I implemented a tilt-compensated compass for the Balboa. It works fine when the Balboa is either standing up on its wheels or horizontal. See attached for an Arduino implementation. This version prints the heading on the LCD display.

I’m not sure how useful it will be for navigation while balancing, as the direction of acceleration can change quite rapidly, but the code is presented for completeness.

The code assumes the default settings for the accelerometer (2 g scale), but should work equally well if the scale is changed to 16 g, as is done in the sample code provided by Pololu. (2.0 KB)

Note: the accelerometer in my example did not need calibration, as the corrections turned out to be negligible. It is quite a good unit!

1 Like

Hi Jim,

Thanks for the tilt-compensated code! I’ve been having good success with it after calibration.

Would there be any issues with incorporating the vector.c and vector.h code directly into the main .ino file? I noticed your LSM9DS1 tilt-compensated code example is a stand-alone file (I realize different libraries), and was curious if I could achieve the same for the Balboa example.


No problem at all.

It is safest to place the code before setup() and loop(), as the Arduino IDE sometimes misplaces function prototypes during its hidden manipulations of the source code.

Thanks, Jim!

I also noticed in the LSM9DS1 tilt-compensated example that you’ve switched to using simple float arrays instead of a structure for the x/y/z vectors and pointers when the functions are called. Is it no problem to do the same for the Balbao example? I assume this would also mean the extern functions in vector.h would no longer be necessary?

Edit: I suppose what I’m trying to ask is if the approach you used for the LSM9DS1 can be used for the LSM6DS33+LIS3MDL.


In the case where only one type of variable with a few elements is involved, I can’t think of a significant difference in using a structure over an array. The math is the same, only the notation changes.

So yes, you could take the same approach as used for the LSM9DS1.

Thanks again, Jim.

Initially, I wasn’t sure if the code was specific to the LSM9DS1, but after successfully modifying it for the LSM6DS33 + LISM3DL, I realized that it’s actually sensor agnostic, as long as you specify the correct vector components.

I’ve always been a bit fuzzy on vector cross calculations, and plan to spend some time to better understand the math behind them.


Vector, matrix and quaternion algebra (which come under the general heading of “linear algebra”) are extremely useful in navigation programming, and save a lot of time and effort.

It is well worth the time to study the rules and learn how to use the methods, as given a few basic functions, you can manipulate vectors, etc. as objects that undergo familiar operations like add and multiply. That allows you to forget about the sometimes ugly math details.

Hi there!
Thanks for code and explanations.