HMC5883L magnetometer calibration

widgets Article
HMC5883L magnetometer calibration

Here is a brief explanation for the calibration of HMC5883L magnetometer  . Welcome to communicate, Sina Changsha @WalkAnt.

         (Updated on 2016-10-15, recently WalkAnt is okay privately, compiled a small software AntMag for magnetometer calibration, which can help everyone to calibrate magnetometer very well. Website:

        First look at this article:

        If the magnetometer is operated in an environment containing an additional local magnetic field , additional correction of the magnetometer output will be necessary. Without the influence of any local magnetic field , the following figure 1 can be generated by rotating the plane 360 ​​°  , and Figure 2 shows the introduction of a local magnetic field.

View Image View Image

        Method 1. The corrected output can be calculated according to the following method:
        1) Under the condition of magnetic field interference, the data collection device is rotated 360 °.
        2) The data is analyzed to produce biased offset and sensitivity scale factors to compensate for the interference.

        The maximum output of the X and Y magnetometers found in the data:
                        X min =  -0.284gauss  X max  =  + 0.402gauss
                   Y min  = -0.322gauss  Y max  = + 0.246gauss
        It can be seen from the X axis data, X has a greater response, we set its scale factor to 1
                        X s  = 1
        Then calculate other scale factors:
                                    ( X max-  X min )
                        Y s  = ————————
                                    ( Y max-  Y min )

        For offset compensation:
                        X b  = X s [1/2 ( X max-  X min -X max  ]
                        Y b  = Y s [1/2 ( Y max-  Y min -Y max  ]

        The correct output:
                        X out  = X in * X s  + X b
                        Y out  = Y in * Y s  + Y b

        Method Two.
                1) Horizontal and uniform rotation, collecting XY axis data
                2) Turn the equipment 90 degrees (Z axis) at a constant speed to collect Z axis data
                        Xoffset = (Xmax + Xmin) / 2
                        Yoffset = (Ymax + Ymin) / 2
                        Zoffset = (Zmax + Zmin) / 2

                Subtract the offset from the bare value read by the magnetometer to get the heading value used for angle calculation
                        XH = X-Xoffset
                        YH = Y-Yoffset
                        ZH = Z-Zoffset

        Horizontal test, obtained azimuth = arctanYH / XH
        For non-horizontal testing, you need to use an accelerometer for inclination compensation, first calculate the roll angle Roll and pitch angle Pitch, and then calculate the Heading value:
                       XH = x * cos (P) + Y * sin (R) * sin (P) -Z * cos (R) * sin (p)
                        YH = Y * cos (R) + Z * sin (R)


        For why to set the offset, please refer to the following article:

        ST integrated sensor solution realizes electronic compass function:

        HMC5883L frequently asked questions:


        Here I used the simple method above to calculate an offsetX, offsetY, offsetZ, and then subtracted this offset to get the correct result. Below is the code. Below I did a process (such as: mag.x * 0.2 + magRange [0] * 0.8) This is because occasionally an abnormal value appears in mag.x mag.y mag.z, which makes the calculated offsetX   offsetY offsetZ inaccurate, so this filtering process is added.


        static float magRange [6] = {1.0, -1.0,1.0, -1.0,1.0, -1.0}; // magRange [0] corresponds to X minimum, magRange [1] corresponds to X maximum

        // Magnetometer not yet used more then for logging.
        // Magnetometer has not been used, just log records.
        imu9Read (& gyro, & acc, & mag);
        if (magRange [0]> mag.x) magRange [0] = mag.x * 0.2 +  magRange [0] * 0.8// x min
        if (magRange [1] <mag .x) magRange [1] = mag.x * 0.2 + magRange [1] * 0.8;  // x max
        if (magRange [2]> mag.y) magRange [2] = mag.y * 0.2 + magRange [2 ] * 0.8;
        if (magRange [3] <mag.y) magRange [3] = mag.y * 0.2 + magRange [3] * 0.8;
        if (magRange [4]> mag.z) magRange [4] = mag .z * 0.2 + magRange [4] * 0.8;  // z min
        if (magRange [5] <mag.z) magRange [5] = mag.z * 0.2 + magRange [5] * 0.8;  // z max
        magOffset [0] = (magRange [0] + magRange [1]) / 2.0;  magOffset [2] = (magRange [4] + magRange [5]) / 2.0;
         magOffset [1] = (magRange [2] + magRange [3]) / 2.0;
         mag.x-= magOffset [0];
        mag.y-= magOffset [1];
        mag.z-= magOffset [2];


        Self-testing is also important. Through the self-detection function provided by the HMC5883l chip, perform self-test, and then find a scale factor. The magnetic field can be corrected by multiplying the detection value of the sensor by this scale factor. I didn't use it in the code. For more detailed information, please see the following English.

For related source code, you can refer  to the bool hmc5883lSelfTest () function in Crazyflie  firmware. 

Self test



To check the HMC5883L   for proper operation, a self test feature in incorporated in which the sensor offset straps are excited to create a nominal field strength (bias field) to be measured. To implement self test, the least significant bits (MS1 and MS0) of configuration register A are changed from 00 to 01 (positive bias) or 10 (negetive bias), eg 0x11 or 0x12.


Then, by placing the mode register into single-measurement mode (0x01), two data acquisition cycles will be made on each magnetic vector. The first acquisition will be a set pulse followed shortly by measurement data of the external field. The second acquisition will have the offset strap excited  (about 10 mA) in the positive bias mode for X, Y, and Z axes to create about a ± 1.1 gauss self test field plus the external field. The first acquisition values   will be subtracted from the second acquisition, and the net measurement will be placed into the data output registers. 


Since self test adds ~ 1.1 Gauss additional field to the existing field strength, using a reduced gain setting prevents sensor from being saturated and data registers overflowed. For example, if the configuration register B is set to 0x60   (Gain = 3), values ​​around +766 LSB   (1.16 Ga * 660 LSB / Ga) will be placed in the X and Y data output registers and around +713 (1.08 Ga * 660 LSB / Ga) will be placed in Z data output register. To leave the self test mode, change MS1 and MS0 bit of the configuration register A back to 00 (Normal Measurement Mode), eg 0x10. 


Scale factor calibration



Using the self test method described above,   the user can scale sensors' sensitivity   to match each other.   Since placing device in positive bias mode   (or alternatively negative bias mode) applies a known artificial field on all three axes, the resulting ADC measurements in data output registers can be used to scale the sensors. For example, if the expected self test value for X-axis   is 766 and the actual value   is 750 then a scale factor of (766/750) should be multiplied to all future readings of X -axis. Doing so for all three axes will ensure their sensitivity are well matched.


The built-in self test can also be used to periodically compensate the scaling errors due to temperature variations. A compensation factor can be found by comparing the self test outputs with the ones obtained at a known temperature. For example, if the self test output is 750 at room temperature and 700 at the current temperature then a compensation factor of (750/700) should be applied to all current magnetic readings. A temperature sensor is not required using this method.


The bool hmc5883lSelfTest () function code in Crazyflie firmware is as follows, for reference only: (various definitions in the function are not given here)

bool hmc5883lSelfTest ()
  bool testStatus = TRUE;
  int16_t mxp, myp, mzp;   // positive magnetometer measurements
  int16_t mxn, myn, mzn;   // negative magnetometer measurements
    uint8_t configA;
    uint8_t configB;
    uint8_t mode;
   } regSave

  // Save register values
  if (i2cdevRead (I2Cx, devAddr, HMC5883L_RA_CONFIG_A, sizeof (regSave), (uint8_t *) & regSave) == FALSE)
    // TODO: error handling
    return FALSE;
  // Set gain (sensitivity)
  hmc5883lSetGain ( HMC5883L_ST_GAIN);

  The Register and the Write CONFIG_A do // positive the Test
  i2cdevWriteByte (the I2Cx, DevAddr, HMC5883L_RA_CONFIG_A,
      (HMC5883L_RATE_15 << (HMC5883L_CRA_RATE_BIT - HMC5883L_CRA_RATE_LENGTH + 1)) |

  hmc5883lSetMode (HMC5883L_MODE_SINGLE);
  vTaskDelay (M2T (HMC5883L_ST_DELAY_MS));
   hmc5883lGetHeading (& mxp, & myp, & mzp);

  The Register and the Write CONFIG_A do // negative the Test
  i2cdevWriteByte (the I2Cx, DevAddr, HMC5883L_RA_CONFIG_A,
      (HMC5883L_RATE_15 << (HMC5883L_CRA_RATE_BIT - HMC5883L_CRA_RATE_LENGTH + 1)) |

  hmc5883lSetMode (HMC5883L_MODE_SINGLE);
  vTaskDelay (M2T (HMC5883L_ST_DELAY_MS));
  hmc5883lGetHeading (& mxn, & myn, & mzn);

  IF (hmc5883lEvaluateSelfTest (HMC5883L_ST_X_MIN, HMC5883L_ST_X_MAX, MXP, "X-POS") &&
      hmc5883lEvaluateSelfTest (HMC5883L_ST_Y_MIN, HMC5883L_ST_Y_MAX, MYP, "the Y POS") &&
      hmc5883lEvaluateSelfTest (HMC5883L_ST_Z_MIN, HMC5883L_ST_Z_MAX, MZP, "the Z POS") &&
      hmc5883lEvaluateSelfTest (-HMC5883L_ST_X_MAX, - HMC5883L_ST_X_MIN, MXN, "X-NEG") &&
      hmc5883lEvaluateSelfTest (-HMC5883L_ST_Y_MAX, -HMC5883L_ST_Y_MIN, MYN, "the Y NEG") &&
      hmc5883lEvaluateSelfTest (-HMC5883L_ST_Z_MAX, -HMC5883L_ST_Z_MIN, MZN, "NEG the Z"))
    DEBUG_PRINT ( "Self Test [the OK ]. \ n ");
    testStatus = FALSE;

  // Restore registers
  if (i2cdevWrite (I2Cx, devAddr, HMC5883L_RA_CONFIG_A, sizeof (regSave), (uint8_t *) & regSave) == FALSE)
    // TODO: error handling
    return FALSE;

  return testStatus;


        In addition, here is a method, if there is any research in the future, I will give a detailed description, here is a link for reference:


widgets Related Articles

widgets Contribution

This article is contributed by Anonymous and text available under CC-SA-4.0