Magnometer calibration

All of our smartphones have an integrated compass. It can be very useful when you are looking for in which direction you should start heading while walking. However, you have probably noticed that sometimes, the direction showed by our phones can be quite...wrong! It happens when the compass has not been calibrated correctly. But if you try to turn the compass around in every direction, you'll probably notice that the direction arrow finally takes the right direction. Why? Because the smartphone is doing a "live" calibration as you move the compass.

What you should keep from this introduction is that compass calibration is fundamental ! Don't intend on using a magnetic sensor in your project unless you have it calibrated. Otherwise, the data you'll be using will probably be inaccurate, if not completely random ūüėÄ

Back to basics
What does a magnetometer do?

"Well, it obviously gives you your heading!"

"NOPE! (Chuck Testa)"

The magnetometer gives you a three dimensional vector of the magnetic field it senses. This magnetic field is a combination of both the earth's field and of the magnetic field created by all the surrounding objects. And this second magnetic field is far from being negligible,  especially in our hobbyist projects where there's electronics (and motors) all around.

Theoretically, the measured magnetic field should :

  • ¬†be centered around 0
  • always have the same strength

If we could represent it in 3D, it should basically look like a perfect sphere centered in 0.

In reality :

  • it is not centered around 0, because of the presence of other magnetic fields around the sensors(such as other magnets, electric wires) : it is hard iron¬†distortion.
  • it does not have a constant strength, because of the presence of other ferromagnetic materials around the sensors, which skew the magnetic field. This is soft iron distortion.

What we get is essentially a potato-shaped magnetic field (because of soft iron distortion), that is not even centered (because of hard iron distortion).

Calibration techniques
Hard Iron distortion

Hard iron errors introduce an offset in the magnetometer data

\mathbf(Field_{magnetomer} =†Field_{earth} +†Field_{hard iron})


To get this offset is pretty simple : we keep track of the maximum and minimum values the magnetometer outputs on each axis while moving it in space. From what we know the maximum and minimum should be centered around 0, so we get the offset by :

 x_{offset} = \frac{x_{min} + x_{max}}{2}

We can then subtract the measured offset from each raw measurement in order to get  hard iron free data.

Soft Iron distortion

Here, the work consist in transforming a potato in an orange. Or, transforming an ellipsoid into a sphere, if you prefer. It's actually easier than it sounds. There are  obviously mathematical formulas that involve matrices but let's keep it simple here.

Let's say earth magnetic field's value is F. It is the norm of the vector given by the magnetometer. While we've seen it should be the same on the three axes, experience shows it's not. So let's assume we have measured the maximum magnetic field Fx for the x axis,Fy for the y axis, Fz for the z axis.

We then calculate the average field value F by :

 F = \frac{F_x+F_y+F_z}{3}

Let's assume that F = 1. If F_x = 0.8, it means that F_x is only 80% of the average field value. Hence the potato. Now if we simply multiply all the incoming x values by F/F_x, the potato effect, will be gone, as we expand the range of the x values so that it is no longer 80% of what it should be, but 100%.

Scale_x = \frac{F}{F_x}

Final equation

If we no combine the two equations and use vectors, we get:

\mathbf(Field_{corrected} †= Scale * (Field_{raw} - Offsets) )

Scale transforms the potato into an orange(soft iron distortion) and the Offsets vector brings it back in the center(hard iron).

Calibration process

The calibration consist in getting the min/max values of x/y/z fields, and then calculations the offsets and scale factors. The more you move the magnetometer in all directions, the more efficient your calibration will be.

Using calibration data

// We get the raw values here  in mx,my,mz

//(this is pseudo code)


// Now we apply the calibration data

mx = (int)((scale_x)*(mx-x_offset));
my = (int)((scale_y)*(my-y_offset));
mz = (int)((scale_y)*(mz-z_offset));


Using Processing and  a slightly modified version of this program, I was able to quickly draw the 3D representation of the magnetic field. The video shows the before/after.


As you can see, hard irons distortion was HUGE before calibration. I could never have gotten reliable readings without correcting them. Soft iron errors¬†were also present, and completely removed by the calibration. ūüôā






The quadcopter : how to compute the pitch, roll and yaw

 The stick IMU from sparkfun
The stick IMU from sparkfun: ADXL345 accelerometer, ITG3200 gyroscope, HMC5883L compass.

After having introduced here the basics of an aircraft orientation and how to control it, this article is about actually computing the orientation of the quadcopter in space with sensors and with the Arduino.

 Which sensors to use?

3 axis accelerometer + compass for yaw

To get easily the orientation of a non-moving object (pitch and roll), a 3-axis accelerometer (how does it work?) can be used. For a static object, it gives the value of the gravity field on 3 axes, therefore its direction. And since it always points to the center of the earth, we can therefore know how the accelerometer is inclined with the help of man's best friend, trigonometry.

This method has been used in a lot of smartphones and gives pretty accurate results, if you are not moving. Indeed, if you start translating the phone in space, you are creating a force on it, therefore you are changing its acceleration. The assumption we made previously to compute the  orientation is not valid anymore, so the calculated orientation won't be accurate.

Another problem of accelerometers is their sensitivity to vibrations.(it is actually not a "problem"  per say, it is just a consequence of the forces created by vibrations, which are essentially shocks). If you want a stable quadcopter, you absolutely need a smooth angle! I think it is by far the most important thing about a quadcopter. If you're stuck with your PID tuning and that all the settings you try are not efficient, it probably means that your angles are not smooth/ accurate enough.

As you can see, using only an accelerometer is not a valid option. You can of course try to filter the signal and reduce the amount of vibrations received by the sensor( using some foam or anything to free the accelerometer from the motors vibrations can really change a lot the output of the sensor) but it will never be precise/ fast enough to satisfy the needs of this application.


The most common used sensor in quadcopter control boards is the gyroscope sensor. It  gives the angular rate around the 3 axes of space in deg/s, so, as for the accelerometer, some simple maths are needed to compute the actual angle by integration. But using only a gyroscope raises problems:

  • The first problem raised is caused by the nature of the sensor. It just gives an angular rate, not an absolute measure. So if you start up the quadcopter on a crooked floor, the initials pitch/ roll angles shouldn't be 0, but they will be in your program since the gyroscope will just output null angular rates!
  • All the common MEMS gyroscopes used with Arduinos have a drift. It means that even if you stay steady and don't move, the sensor will output values different than zero. The drift can be pretty big for some sensors (it can go up 2 deg/s on the z axis of our L3G4200d !), therefore ruining the accuracy of the measurement when you integrate the values ! But it's not as bad as it could sound, most of the gyro drift can be subtracted from the measurement since it's a constant value (given a certain temperature).

So, both accelerometers and gyros are bad? The answer is no!

There is actually many different ways of getting the most accurate orientation from a combination of sensors and they all have their good and bad sides. The simplest  approach we've taken so far for our quadcopter, and which turns out doing pretty good, is a complementary filter.

The idea of the complementary filter is the following : the filtered accelerometer value of the angle  is not subject to drift, so we use it to "correct" the value given by the gyroscope(more precise and less subject to vibrations noises). How do we do that? We combine the two values like so:

 Angle_{accurate} = (GyroPercentage) * Angle_{Gyro} +(1-GyroPercentage)* Angle_{Accel}

GyroPercentage is just a floating value between 0 and 1. It is typically ranged from 0.9 to almost 1, depending on how much you can trust your gyroscope and accelerometer.

The angle given by this very simple method is actually pretty accurate and isn't too much time consuming for the Arduino. So if you don't want to go too deep into ¬†the maths, I would suggest you to use this method, it is very satisfactory and easy to understand ūüėČ

On the yaw axis, the same complementary filter is used  with a compass (which gives the direction of the earth's magnetic field), giving us the 3D orientation of the quadcopter like so:

 Yaw_{accurate} = (GyroPercentage) * Yaw_{Gyro} +(1-GyroPercentage)* Yaw_{Compass}

Last, but not least, filter your signals!!  It is fundamental to reduce the impact of the frame vibrations on the angle computation, if your computed angles are not accurate, your quadcopter will never fly. It is really the most important thing to do. And filtering the signals means both in software and in the conception of the frame. Use strong materials (such as carbon fiber), try to protect your IMU( Inertial Measurement Unit = Accelerometer + gyroscope + compass) from the vibrations. On the software side, if your sensors have a built in low pass filter (like on the ITG3200 and the L3G4200D ), activate them by writing in the good registers.  If you want to program  your own software low pass filter for you sensors, a simple method is to smooth the values given by the sensor like it is done in this pseudo  C code:

/* This code shows an easy way to smooth readings from a sensor subject to
 high frequency noise.
It uses a low pass filter on a circular buffer.
This circular buffer always contains the last BUFFER_SIZE-1 readings from
the sensor.
The new reading is then added to this buffer, from which wecompute the
mean value by simply dividing the sum of the readings in the buffer by the
number of readings in the buffer.

int indexBuffer;
float circularBuffer[BUFFER_SIZE];
float sensorDataCircularSum;
int BUFFER_SIZE; // Number of samples you want to filter on.
float filteredOutput;
float sensorRawData; // typically the value you read from your sensor
 //in your loop() function

void smoothSensorReadings(){
 // We remove the oldest value from the buffer
 sensorDataCircularSum= sensorDataCircularSum - circularBuffer[indexBuffer];
  // The new input from the sensor is placed in the buffer
// It is also added to the total sum of the last  BUFFER_SIZE readings
// This method avoids to sum all the elements every time this function is called.
// We increment the cursor

 if (indexBuffer>=BUFFER_SIZE) indexBuffer=0;// We test if we arrived to the end
//of the buffer, in which case we start again from index 0
 filteredOutput =(SensorDataCircularSum/BUFFER_SIZE); // The output is the the mean
//value of the circular buffer.

What is basically done here is just an average value of the BUFFER_SIZE last inputs from the sensor. This is a simple way to get rid of high frequency noise and can also be used to smooth PWM inputs from a RC control, for example ūüôā

If you made it until here, I hope you are now able to implement by yourself your own implementation of orientation computing. It's actually a big area of research and much more complex solutions are still developed ( I invite you to look at Sebastian Madgwick ¬†fusion filter algorithm ūüôā ).

The quadcopter : the flight controller shield

For our new quadcopter frame(article coming soon ūüėČ ), we decided to create a completely new Arduino shield, using new sensors and trying to avoid having lots of wires floating around.

We bought the 9 DOF stick sensor from sparkfun and the BMP085 barometer (used for altitude hold). When on the ¬†old shield we had the 3 sensors (ADXL345, L3G4200D, HMC588L) on different boards and linked to the shield by ¬†wires, we now have a single breakout board, directly soldered on the shield, which is a much better looking solution. It also avoids long steps of soldering, plugging mistakes etc... The EM406 breakout board is also directly soldered on the shield. We added two 7x2 connectors, in order to plug the RC receiver, SRF02 sonar sensor and possibly 2 servos in order to control a 2 axis gimball ūüôā The schematic looks like this:

The shield schematic

Another novelty of this shield is the connections with LEDs flexible strip. In addition to the aesthetic side, it will also be useful to distinguish the front from the back of the quadcopter, signal the end of calibration, blink during altitude hold mode etc.. These LEDs strip are not directly plugged to the Arduino, since they require 12V input and could be a bit too much power consuming to be fed from the Arduino. So they are controlled by a NPN BC517 transistor of which the base is connected to a digital output  like so:

How to control the LED strip with the Arduino

The last difference with our previous quadcopter is the change from a L3G4200D gyro in SPI mode to a ITG3200 in I²C mode. We did this partly to free all the SPI connections of the Arduino (10-13) because we needed available ports for the LEDs and for the future gimball, and because our L3G4200D was  pretty often giving completely false readings without any reason. We couldn't find the cause of this problem but we found a few people having the same problem  when we googled it... That pushed us to buy this stick sensor.

The final PCB looks like this:

The shield PCB

The holes and the contour were drilled with Benoit's CNC router and after soldering all the parts, the final results looks like this:

The Arduino shield with theEM406 breakout board, the 9 DOF stick IMU, and the BMP085 barometer

We have tried this shield with our old prototype frame and everything works fine, the flight is really stable.We use the LEDs to show the end of calibration and of course to show the front of the aircraft which is a big help when flying.

So as I said in the beginning of this article we are currently building a brand new frame entirely made of carbon fiber, with new motors, new ESCs and new propellers. We hope that we can finish the construction within the month to come (if we receive our order made at Hobbyking 2 weeks ago ūüôā ) and we will of course post pictures on the website! Stay tuned ūüėČ