Introduction: Configure, Read Data & Calibrate the HMC5883L Digital Compass Using Python
As in other blogs, this one also contains information that can be found elsewhere on the web. I just structured it to make it easy to use for beginning robot hobbyists.
Connecting the compass is quite simple. When mounting the sensor to the bot, be aware of the x-y-z marker on the sensor and assure that the x-> points towards the front of the bot.
The compass uses the I2C protocol. Connect the sensor to the (I2C-) GPIO pins on the Pi as follows:
- Connect VCC to pin-1 (3.3.V)
- Connect SDA to pin-3 (SDA = System Data)
- Connect SCL to pin-5 (SCL = System Clock)
- Connect GND to pin 7 (GND)
First of all the Raspberry Pi needs to be configured for using the I2C protocol. This is disabled by default on the Raspberry. You’ll find an excellent tutorial at: Adafruit: configuring Raspberry Pi for I2C
To calibrate the compass the bot will have to be turned several times. This can be done by hand. I used a rotating snack display to make turning the bot, while keeping it horizontal, easy.When testing the I2C connection as explained at the end of the Adafruit lesson, the 0x1e address will most likely be shown for the HMC5883L sensor. If you get a different address you’ll have to change the address variable in the scripts.
The next step is reading and displaying data from the sensor. For this I used a very simple script which can be found here:
How to use the sensor registers for binary reading and writing can be found in the sensor data sheet and in the script. I won’t go into that details here. The script initializes the sensor as follows:
- 8 samples at 15 Hz,
- LSb gain 1.5 (default),
- Gauss 1090 (default),
- continuous sampling,
- scaling 0.92
If you don’t want to dive into the sensor characteristics, just leave the values as in the script. They are pretty generic and will suffice for almost all situations.
Now the script reads the x, y and z registers, calculates the arctangent of x and y and converts the outcome into degrees. Just run the script and you’ll get a first bearing e.g. 46.53891942 (46° 53’ 89.2”) Start rotating the bot until you get close to 0 degrees. Then rotate it through four 90° positions, reading the sensor data at every step. Most probably you might see that the values deviate and are not fully right.
So let’s figure out what is happening. For this you can use the next simple script:
This script gathers 500 readings and writes the values to a CSV file which can easily be imported into Excel and be plotted in a scattered graph.Run the script ( sudo python HMC5883L_calibrate_step_2.py ) and start rotating the bot back and forwards through 360° while the script is running.The outcome should be something like in the picture.
Now the offset (just a slight one in my case) is visualized. To get a more specific value for the x and y offset, you can use the third script, which can be found here:
This script again takes 500 reading, keeps the minimum and maximum values of x and y and calculates the offsets. Run the script in the same way as the former script, constantly rotating the bot. You should get something like this:
- minx: -212
- miny: -246
- maxx: 262
- maxy: 239
- x offset: 25
- y offset: -4
Now these offsets can easily be applied to the script, which is done in this example:
(To make a new plotting, the script can be combined with step 2 to have the values saved into a CSV file.)
The last step to get a rather accurate working compass, is applying the declination. You’ll find your specific declination through this page: magnetic declination
Bear in mind that the declination is in degrees, so subtract the declination when the bearing is calculated in degrees. An example can be found here:
Now the calibrating is done, you can run the last script and repeat the first test by rotating the bot through 90° steps and even compare the bearing with another digital compass (on your cell phone for example).
That’s it !
8 months ago
it's a useful project. I changed it to ESP32 instead or Raspberry. Please write me how to rotate.
5 years ago
This is an excellent and very helpful tutorial.
I have a question so simple that I feel like I am just overlooking something, but..
Why did you scale the raw x and y variables by 0.92?
Reply 5 years ago
It is an emperical value for temperature calibration. You'll find a short description in one if the chip-datasheets (e.g. https://cdn.sparkfun.com/datasheets/Sensors/Magnet...
0.92 gave the best (-constant) results
Reply 5 years ago
6 years ago
Also (sorry, saw this right after) I think you should subtract (and not add) the magnetic declination to get the true (geographic) bearing. At least I understood that from the wikipedia page: https://en.wikipedia.org/wiki/Magnetic_declination.
Reply 6 years ago
You're right both times. Obviously I left some copy-&-paste in script 4 and 5. And declination should indeed be subtracted. I'll correct those items tonight to make the whole a usefull intsructable. Thanks for your remarks!
6 years ago
First of all, great guide, thank you for it! Just a couple of comments (doubts?):
- I think that in the file "HMC5883L_calibrate_step_5.py" you forgot to erase lines 50 to 52, no?
- I run the code and in my case I got different "spans" for X and Y. You got maxx-minx = 485; and maxy-miny= 474, where in my case it was: 388 and 435 resp. Although I think the SHOULD be the same (of close), do you think I should rescale one of them, in other to get a circle (when graphing the data) and not a ellipse?
All the best,