-
Notifications
You must be signed in to change notification settings - Fork 190
Simple and Effective Magnetometer Calibration
Magnetometers are wonderful devices and absolutely essential for correcting gyro drift in applications that require absolute orientation through sensor fusion. The bane of magnetometer usage is, however, their non-ideal response surfaces. The ideal response surface for a three-axis magnetometer is a sphere centered at the 3 D origin. This means the response to an external magnetic field of, let's say 400 milliGauss (mG) in the z-direction would be exactly Mz = 400 mG when the magnetometers z-axis was normal to the floor, My = 400 mG when the magnetometer's y-axis was normal to the floor, and Mx = 400 mG when the magnetometer's x-axis was normal to the floor. More simply, the ideal response surface no matter the orientation of the magnetometer is a sphere with radius 400 mG centered on the origin. In practice, MEMS magnetometers are rarely so well calibrated when you receive them. There are good reasons for this. The MEMS sensors are typically characterized at the factory but mounting on a pc board can add stresses that can easily result in a shift of the calibration. Additionally, manufacturers expect that users will calibrate their sensors before use, meaning that costly means to ensure greater stability can usually be avoided especially for the cell phone market where extraordinary accuracy is usually not expected absent some calibration. Here is a measure of a typical response from an uncalibrated AK8963C magnetometer embedded in the popular MPU9250 9 DoF motion sensor.
Magnetic field in mG along various 2D slices measured for an uncalibrated MPU9250. Note the clumps of data representing the field values when the magnetometer was at rest. These should measure, Mx ~ 200 mG, My ~ 50 mG, and Mz ~420 mG in my area of California.
This data was taken by spewing properly-scaled (mG) Mx, My, and Mz values to my Serial monitor at 1 Hz while I was slowly turning the sensor board with the MPU9250 and Teensy 3.1 microcontroller through a variety of figure-eight patterns. I am plotting slices through 3D space to illustrate the facts that 1) the response between axes is not centered at the origin and 2) the response sensitivity is different along each axis. These are often referred to as hard iron and soft iron errors or biases, respectively. There is an additional common type of magnetometer bias often encountered which is due to the presence of man-made sources of magnetic field like big steel buildings and current carrying wires, etc. Encounters with these environmental sources of magnetic field can be interpreted as changes in orientation if a magnetic anomaly algorithm is not employed to detect and prevent it. This is beyond the scope of this note but we will be taking a look at this later.
Hard iron biases are typically the largest and the easiest errors to correct for. The simplest way to correct for them is to record a bunch of magnetometer data as the sensor is moved slowly in a figure eight pattern and keep track of the minimum and maximum field measured in each of the six principal directions; +/- Mx, +/- My, +/- Mz. Once the min/max values along the three axes are known, the average can be subtracted from the subsequent data which amounts to re-centering the response surface on the origin. Here is what the code to do this looks like:
`
void magcalMPU9250(float * dest1, float * dest2)
{
uint16_t ii = 0, sample_count = 0;
int32_t mag_bias[3] = {0, 0, 0}, mag_scale[3] = {0, 0, 0};
int16_t mag_max[3] = {-32767, -32767, -32767}, mag_min[3] = {32767, 32767, 32767}, mag_temp[3] = {0, 0, 0};
Serial.println("Mag Calibration: Wave device in a figure eight until done!");
delay(4000);
// shoot for ~fifteen seconds of mag data
if(MPU9250Mmode == 0x02) sample_count = 128; // at 8 Hz ODR, new mag data is available every 125 ms
if(MPU9250Mmode == 0x06) sample_count = 1500; // at 100 Hz ODR, new mag data is available every 10 ms
for(ii = 0; ii < sample_count; ii++) {
MPU9250readMagData(mag_temp); // Read the mag data
for (int jj = 0; jj < 3; jj++) {
if(mag_temp[jj] > mag_max[jj]) mag_max[jj] = mag_temp[jj];
if(mag_temp[jj] < mag_min[jj]) mag_min[jj] = mag_temp[jj];
}
if(MPU9250Mmode == 0x02) delay(135); // at 8 Hz ODR, new mag data is available every 125 ms
if(MPU9250Mmode == 0x06) delay(12); // at 100 Hz ODR, new mag data is available every 10 ms
}
// Get hard iron correction
mag_bias[0] = (mag_max[0] + mag_min[0])/2; // get average x mag bias in counts
mag_bias[1] = (mag_max[1] + mag_min[1])/2; // get average y mag bias in counts
mag_bias[2] = (mag_max[2] + mag_min[2])/2; // get average z mag bias in counts
dest1[0] = (float) mag_bias[0]*MPU9250mRes*MPU9250magCalibration[0]; // save mag biases in G for main program
dest1[1] = (float) mag_bias[1]*MPU9250mRes*MPU9250magCalibration[1];
dest1[2] = (float) mag_bias[2]*MPU9250mRes*MPU9250magCalibration[2];
// Get soft iron correction estimate
mag_scale[0] = (mag_max[0] - mag_min[0])/2; // get average x axis max chord length in counts
mag_scale[1] = (mag_max[1] - mag_min[1])/2; // get average y axis max chord length in counts
mag_scale[2] = (mag_max[2] - mag_min[2])/2; // get average z axis max chord length in counts
float avg_rad = mag_scale[0] + mag_scale[1] + mag_scale[2];
avg_rad /= 3.0;
dest2[0] = avg_rad/((float)mag_scale[0]);
dest2[1] = avg_rad/((float)mag_scale[1]);
dest2[2] = avg_rad/((float)mag_scale[2]);
Serial.println("Mag Calibration done!");
}
`
This function is so simple I trust everyone can see what is going on. The min/max bias arrays are initialized with the largest and smallest possible 16-bit values, respectively. Then the data is accumulated taking into account the average sample rate and enough data are taken to produce a reasonable estimate of the minimum and maximum values for each axis, to calculate the offset bias. This is trial and error but 128 samples work well for the MPU9250. The result is a set of three numbers: Mbiasx = -406.09 mG, Mbiasy = 3.46 mG, and Mbiasz = -121.55 mG. Here is what the response surface looks like after these offset bias corrections:
The MPU9250 magnetometer data after subtraction of the three axial offset biases determined from the calibration function above. The subtracted values are -406.1, -3.5, and -121.6 mG in the x, y, and z directions, respectively. The response surface is starting to look a lot more like a sphere!
These offset biases are then subtracted from the magnetometer data taken during use to re-center the response surface and get a more accurate magnetic field measurement. This procedure can be done every time the device is powered on or it can be done once and the bias values stored on the host in the operating sketch or off-sensor on an EEPROM for loading at startup. This works because the bias values represent inherent deviations of the sensor in its final board-mounted configuration and don't change much with different environments. Still, checking every once in a while is prudent!
There is one more use to which we can put this min/max calibration data and that is in re-scaling the axial response to make is even more spherical. This is the soft iron correction people talk about and to do it right means deconstructing the response surface into its elliptical principle axes and devising a 3 x 3 correction matrix to transform the general ellipsoidal response surface into a spherical one (see here and here).
We will do something not quite as sophisticated but in the same spirit. We will take the min/max values already computed and use them to rescale the magnetometer data to equalize the response along the three measurement axes. This is done in the last few lines of the calibration function above where a scale factor is calculated by taking the ratio of the average max - min along each axis and the average of all three axes. This means that an axis where the max - min is large has its magnetic field reduced and an axis that under-measures the field with respect to the other axes has its magnetic field values increased. This is just a simple orthogonal rescaling, equivalent to a diagonalized 3 x 3 calibration matrix but it allows some additional correction for scale bias. When applied to the MPU9250 we calculate scale biases of 0.96, 1.01, and 1.03 or the x, y and z axes, respectively. These are not huge but even a four percent scale error will show up as several degrees of heading error. And the cost of correcting for scale bias in this way is small, so why not? Especially compared to the much more mathematically-formidable ellipsoidal error corrections employed in the most sophisticated algorithms. The result of rescaling the MPU9250 magnetometer data is shown below:
The MPU9250 magnetometer response after offset and scaling bias corrections. The 1 - 4% scaling errors hardly seem to make a difference on first look but the response surface is a bit more ideal afterwards. There is still an ellipsoidal skew to the response surface that can only be removed with matrix mechanics.
This simple use of the magnetometer data for offset and scale bias error correction should satisfy the needs of most people using 9 DoF sensor fusion solutions for absolute orientation applications. Keep in mind that, in principle, the accelerometer and gyroscope have offset and scale biases too and need to be corrected in most use cases. Methods similar to that described here should suffice for these sensors too.
There is a lot more to magnetometer (and other MEMS sensor) calibration than what we have just described. I mentioned magnetic anomaly detection, where the accelerometer is used as a check on magnetic excursions that might not be due to actual orientation changes. There is the issue of correction for temperature changes; there is a reason most MEMS sensors have an embedded temperature sensor! There are several strategies for automatic and continuous calibrations of all of the sensors and several sensors employ a means to characterize the state of sensor calibration from fair to well-calibrated to allow users additional information about how well they can "trust" the sensor readings. We will address some of these issues later...