You are on page 1of 9

Platforms Products Projects Posts Roadmap More 

Login Signup

How to Calibrate a Magnetometer


By ShawnHymel

A magnetometer allows you to detect the presence and strength of a magnetic field. A 3-axis magnetometer gives you
the strength of that magnetic field in 3 dimensions: along the X, Y, and Z axes. Combined, these give you a vector dictating
the strength and direction of the magnetic field.

Beyond just detecting nearby magnets, a magnetometer can also measure the Earth’s magnetic field. As a result, we can
use these tiny sensors as a method of constructing a digital compass. However, it’s not quite as easy as just reading the
strength of an ambient field.

This guide will walk you through the process of performing 3 types of calibration for a magnetometer: hard-iron
calibration, soft-iron calibration, and magnetic declination. With these, you should be able to build a functioning digital
compass out of your magnetometer readings!

If you’d like to watch this tutorial in video form, see here:

How to Calibrate a Magnetometer | Digi-Key Electronics

Hardware Connections

I’ll be using an Adafruit Feather M0 and a LIS3MDL 3-axis magnetometer. The code we will use works for a number of
Adafruit magnetometer breakout boards, but the concepts should apply to all magnetometers.

Important! The Adafruit Sensor Lab code requires a good amount of flash memory. As a result, it will not fit on many older
Arduino boards (e.g. UNO). I recommend using a SAMD21 or better for this exercise. That being said, you are welcome to
write your own program that sends raw magnetometer (in uTesla) data to MotionCal.
Connect your magnetometer to your Arduino board. I’ll be using I2C to communicate with the sensor.

Hard-iron and Soft-iron Calibration Concepts

Hard-iron distortions are caused by nearby permanent magnets. These nearby magnetic fields result in a simple additive
offset to our readings and are relatively straightforward to correct. We take a bunch of X, Y, and Z raw readings while
moving the magnetometer about in all directions. We can plot the X/Y, Y/Z, and Z/X values, which would appear as 3
separate circles. With hard-iron distortion, these circles will appear in separate areas of the sample plot.

Next, we find the halfway point between the minimum and maximum values of each axis. These become our offsets.
When we subtract these offsets from future raw readings, we have successfully corrected for hard-iron distortions. If we
take a new series of readings and plot the results, the circles should overlap each other.
Soft-iron distortions result from the presence of iron, steel, and other ferrous materials near the sensor. Non-ferrous
materials can also cause some distortions, as different objects and materials can deflect and warp magnetic fields.

When plotted, the raw X, Y, and Z samples will look elliptical rather than spherical.
Note that MotionCal will automatically re-scale the display to make the collected points look more spherical, so I had to
manipulate the image some to demonstrate the concept.

The math to perform soft-iron calibration is a little more involved than we have time for in this article. You can read about
it here if you would like to dig into the math. Fortunately, we have software tools to help us out.

When complete, you end up with 2 sets of values: a vector for the hard-iron offsets and a matrix for the soft-iron scale
values. To compensate for the hard-iron effects, you simply need to subtract the hard-iron offset values from your raw
readings:

To compensate for soft-iron effects, you should perform a matrix multiplication using the soft-iron scale values. Note that
you can compensate for hard-iron distortion as well to create a set of completely calibrated values.

Magnetometer Calibration with Motion Cal

In the Arduino IDE, go to File > Preferences and add the following URL to the board manager list:

https://adafruit.github.io/arduino-board-index/package_adafruit_index.json

Go to Tools > Board > Board Manager. Search for and install Adafruit SAMD Boards. 

Go to Sketch > Include Library > Library Manager. Search for and install Adafruit Sensor Lab. This should install the
required library dependencies.

Open File > Examples > Adafruit Sensor Lab > calibration > imucal_nosave. Upload this program to your Arduino board.
Note that this program is intended to calibrate 9 DoF inertial measurement units (IMU), but we can use it on singular
sensors like our LIS3MDL magnetometer. It outputs raw accelerometer, gyroscope, and magnetometer data over the
serial port. If one or more sensor is not present, those values will be 0.

Download and install MotionCal from https://www.pjrc.com/store/prop_shield.html for your operating system. Run it, and
connect to the serial port of your Arduino (make sure that the serial port is not being used by another program). When the
connection is established, begin turning and twisting your magnetometer board in all directions.
The idea is to make a sphere with all of the captured x, y, and z magnetometer points. Aim to have gaps less than 1%.
When you’re done, you can select “none” under the serial port to have it stop collecting data.

Copy down the values from the “Magnetic Offset” (hard iron calibration) and “Magnetic Mapping” (soft iron calibration).

Magnetic Declination

If you are hoping to use the magnetometer as a compass, you should be aware of the differences between magnetic
heading and geographic heading. The north and south geographic poles are different from the earth’s magnetic poles.
Also, the magnetic poles shift continuously. You can find a fun animation of how the magnetic poles shift here:
https://geomag.colorado.edu/historical-main-field-change-and-declination.

To use your magnetometer like a regular compass (magnetic heading), you do not need to account for magnetic
declination (as a regular compass will point to magnetic north). However, if you’d like to find your heading based on the
geographic poles, you’ll need to convert from a magnetic heading to a geographic heading.

You can use this tool (https://www.ngdc.noaa.gov/geomag/calculators/magcalc.shtml#declination) from NOAA to find


the magnetic declination in your area. Simply enter your location (city, latitude/longitude, etc.), and you’ll get a number in
degrees and minutes.

To compute the floating point value of your declination offset:

Copy Code

declination_offset = degrees minutes / 60

If your offset is east, the number should be positive. If the offset is west, the number should be negative.

From here, you can apply the offset as follows:

Copy Code

geographic_heading = magnetic_heading declination_offset

Digital Compass Example

Copy the following program to your Arduino IDE:

Copy Code
/**
* Compass Demo
*
* Print heading (in degrees) to attached I2C OLED display. Demonstrate
* how to use magnetometer calibration data and convert magnetic heading
* to geographic heading.
*
* Author: Shawn Hymel
* Date: May 5, 14
*
* License: 0BSD (https://opensource.org/licenses/0BSD)
*/

#define DEBUG 1
#define OLED 0

#include <Wire.h>
#include <Adafruit_LIS3MDL.h>
#include <Adafruit_Sensor.h>

#if OLED
#include <SFE_MicroOLED.h>
#endif

// Pins
const int pin_reset = 8;

// Hard-iron calibration settings


const float hard_iron[3] = {
-32.34, -1.19, 6.25
};

// Soft-iron calibration settings


const float soft_iron[3][3] = {
{ 0.993, 0.040, -0.002 },
{ 0.040, 1.003, -0.009 },
{ -0.002, -0.009, 1.006 }
};

// Magnetic declination from magnetic-declination.com


// East is positive ( ), west is negative (-)
// mag_decl = ( /-)(deg min/60 sec/3600)
// Set to 0 to get magnetic heading instead of geo heading
const float mag_decl = -1.233;

// Globals
Adafruit_LIS3MDL lis3mdl;
#if OLED
MicroOLED oled(pin_reset);
#endif

void setup() {

// Pour some serial


#if DEBUG
Serial.begin(115200);
while (!Serial) delay(10);
Serial.println("LIS3MDL compass test");
#endif

// Initialize magnetometer
if (!lis3mdl.begin_I2C()) {
#if DEBUG
Serial.println("ERROR: Could not find magnetometer");
#endif
while (1) {
delay(1000);
}
}

// Initialize OLED
#if OLED
delay(100);
Wire.begin();
oled.begin(0x3D, Wire);
// Clear display
oled.clear(ALL);
oled.display();
delay(1000);
oled.clear(PAGE);
#endif
}

void loop() {

static float hi_cal[3];


static float heading = 0;

// Get new sensor event with readings in uTesla


sensors_event_t event;
lis3mdl.getEvent(&event);

// Put raw magnetometer readings into an array


float mag_data[] = {event.magnetic.x,
event.magnetic.y,
event.magnetic.z};

// Apply hard-iron offsets


for (uint8_t i = 0; i < 3; i ) {
hi_cal[i] = mag_data[i] - hard_iron[i];
}

// Apply soft-iron scaling


for (uint8_t i = 0; i < 3; i ) {
mag_data[i] = (soft_iron[i][0] * hi_cal[0])
(soft_iron[i][1] * hi_cal[1])
(soft_iron[i][2] * hi_cal[2]);
}

// Calculate angle for heading, assuming board is parallel to


// the ground and Y points toward heading.
heading = -1 * (atan2(mag_data[0], mag_data[1]) * 180) / M_PI;

// Apply magnetic declination to convert magnetic heading


// to geographic heading
heading = mag_decl;

// Convert heading to 0..360 degrees


if (heading < 0) {
heading = 360;
}

// Print calibrated results


#if DEBUG
Serial.print("[");
Serial.print(mag_data[0], 1);
Serial.print("\t");
Serial.print(mag_data[1], 1);
Serial.print("\t");
Serial.print(mag_data[2], 1);
Serial.print("] Heading: ");
Serial.println(heading, 2);
#endif

// Display heading (rounded) to OLED


#if OLED
oled.clear(PAGE);
oled.setFontType(1);
oled.setCursor(5, 20);
oled.print(int(heading 0.5));
oled.display();
#endif

delay(100);
}

Change the hard_iron array to match the “Magnetic Offset” values you obtained from MotionCal. Change the soft_iron
array to match the “Magnetic Mapping” values you copied from MotionCal. Finally, change the mag_decl value to the
magnetic declination value you obtained earlier.
Run the code, and open a Serial monitor. You should see the magnetic heading being printed to the console. Note that the
direction of heading is given by the Y axis, assuming Z is pointing to the sky.

If you attach an I2C OLED screen to your Arduino and enable the OLED code (#define OLED 1), you can see the heading
printed out to the OLED. The heading should match up with the readings from other digital compasses (note that you
might need to change their settings to report the geographic/true heading instead of a magnetic heading).

Conclusion and Going Further

Because the hard iron and soft iron distortions will change depending on the environment of the magnetometer (due to
nearby speakers, motors, and ferrous items), you should ideally recalibrate the sensor any time it changes location.
Additionally, such distortions can be produced by nearby electronics. So, it’s a good idea to recalibrate if your
magnetometer is placed on a new PCB with or near other electronics.

I recommend checking out the following guides if you would like to learn more about calibrating magnetometers:

Adafruit Magnetometer Calibration Tutorial


How to Calibrate a Magnetometer?
Tutorial: How to calibrate a compass (and accelerometer) with Arduino
Magnetometer Hard & Soft Iron Calibration
Calibrate an eCompass in the Presence of Hard- and Soft-Iron Interference

Code samples from this tutorial and the video can be found in this GitHub repository.

Key Parts and Components 3 Items


Mfr Part # 2772 Mfr Part # 4479 Mfr Part # 3955

FEATHER M0 BAS PROTO ATSAMD21G18 TRIPLE-AXIS MAGNETOMETER LIS3MDL JST PH 4-PIN TO MALE HEADER CA
Adafruit Industries LLC Adafruit Industries LLC Adafruit Industries LLC

$19.95 Details $14.95 Details $1.50 De

Add all Digi-Key Parts to Cart

TechForum

Have questions or comments? Continue the conversation on TechForum, Digi-Key's online community and technical
resource.

Visit TechForum
Arduino | 3D Printing | Raspberry Pi

Project Details

Platforms
Arduino
Development
C
C++
Tags
Arduino
Compass
Magnetic
Sensor
License
Attribution

Get Involved

Like
Save

 1-800-344-4539 218-681-6674  sales@digikey.com  218-681-3380

United States Copyright © 1995-2023, Digi-Key Electronics. All Rights Reserved.


Local Support: 701 Brooks Avenue South, Thief River Falls, MN 56701 USA

Do Not Sell / Do Not Share My Personal Information

You might also like