Friday, January 18, 2013

Magnetometer calibration helper, for AVR Atmega, Arduino and other micro

A magnetometer is a measuring instrument used to measure the strength and, in some cases, the direction of magnetic fields.
A compass is a navigational instrument that measures directions in a frame of reference that is stationary relative to the surface of the earth.
We can use magnetometer to build a compass.



Magnetic distortions can be categorized as two types—hard iron and soft iron effects. Hard iron distortions arise from permanent magnets and magnetized iron or steel on the compass platform. These distortions will remain constant and in a fixed location relative to the compass for all heading orientations. Hard iron effects add a constant magnitude field component along each axes of the sensor output. The soft iron distortion arises from the interaction of the earth’s magnetic field and any magnetically soft material surrounding the compass.

This Python script implements a simple Magnetometer calibration routine to estimate soft and hard iron correction values.

The magnetomer calibration consist of collecting raw values from the magnetometer placing it in varios orientation.
Then the raw data collected are computed by one of the two script provided.
The math of the computation scripts is out of this document.
Script references:
* "ellipsoid fit" by Yury Petrov - http://www.mathworks.com/matlabcentral/fileexchange/24693-ellipsoid-fit
* "magneto" by http://www.sailboatinstruments.blogspot.com - http://sites.google.com/site/sailboatinstruments1/home

The bias obtained will correct hard iron errors, the scale factor will correct soft iron errors.



To obtain values, run this python script and follow the instructions.
A processing script is also provided to collect raw values.

Once you obtain those values you can get calibrated data by applying this formula:
xt_raw = x_raw - offsetx;
yt_raw = y_raw - offsety;
zt_raw = z_raw - offsetz;
x_calibrated = scalefactor_x[1] * xt_raw + scalefactor_x[2] * yt_raw + scalefactor_x[3] * zt_raw;
y_calibrated = scalefactor_y[1] * xt_raw + scalefactor_y[2] * yt_raw + scalefactor_y[3] * zt_raw;
z_calibrated = scalefactor_z[1] * xt_raw + scalefactor_z[2] * yt_raw + scalefactor_z[3] * zt_raw;


Once you obtain raw values using the script provided, to compute offset and
scale factor values, you can use the matlab script or magneto12 program provided.

On the microcontroller side you have to setup a function that print out to UART
raw values read from your chip.
Given 2 bytes (int16_t) variables for every axis, output the LSB and then MSB
byte ((uint8_t)(int16_t>>0) + (uint8_t)(int16_t>>8)), follow by a '\n' char.

Snippets are provided for AVR Atmega and Arduino, but it can be setup for other micro too.


Code


Notes
  • read risk disclaimer
  • excuse my bad english

96 comments:

  1. Fantastic!!! Thank you very very much for your work. There is finally a place where one can easily get a transparent idea about making Soft Iron Calibration. Site found, steps followed, Hard and Soft Iron Calibration done.
    María Jesús Sogorb Amorós

    ReplyDelete
  2. Dear Avide Gironi,
    that is really interesting, I have tried to do but not successful because I am beginner. I am working with sensor HMC5883L but the "processing software" can not read any data from this sensor this is Arduino code I got from Arduno example. can you please help me to send data to processing software.
    million thanks you
    kindly regards.
    Hoang

    #include //I2C Arduino Library

    #define address 0x1E //0011110b, I2C 7bit address of HMC5883

    void setup(){
    //Initialize Serial and I2C communications
    Serial.begin(9600);
    Wire.begin();

    //Put the HMC5883 IC into the correct operating mode
    Wire.beginTransmission(address); //open communication with HMC5883
    Wire.write(0x02); //select mode register
    Wire.write(0x00); //continuous measurement mode
    Wire.endTransmission();
    }

    void loop(){

    int x,y,z; //triple axis data

    //Tell the HMC5883 where to begin reading data
    Wire.beginTransmission(address);
    Wire.write(0x03); //select register 3, X MSB register
    Wire.endTransmission();


    //Read data from each axis, 2 registers per axis
    Wire.requestFrom(address, 6);
    if(6<=Wire.available()){
    x = Wire.read()<<8; //X msb
    x |= Wire.read(); //X lsb
    z = Wire.read()<<8; //Z msb
    z |= Wire.read(); //Z lsb
    y = Wire.read()<<8; //Y msb
    y |= Wire.read(); //Y lsb
    }

    //Print out values of each axis
    Serial.print("x: ");
    Serial.print(x);
    Serial.print(" y: ");
    Serial.print(y);
    Serial.print(" z: ");
    Serial.println(z);

    delay(250);
    }

    ReplyDelete
    Replies
    1. hello,
      change the serial output part of your code (Serial.print... etc..) to something the processing script magn_collectandviewpoints.pde can read, look at the "readfromserial()" method. Now i can not check that code, but something like this should work:
      Serial.write(ax);
      Serial.write(ax>>8);
      Serial.write(ay);
      Serial.write(ay>>8);
      Serial.write(az);
      Serial.write(az>>8);
      Serial.write('\n');

      Delete
  3. thank you very much it is working now

    ReplyDelete
  4. HI,
    I want to thank you for your magnetometer calibration work. I try to execute the magn_docalibration.py script by type python magn_docalibration.py -g, when successly conducted, it should plot the picture just like in your video and output a file named magnpoints.txt right? Thing is it output nothing just stop at move magn in every position to collect correct data, press any key to continue. I have no idea, cause in the chip side I have done this:
    while(UART_ReceiveData(SC1_UART)!= 0x20);
    UART_SendData(SC1_UART,(uint8_t)buf[3]);
    UART_SendData(SC1_UART,(uint8_t)buf[2]);
    UART_SendData(SC1_UART,(uint8_t)buf[5]);
    UART_SendData(SC1_UART,(uint8_t)buf[4]);
    UART_SendData(SC1_UART,(uint8_t)buf[7]);
    UART_SendData(SC1_UART,(uint8_t)buf[6]);
    UART_SendData(SC1_UART,'\n');
    Those buf are configured just like you said.
    Can you helo me, thanks a lot.

    ReplyDelete
    Replies
    1. Hello, you could use the python script (using the - g option), or the processing script to collect points.
      Using the python script you should see how many point are been collected.
      If you are skilled with Java, try debug it with the processing script. Try to understand what does is sent by the micro.

      Delete
  5. Hi Davide!

    Looks great
    By any chance do you have the Ellipsoid code in python too?

    Thanks
    Morgan

    ReplyDelete
    Replies
    1. Hello. Thank you for the feedback.
      About the python Ellipsoid version, not in the next future. I'm sorry.

      Delete
  6. Hi David, to ensure Im using your tool(magneto 1.2) correctly I have some questions can I send you a direct email? I connected with you on hangouts

    1) This is what I calculate for Norm of Magnetic field:
    For LSM303DLHC Data Sheet
    =========================

    GN bits set to 001 - +/- 1.3Gauss
    Gain (LSB/Gauss) - 1100

    Cork - 48,709.0 nT
    Latitude - 51° 53' 53"
    Longitude - 8° 29' 28"

    Total Field - 48,709.0 nT
    Convert to Gauss - 0.48709

    Raw Total Field - 535.799 Gauss

    2) I have raw magnetometer data and use that as 'h' ....

    3) I click calibrate and get the fields for 'b' and A^-1 filled in ...
    The last matrix (3x£3) which is A I believe ? is this the inverse of A^-1 ? I entered A^-1 in Matlab and then computed inv(A) but A*A^-1 I dont get a unity matrix? I must be missing something

    In the python code you have the following:
    ce = [-99.7, -154.0, -22.7] .. (1)
    m1 = [0.952017, 0.00195895, 0.0139661] ..(2)
    m2 = [0.00195895, 0.882824, 0.00760243]...(3)
    m3 = [0.0139661, 0.00760243, 0.995365] ..(4)

    In Matlab I get :
    >> magnetometer_calibration
    Ellipsoid center :
    0.115 0.0126 0.056 ... (5)
    Ellipsoid radii :
    0.474 0.375 0.346 .. (6)
    Ellipsoid evecs
    0.930281 -0.366051 0.0241799 ... (7)
    0.265757 0.627023 -0.732268 ...(8)
    0.252886 0.68764 0.680587 .. (9)
    Ellpisoid comp evecs :
    0.761667 0.0790756 0.00866253 .. (10)
    0.0790756 0.93419 0.0371094... (11)
    0.00866253 0.0371094 0.959287 ... (12)

    Is (1) in the python equivalent to (5) from Matlab above ?
    (2),(3),(4) equivalent then to (10),(11),(12) ?

    Thanks for the help Davide

    I have some sample data that I can share to do a quick sanity .. if it is okay to send you an email then I could explain better there?

    ReplyDelete
    Replies
    1. Hello.
      Yes, Ellipsoid center (5) is ce vector (1), and yes, vestor m1,2,and 3 (2), (3), (4) are Ellipsoid comp evecs vectors (10), (11), (12)
      Your offset values (ellpisoid center) seems too small to me, especially if compared to your ellipsoid shape.
      Yes, send me that email, but i tell you beforehand that I do not have time to do this type of sanity check in this period.

      Delete
  7. This comment has been removed by the author.

    ReplyDelete
  8. Hi Davide,

    Seem to have some problems posting ... will try again

    I have MagCal and Magneto matching alright using a 'Norm of Magnetic or Gravitational Field ' of 0.48709

    Magneto computes inverse A but these values do not match the matlab:
    Combined bias(b) :
    0.105409 0.024840 0.064395

    A^-1 :

    1.166645 0.083580 0.013880
    0.083580 1.376303 0.046656
    0.013880 0.046656 1.388847
    For Matlab I get the following
    >> magnetometer_calibration
    Ellipsoid center :
    0.115 0.0126 0.056
    Ellipsoid radii :
    0.474 0.375 0.346
    Ellipsoid evecs :
    0.930281 -0.366051 0.0241799
    0.265757 0.627023 -0.732268
    0.252886 0.68764 0.680587
    Ellpisoid comp evecs :
    0.761667 0.0790756 0.00866253
    0.0790756 0.93419 0.0371094
    0.00866253 0.0371094 0.959287

    ReplyDelete
  9. The bias of both look similar(?) but the matrices look quite different. I notice that the Matlab does not factor in 'Hm' (the Earths Magnetic Field)?

    Here is some of my data (I can share more if I could send you an email):

    -0.21300000 0.20000000 0.11200000
    -0.19500000 0.06900000 -0.13100000
    0.26900000 0.17800000 0.30200000
    -0.14700000 0.04300000 -0.19500000
    0.41300000 -0.00800000 0.21400000
    -0.07800000 0.16500000 -0.22400000
    0.40800000 0.21300000 0.04800000
    -0.12600000 0.01300000 -0.24300000
    0.22100000 0.34300000 -0.02400000
    -0.06900000 0.33000000 -0.04300000
    0.26500000 0.15200000 -0.23400000
    0.02600000 0.10400000 -0.26300000
    -0.10400000 0.20800000 -0.17500000
    -0.08200000 0.30000000 -0.06800000
    0.41300000 0.26000000 0.06300000
    -0.15200000 0.13400000 -0.17000000
    0.34700000 0.26500000 -0.07300000
    0.18600000 0.19100000 -0.23400000
    0.26000000 0.20800000 -0.19000000
    0.36900000 0.18200000 0.24800000
    0.47300000 0.07300000 0.07300000
    -0.22600000 0.17800000 0.01400000
    -0.14700000 0.24700000 -0.11700000
    0.23000000 0.30400000 0.24300000
    0.03400000 0.22600000 -0.21900000
    0.27800000 -0.07300000 0.35100000
    0.27300000 -0.31300000 0.08700000
    0.15200000 -0.33900000 0.14100000
    0.01700000 0.38200000 0.18500000
    0.20400000 -0.21700000 -0.16500000
    0.29100000 0.31300000 0.15100000
    0.40000000 0.05600000 0.25800000
    0.41300000 -0.18600000 -0.02400000
    0.44700000 -0.09500000 -0.02400000
    0.33400000 0.32600000 -0.00900000
    0.03900000 0.33400000 -0.13100000
    -0.19100000 0.19500000 -0.08200000
    -0.13000000 0.10400000 0.32600000
    -0.08200000 -0.09100000 0.35600000
    0.33000000 -0.23900000 0.23400000
    0.00400000 0.31300000 0.29200000
    0.27800000 0.13400000 -0.24300000
    0.17300000 -0.02600000 0.39500000
    0.18200000 -0.16500000 0.33100000
    0.42600000 0.14300000 0.16500000
    0.19500000 -0.33400000 0.02900000
    0.26000000 -0.26900000 -0.08700000
    0.19500000 -0.04300000 0.36500000
    0.36000000 -0.06900000 0.28200000
    0.39500000 0.18600000 0.17500000
    0.35600000 -0.09500000 0.30200000
    0.27300000 -0.30000000 -0.04300000
    0.20800000 -0.32600000 -0.01400000
    0.36900000 0.26900000 0.15600000
    -0.13000000 0.26000000 0.23400000
    -0.01700000 -0.27300000 0.25300000
    0.45200000 -0.16000000 0.08700000
    0.30000000 0.00800000 -0.24300000
    0.39100000 -0.22600000 0.11200000
    0.26500000 -0.17300000 -0.19500000
    0.46500000 -0.06900000 0.10200000
    0.09500000 0.28600000 -0.20000000
    -0.01300000 0.23000000 -0.22400000
    -0.07800000 0.30800000 0.22900000
    0.22600000 -0.03000000 0.37500000
    0.04300000 -0.02100000 -0.27300000
    0.30000000 0.09100000 -0.23400000
    0.25200000 -0.06000000 0.34100000
    0.33400000 -0.04700000 0.29700000
    0.20800000 0.29100000 0.25800000
    -0.12600000 -0.09100000 0.32100000
    0.10000000 -0.32600000 -0.01400000
    0.48200000 0.00000000 0.00000000
    0.35600000 -0.10800000 0.29200000
    0.42600000 0.06000000 -0.11200000

    ReplyDelete

  10. I have put some of my data up on Google Docs ... when you have some time can you have a look for me thanks
    https://docs.google.com/document/d/1hIvlzmd92Pt76edXUp1JY0g_7ydeiY2YPl4pvC8FhzY/edit?usp=sharing

    ReplyDelete
    Replies
    1. Hello,
      Magneto uses a slightly different math for the Ellipsoid calculation. But the values should be similar.
      Try with another magnetometer, the one that's looks odds to me are the ellipsoid center (offset) values.

      Delete
  11. This comment has been removed by the author.

    ReplyDelete
  12. Hello thank you for your good projects .I download this project and when I run python script I get this massage: "ImportError: No module named serial"

    ReplyDelete
  13. hello excuse me for these questions i'm not familiar to python i install pyserial on python and i no get this error:
    Traceback (most recent call last):
    File "C:\Users\mazir\Desktop\Elctronic Project\campass calibration software\magn_docalibration.py", line 18, in
    from mpl_toolkits.mplot3d import Axes3D
    ImportError: No module named 'mpl_toolkits'

    ReplyDelete
    Replies
    1. No problem. "No module named" means that you have to install that module, So you have to install the mpl_toolkits module. More generally, you can find usefull information on Traceback googling aroud for the error. You should encounter a few if you have no module installed.

      Delete
    2. This comment has been removed by the author.

      Delete
  14. This comment has been removed by the author.

    ReplyDelete
  15. hello I finally could run magn_docalibration.py and gave this massage :
    New folder\magn_docalibration.py
    Magnetometer Calibration functions 01

    Usage: magn_docalibration.py [options]

    Options:
    -h, --help show this help message and exit
    -d, --debug debug output
    -g, --get collect data from chip
    -p, --plot plot raw values collected
    -t, --testcalib compute estimation of collected data and do plot, need
    calibration points to be setup with other script

    Now what I should do to plot?

    ReplyDelete
    Replies
    1. Hello.
      It's the python script help. As example run "magn_docalibration.py -g" to collect data.
      Then you could use the matlab script provided to compute calibration values.
      Tip: you can also collect the data using the processing sketch (like I do in the youtube video on this page)

      Delete
  16. hello
    excuse me I'm a little confused.I connect hmc5883l(gy_271) to AVR board and then to serial com1 on my computer and load hex file to AVR Atmega16l then run magn_docalibration.py in python shell and now what I have to do to plot like you do in youtube video? I can see hmc5883l send data to hyper terminal .

    ReplyDelete
    Replies
    1. You have to collect data points. You can do this by the processing sketck, or using the magn_docalibration.py script.

      Delete
  17. and when I run magn_docalibration.py -g

    I get the following error:
    Traceback (most recent call last):
    File "", line 1, in
    magn_docalibration.py -g
    NameError: name 'magn_docalibration' is not defined

    ReplyDelete
    Replies
    1. This script is writte on Python 2.7, that may be the problem.

      Delete
  18. just right now I install python2.7 nothing change I will try some other code to do it .I'm new in python thank you

    ReplyDelete
    Replies
    1. No problem at all. It should have somthing to do with your version of python, here I've no problem. you could try python -V to check your python version. Also try the processing.org script to collect the data point. That could be easyer.

      Delete
  19. where I should write python -V?in python shell or cmd?

    ReplyDelete
    Replies
    1. In your command prompt or terminal. Here you can find a Python tutorial that may help you: http://www.tutorialspoint.com/python/

      Delete
  20. hello i can't run python script .I want to collect 100 point in then run ellipsoid fitting to these points .all in atmega16 without any PC. can you give me an algorithm to how can I do it?

    ReplyDelete
    Replies
    1. Hello, you can collect 100 point to an array, then use this array of points as you need.

      Delete
  21. hello .i can't run python script .i want to run the ellipsoid fitting in atmega16 .collecting 100 point and then run fitting part. can you give me an algorithm how to do it?

    ReplyDelete
    Replies
    1. If you can not run the python script, you can run the processing.org script provided in the download link of this page in order to collect the points.

      Delete
  22. i can't find processing.org script ? where is it?

    ReplyDelete
    Replies
    1. It's the magn_collectandviewpoints\magn_collectandviewpoints.pde file.

      Delete
  23. hello . I connect hmc5883l to atmega16 and connect them to com1 and I can see numbers on hyperterminal and when run python script in windows cmd , first i get this massage:
    "Usage: magn_docalibration.py [options]

    Options:
    -h, --help show this help message and exit
    -d, --debug debug output
    -g, --get collect data from chip
    -p, --plot plot raw values collected
    -t, --testcalib compute estimation of collected data and do plot, need
    calibration points to be setup with other script "
    and then i type "magn_docalibration.py -g" i get this massage " move magn in every position to collect correct data, press any key to continue" . and then i move magn in every position and then press enter nothing happened what should i do? no point collected

    ReplyDelete
    Replies
    1. I suppose there's something wrong in your python library, you should get points collected to a magnpoints.txt file. You have to debug the python script, check the collectpoints method.

      Delete
  24. is there any manipulation of code to do it right? which code have to be programmed to avr?

    ReplyDelete
    Replies
    1. The avr firmware sample can be found in the readme file, the "avr-gcc snippet" part, if you are using avrgcc as compiler. For debugging the python code, the most simple way is to use the print function, or you should use the python debugger.

      Delete
  25. excuse me can you record a short video of python code running?
    and send your code to my email: maziar111@gmail.com i try so but nothing i can read degree in lcd 16*2 and usart but python nothing

    ReplyDelete
    Replies
    1. Hello. I'm sorry but i can not do this. I'm actually envolved full time in other project, the point collection running in python is something like the one you see in the video of this post, which is running on processing.org. If you can not run the python script, my suggestion is to run the processing script. Or find another script online to collect your points from the magnetometer.

      Delete
  26. This comment has been removed by the author.

    ReplyDelete
  27. Hello in magn_docalibration.h file i found code line:
    //wait for the input request
    while(cgetc() != 0x22);
    what this mean?

    ReplyDelete
    Replies
    1. It means, wait until a 0x22 it is read by the uart library of your micro.

      Delete
  28. hello excuse me again i'm totally cunfused what's problem. in python I open magn_docalibration.py and in RUN menu Run Moudle and i get first this massege:
    "" Python 3.5.1 (v3.5.1:37a07cee5969, Dec 6 2015, 01:38:48) [MSC v.1900 32 bit (Intel)] on win32
    Type "copyright", "credits" or "license()" for more information.
    >>>
    RESTART: C:\Users\mazir\Desktop\Elctronic Project\campass calibration software\magn_docalibration.py
    Magnetometer Calibration functions 01

    Usage: magn_docalibration.py [options]

    Options:
    -h, --help show this help message and exit
    -d, --debug debug output
    -g, --get collect data from chip
    -p, --plot plot raw values collected
    -t, --testcalib compute estimation of collected data and do plot, need
    calibration points to be setup with other script""
    After that when I TYPE:""magn_docalibration.py -g"" I get this massage:
    ""Traceback (most recent call last):
    File "", line 1, in
    magn_docalibration.py -g
    NameError: name 'magn_docalibration' is not defined
    >>>
    ""
    and I totaly confused what should I do"" PLEASE HELP ME ""

    ReplyDelete
    Replies
    1. Python 3.5.1 (v3.5.1:37a07cee5969, Dec 6 2015, 01:38:48)
      this is whole PYTHON:

      [MSC v.1900 32 bit (Intel)] on win32
      Type "copyright", "credits" or "license()" for more information.
      >>>
      RESTART: C:\Users\mazir\Desktop\Elctronic Project\campass calibration software\magn_docalibration.py
      Magnetometer Calibration functions 01

      Usage: magn_docalibration.py [options]

      Options:
      -h, --help show this help message and exit
      -d, --debug debug output
      -g, --get collect data from chip
      -p, --plot plot raw values collected
      -t, --testcalib compute estimation of collected data and do plot, need
      calibration points to be setup with other script
      >>> magn_docalibration.py -g
      Traceback (most recent call last):
      File "", line 1, in
      magn_docalibration.py -g
      NameError: name 'magn_docalibration' is not defined
      >>>

      Delete
  29. hello again I programmed my avr and connect to com1 by usart when I run processing.org i get this massage

    "Display -1 does not exist, using the default display instead.
    WARNING: RXTX Version mismatch
    Jar version = RXTX-2.2pre1
    native lib Version = RXTX-2.2pre2
    OpenGL error 1280 at after bind: invalid enumerant
    "

    ReplyDelete
    Replies
    1. Hello, this is a processing.org error on the RXTX port, you can find usefull information to recover this error on the processing.org forum. You can start checking if your Java Runtime edition fits the processing.org version. Also try to start some processing.org sample, to see if event the samples sketch does not works.

      Delete
  30. This comment has been removed by the author.

    ReplyDelete
  31. This comment has been removed by the author.

    ReplyDelete
  32. Hey Davide !

    Your work is awesome !
    I would like to use the matlab function to calibrate my magnetometer.
    I run it and it works perfectly. However, I need to calculate the calibrated data right ?
    You said that we have to use the scale factor and offset value to get the new data but I don't see those variables in the matlab function.
    Could you help me ?

    Thank you very much!

    Yann

    ReplyDelete
    Replies
    1. Hello, and thank you for your feedback. Look at minute 2:18 of my youtube video, the matlab output you have to use is the one i'm selecting.

      Delete
    2. Ok thank you for answering.
      Tell me if I am write :
      e_center are the x,y,z offset and comp (line 1) = scalefactor_x and comp (line2) and comp(line3) are y and z respectively ?

      Actually I calculate that and I don't get a perfect circle for the calibrated data, I think I should, should I ?

      Moreover, if I calculate the module of the x,y and z (u = sqrt(x.^2 + y.^2 + z.^2) I should find the same one, right ?

      Thank you for your time.

      Delete
    3. I also don't use Ellipsoid radii and Ellipsoid evecs, is it normal ?

      Delete
    4. Hello, take a look at the "MorganMay 15, 2015 at 1:28 PM" comment and reply.

      Delete
    5. Thank you for taking the time to answer.
      Actually I read his comments and replies but it does not seem to work correctly. So I probably make a mistake somewhere.

      Fisrt when I run the matlab code I get a ellipse at the end.. I should get a circle right ?
      A good calibration leads to a circle, isn't it ?

      Second I used Magneto 1.2, and I don't succeed by this way also.
      To be sure in Norm of Magnetic I put the one I would like to reach or I put the one I calculate from the raw data (if it's points 2, the norm is different at every point since it's not calibrated yet).

      Thank you for your help. It's been a while I am working on this magnetometer calibration and it seems strange that it still does not work properly...

      Delete
    6. The green image should be a the fitted ellipsoid. Should be something next to a circle.
      At first, what calibration does not works for you? soft, or hard iron distortion calibration?

      Delete
    7. Hello Davide! Sorry I have not seen you reply. Thank you.
      I don't exactly know (how can I know this ?)

      After the calibration, I plot the 3 angles (using atan2 on matlab).
      And when I move the device only around one axis, I should find a angle of zero (around zero) for one of the three angles, and I don't.
      So I suppose that the calibration process is wrong.

      I don't know if you see what I mean.

      Delete
    8. If you put a magnet next to you magnetometer it will procude hard-iron distorsion. It is identified on the graph by an offset of the original of the collected point to the graph origin (0,0). Indeed the soft-iron distorsion is visibile by a non perfect circle, collected points will look like an ellipse.
      What angles are you talking about?

      Delete
    9. Ok thank you for the answer.
      So I did not solve both distorsion. I have an offset (this is pretty easy to correct I think) and I also get a kind of an ellispe, so this is soft_iron distorsion.
      I would like to get the angle of my device related to the north. So forward/backwar axis is x, medio-lateral is y and vertical is z. So I need the angle between z and y.

      Thank you for helping me.

      Delete
    10. I'm sorry but I don't get it. Usually, if your z-axis is parallel to ground, you get y and x axis value, then you measure the heading of your device by arctan2 those values. Then you transform this value to degree, and you add the magnetic declination for your location.

      Delete
    11. Ok thank you for your help. I will keep going to find my needs.

      Delete
  33. This comment has been removed by the author.

    ReplyDelete
  34. hello processing running without any error and avr(main.c programmed to avr) send code to com1 but no point shown in processing 3d plot. is there any code manipulation needed? or i'm programmed wrong code on avr?

    ReplyDelete
    Replies
    1. What do you get in processing reading routine? Debug there.

      Delete
  35. Hello thank you for your great work.I'm using Arduino2560mega.my code is:
    #include "Wire.h"
    #include "HMC5883L.h"

    HMC5883L compass; //Copy the folder "HMC5883L" in the folder "C:\Program Files\Arduino\libraries" and restart the arduino IDE.
    int16_t xv, yv, zv;

    void setup()
    {
    Serial.begin(9600);
    Wire.begin();
    compass = HMC5883L();
    setupHMC5883L();
    }

    void loop()
    {
    getHeading();

    Serial.flush();
    Serial.print(xv);
    Serial.print(",");
    Serial.print(yv);
    Serial.print(",");
    Serial.print(zv);
    Serial.println();
    //Serial.write(xv);
    //Serial.write(xv>>8);
    //Serial.write(yv);
    //Serial.write(yv>>8);
    //Serial.write(zv);
    //Serial.write(zv>>8);
    //Serial.write('\n');
    // delay(100);
    }

    void setupHMC5883L()
    {
    compass.SetScale(0.88);
    compass.SetMeasurementMode(Measurement_Continuous);
    }

    void getHeading()
    {
    MagnetometerRaw raw = compass.ReadRawAxis();
    xv = (float)raw.XAxis;
    yv = (float)raw.YAxis;
    zv = (float)raw.ZAxis;
    }
    in hyperterminal can see:
    29,476,455
    29,476,455
    29,476,455
    28,479,456
    28,479,456
    28,479,456
    28,479,456
    28,479,456
    30,475,461
    30,475,461
    30,475,461
    30,475,461
    30,475,461
    28,479,455
    28,479,455
    28,479,455
    28,479,455
    28,479,455
    27,473,455
    27,473,455
    27,473,455
    27,473,455
    27,473,455
    27,477,461
    31,477,461
    31,477,461
    31,477,461
    31,477,461

    hyperterminal show x,y,z. I want to use your processing sketch.
    what changes in my code is needed?

    ReplyDelete
    Replies
    1. Hello. You have to use the commented part of "//Serial.write". Please do not flood with data comments.

      Delete
  36. This comment has been removed by the author.

    ReplyDelete
  37. This comment has been removed by the author.

    ReplyDelete
  38. hello again.I use commented codes:
    Serial.write(xv);
    Serial.write(xv>>8);
    Serial.write(yv);
    Serial.write(yv>>8);
    Serial.write(zv);
    Serial.write(zv>>8);
    Serial.write('\n');
    and programmed arduino
    and changes processing codes line to:
    static String serialport = "COM4";
    static int serialbaud = 9600;
    processing is running but no point shown 0/1000. it's like processing sketch don't get the serial numbers .but when I use HyperTerminal instead of processing , I can see Numbers changes in HyperTerminal of windows7.

    ReplyDelete
    Replies
    1. So, it seems something related to you processing engine. You have to debug the serialEvent. At first, write a new sketch and a new simple rom for your micro, and test if it works. It should be simpler this way.

      Delete
  39. hello I found the processing code problem by do //port.write(0x22); the processing plot points. i think there is problem with port.write command. thank you so much your work is so great.

    ReplyDelete
  40. hello thank you Davide thank you ariyan.I comment //while(cgetc() != 0x22); in magn_docalibration.h and also commend //port.write(0x22); in processing . and everything work good thank you so much.

    ReplyDelete
  41. Hello David i think that this is not a reel time calibration am i right ?

    ReplyDelete
    Replies
    1. Hello, not this one. But you can implement it insiede the microcontroller logic.

      Delete
  42. This comment has been removed by the author.

    ReplyDelete
  43. Hi Davide Gironi,
    Thank you for a great post.
    I am calibrating the magnetometer follow your post.
    I read to your second reference [ https://sites.google.com/site/sailboatinstruments1/proof ]
    On step 2 of [ E. Geometric interpretation ]
    would you mind explain for me why it need to multiple inverse rotation?
    Thank you,

    ReplyDelete
    Replies
    1. Dear Minh, unluckily this post here was from 2013, I've forgot the math behind this, I've to read it and study this again, but I've not time to do this now.

      Delete
    2. Thank you for your reply,
      I have tried calibrate the magnetometer (BMX055 from BOSCH)
      But it didnot work well.
      The ellipsoid fitting function as already mention in your guideline was not work well. It just draw a 1/4 mesh of ellipsoid shape in the result. Have you seen this situation before?
      Thank you.
      Sorry for my English.

      Delete
    3. Never happened. I've used this calibration routine 6 month ago. And it works. You can check if the scripts that collect the points works well. Then try the "magneto" tools also.

      Delete
  44. Would you mind have a look on my Matlab code. I have checked my code and I can not figure out what happend. It recieves the data of magnetometer (x,y,z order):

    ///////////
    %% Connect to arduino
    clc;
    delete(instrfindall);
    Port = 'COM3';
    BaudRate = 9600;
    DataBits = 8;
    Parity = 'None';
    s = serial(Port);
    set(s, 'BaudRate', BaudRate);
    set(s, 'DataBits', DataBits);
    set(s, 'Parity', Parity);
    set(s, 'StopBits', 1);
    fopen(s);
    %% Take sample for arduino
    sample = 4000;
    x = [];
    y = [];
    z = [];
    fprintf( 'Taking sample now ');
    for element_th = 1:1:sample
    temp = fgets(s);
    k = strsplit(temp);
    x(element_th) = str2double(k(1));
    y(element_th) = str2double(k(2));
    z(element_th) = str2double(k(3));
    display(temp);
    display(element_th);
    plot3(x,y,z,'.');
    drawnow;
    end
    x(1) = x(2);

    %% Plot
    %plot3(x,y,z,'o');
    %% Fitting
    x = transpose(x);
    y = transpose(y);
    z = transpose(z);
    %%
    % [center, radii, evecs, pars ] = ellipsoid_fit([x y z]);
    % plot3(x,y,z,'.');
    % display(center)
    % display(radii)
    % display(evecs)
    % display(pars)


    % do ellipsoid fitting
    [e_center, e_radii, e_eigenvecs, e_algebraic] = ellipsoid_fit([x, y, z]);

    % compensate distorted magnetometer data
    % e_eigenvecs is an orthogonal matrix, so ' can be used instead of inv()
    S = [x - e_center(1), y - e_center(2), z - e_center(3)]'; % translate and make array
    scale = inv([e_radii(1) 0 0; 0 e_radii(2) 0; 0 0 e_radii(3)]) * min(e_radii); % scaling matrix
    map = e_eigenvecs'; % transformation matrix to map ellipsoid axes to coordinate system axes
    invmap = e_eigenvecs; % inverse of above
    comp = invmap * scale * map;
    S = comp * S; % do compensation

    % output info
    fprintf( 'Ellipsoid center :\n %.3g %.3g %.3g\n', e_center );
    fprintf( 'Ellipsoid radii :\n %.3g %.3g %.3g\n', e_radii );
    fprintf( 'Ellipsoid evecs :\n %.6g %.6g %.6g\n %.6g %.6g %.6g\n %.6g %.6g %.6g\n', e_eigenvecs );
    fprintf( 'Ellpisoid comp evecs :\n %.6g %.6g %.6g\n %.6g %.6g %.6g\n %.6g %.6g %.6g\n', comp);

    % draw data
    figure;
    hold on;
    plot3( x, y, z, '.r' ); % original magnetometer data
    plot3(S(1,:), S(2,:), S(3,:), 'b.'); % compensated data
    view( -70, 40 );
    axis vis3d;
    axis equal;

    % draw ellipsoid fit
    figure;
    hold on;
    plot3( x, y, z, '.r' );
    maxd = max(e_radii);
    step = maxd / 30;
    [xp, yp, zp] = meshgrid(-maxd:step:maxd + e_center(1), -maxd:step:maxd + e_center(2), -maxd:step:maxd + e_center(3));
    Ellipsoid = e_algebraic(1) *xp.*xp + e_algebraic(2) * yp.*yp + e_algebraic(3) * zp.*zp + ...
    2*e_algebraic(4) *xp.*yp + 2*e_algebraic(5) * xp.*zp + 2*e_algebraic(6) * yp.*zp + ...
    2*e_algebraic(7) *xp + 2*e_algebraic(8) * yp + 2*e_algebraic(9) * zp;
    p = patch(isosurface(xp, yp, zp, Ellipsoid, 1));
    set(p, 'FaceColor', 'g', 'EdgeColor', 'none');
    alpha(0.5);
    view( -70, 40 );
    axis vis3d;
    axis equal;
    camlight;
    lighting phong;
    %%
    fclose(s);
    /////////
    Thank you.

    ReplyDelete
    Replies
    1. Dear Minh, to get data first start with the python script provided or with the processing one. Then, if all goes ok with that scripts, you can rewrite it in Matlab.

      Delete
  45. i cant find
    magn_collectandviewpoints.pde

    ReplyDelete
    Replies
    1. oh okay it was in avr_helper_magn_docalibration_01.zip

      Delete
    2. Hello omeranar, great! You find it :) Hope is working, cause it's a bit outdated project.

      Delete
  46. There is a small challenge in this , let's say i dont want to get into any hassle of the python script of anything i want the calibration to be a part of my main application code in the arduino , then how do i execute this ?

    ReplyDelete
    Replies
    1. Hello, you can build your own script to retrive data points, then use "ellipsoid fit" matlab script or "magneto" software.

      Delete
  47. Hi
    Could you tell me some features of using the Magwick filter? I am currently developing a spatial adjustment device based on the MPU6050 and HMC5883, but the device does not work correctly. I would appreciate any help.

    ReplyDelete
    Replies
    1. Hello, you can find a Mahony filter implemented there in my driver http://davidegironi.blogspot.com/2013/02/avr-atmega-mpu6050-gyroscope-and.html. That's nothing more than the matematical model proposed by Mahony and Magwick. Honestly I've done this back in the 2013 so I have to recall my memories. What I can tell you is that although the MPU6050 DMP processor save your microcontroller resources, the Mahony filter in my opinion works a little better. You have to pay attention to the calibration of course.

      Delete