Magnetometer Converting Heading to Degrees

Hi.

I am using your LSM9DS1 Breakout and the sample Arduino library (LSM9DS1_Basic_I2C.ino) to get the x, y, z value and the heading data from the magnetometer. However, the heading does not become zero when it is facing North. Also, the range of the heading is not from 0-360 degrees and the value does not increase when instead it is supposed to go from 0 to 90 to 180 to 270 to 360. (Please see attached file for the sample output)

Sample Code:

Edited by moderator to add code formatting.

void loop()
{
  // Update the sensor values whenever new data is available
  if ( imu.gyroAvailable() )
  {
    // To read from the gyroscope,  first call the
    // readGyro() function. When it exits, it'll update the
    // gx, gy, and gz variables with the most current data.
    imu.readGyro();
  }
  if ( imu.accelAvailable() )
  {
    // To read from the accelerometer, first call the
    // readAccel() function. When it exits, it'll update the
    // ax, ay, and az variables with the most current data.
    imu.readAccel();
  }
  if ( imu.magAvailable() )
  {
    // To read from the magnetometer, first call the
    // readMag() function. When it exits, it'll update the
    // mx, my, and mz variables with the most current data.
    imu.readMag();
  }
  
  if ((lastPrint + PRINT_SPEED) < millis())
  {
    printGyro();  // Print "G: gx, gy, gz"
    printAccel(); // Print "A: ax, ay, az"
    printMag();   // Print "M: mx, my, mz"
    // Print the heading and orientation for fun!
    // Call print attitude. The LSM9DS1's mag x and y
    // axes are opposite to the accelerometer, so my, mx are
    // substituted for each other.
    printAttitude(imu.ax, imu.ay, imu.az, 
                 -imu.my, -imu.mx, imu.mz);
    Serial.println();
    
    lastPrint = millis(); // Update lastPrint time
  }
}


void printAttitude(float ax, float ay, float az, float mx, float my, float mz)
{
  float roll = atan2(ay, az);
  float pitch = atan2(-ax, sqrt(ay * ay + az * az));
  
  float heading;
  if (my == 0)
    heading = (mx < 0) ? PI : 0;
  else
    heading = atan2(mx, my);
    
  heading -= DECLINATION * PI / 180;
  
  if (heading > PI) heading -= (2 * PI);
  else if (heading < -PI) heading += (2 * PI);
  else if (heading < 0) heading += 2 * PI;
  
  // Convert everything from radians to degrees:
  heading *= 180.0 / PI;
  pitch *= 180.0 / PI;
  roll  *= 180.0 / PI;
  
  Serial.print("Pitch, Roll: ");
  Serial.print(pitch, 2);
  Serial.print(", ");
  Serial.println(roll, 2);
  Serial.print("Heading: "); Serial.println(heading, 2);
}

I have also tried using MPU9250 Breakout and switching arctan2(mx, my) to arctan2(my, mx) since that is what other people had , but that didn’t fix the problem.

Is there something wrong with the sample formula. I have researched other people’s work, but the majority have gotten the correct heading using the same formula.

It will be great if we can get your help. Thank you very much.

LSM9DS1_Sample_Output.txt (5.53 KB)

Hi kousukemaeda12,

A quick suggestion would be to make sure you are adjusting the declination field [here since Earth’s magnetic field varies by location. If you have already adjusted that variable for your testing location, the error may be caused by some other magnetic field nearby. Do you have any motors or other items that may be creating a magnetic field nearby?](LSM9DS1_Breakout/Libraries/Arduino/examples/LSM9DS1_Basic_I2C/LSM9DS1_Basic_I2C.ino at master · sparkfun/LSM9DS1_Breakout · GitHub)

Hi TS-Mark.

Thank you for your reply.

Regarding the declination, we have set it to the value of our current location.

Also, we do not have anything that might have the possibility of creating another magnetic field around our environment…

Do you think there is something wrong with the formula and its conditions?

After digging into this a bit more, we found some discrepancies between the version of the library hosted on the [GitHub Repository and the library available for download from the Arduino Library Manager so I think something went wrong in that transition/update to the library with this formula. In my testing, using either version did not produce great heading data but that may have been due to my testing environment. We are looking into updating/fixing that formula to get more reliable results and I am going to try and test in a better place with less interference.

If you want to take a shot at adjusting the formula while we work on fixing the issue, the application note the heading formula was derived from can be viewed [here.](ArduinoMagnetometer/MagnetometerHMC5883L/datasheet/AN203_Compass_Heading_Using_Magnetometers.pdf at master · dalmirdasilva/ArduinoMagnetometer · GitHub)](GitHub - sparkfun/SparkFun_LSM9DS1_Arduino_Library: Arduino library for the LSM9DS1 9DOF IMU.)

The Issue:

From the example code, the values from the magnetometer aren’t getting scaled before being inputted into the “printAttitude()” function (see the attached picture of raw data output for x and y axes).

For reference, here is a link to the [application note mentioned in the code. You will notice that the sensor data is offset from the norm of 0, which the equations in the “printAttitude()” function are expecting (see Figure 4 in [application note ).

Solution:

Calibrate the sensor output using either the “calibrate()” or “calibrateMag()” functions (already built into library) in the “setup()” part of the sketch. This introduces a bias to offset the magnetometer values closer to the norm of 0 (you will need to rotate the sensor during the calibration process). Then use the calibrated values (like “imu.calcMag(imu.mx)”) in the “printAttitude()” function. (*Make sure to use the proper orientation of the input values.)

(*I’ll try to add an example to the library when I get a chance.)](ArduinoMagnetometer/MagnetometerHMC5883L/datasheet/AN203_Compass_Heading_Using_Magnetometers.pdf at master · dalmirdasilva/ArduinoMagnetometer · GitHub)](ArduinoMagnetometer/MagnetometerHMC5883L/datasheet/AN203_Compass_Heading_Using_Magnetometers.pdf at master · dalmirdasilva/ArduinoMagnetometer · GitHub)