Go Down

Topic: LSM303DLHC - Calibration, Pitch, Roll and Tilt Compensated Heading (Read 18543 times) previous topic - next topic


I strongly doubt that the comma in the input is doing what you think it is doing. But the effect is negligible.

Please explain the difference between the "first" and "second" calibrations.


The  "correction of combined scale factors" is 100 times larger without the comma(decimal point)

Difference from the first and second calibration is that the bias is further away from zero.

Is there a free program I can use to plot the data as a circle?


So now, you have posted three different calibrations. Yes, of course using the comma makes a difference, but a comma and a period have different meanings and effects.

What is the difference between the "first calibration" and the "second calibration"? The input parameter is identical but the results are not.


I'm based in Norway and we use comma as fraction separator. Sorry but forgot to mention this in the fir post.

First calibration is the raw data from LSM303D.

Code: [Select]
Xm_print = compass.m.x*0.080;//*(10000.0/1100.0); // Gain X [LSB/Gauss] for selected sensor input field range (1.3 in these case)
Ym_print = compass.m.y*0.080;//(10000.0/1100.0); // Gain Y [LSB/Gauss] for selected sensor input field range
Zm_print = compass.m.z*0.080;//(10000.0/980.0 );  // Gain Z [LSB/Gauss] for selected sensor input field range

I then use the data to create bias and scale factor with Magento and paste them in the code under.

Code: [Select]
Xm_off = compass.m.x*0.080 -35.250093; //X-axis combined bias (Non calibrated data - bias)
Ym_off = compass.m.y*0.080 -138.764237; //Y-axis combined bias (Default: substracting bias)
Zm_off = compass.m.z*0.080 + 164.007726; //Z-axis combined bias

Xm_cal =  1.020574*Xm_off -0.083683*Ym_off + 0.009699*Zm_off; //X-axis correction for combined scale factors (Default: positive factors)
Ym_cal =  -0.083683*Xm_off + 1.029068*Ym_off +0.027500*Zm_off; //Y-axis correction for combined scale factors
Zm_cal =  0.009699*Xm_off + 0.027500*Ym_off + 0.980459*Zm_off; //Z-axis correction for combined scale factors

The data generated with this code is then put into Magneto again and then the bias numbers should be closer to zero according the the first post in this thread.


I'm based in Norway and we use comma as fraction separator.
The program does not expect the comma to be used as a fraction separator. You MUST check, you cannot assume.

It still is not clear how you are collecting and handling the data. Please describe the operation used to collect the data, and how many points you collect. Are all possible 3D orientations reasonably well represented?

Right now, it is not important to have the data on an absolute scale, so rerun both operations, leaving out the factor of 0.08.

If you like, attach the data to your post and I will run my version of that program.

You can use Scilab (free) to process and plot the data points.


Yes, please try my file in your program.
I've attached the raw magnetometer file.

Data is obtained by moving the compass slowly in all orientations.

With this code.
Code: [Select]

#include <Wire.h>
#include <LSM303.h>
LSM303 compass;

void setup()

void loop()
int Xm_print, Ym_print, Zm_print;

Xm_print = compass.m.x;//*(10000.0/1100.0); // Gain X [LSB/Gauss] for selected sensor input field range (1.3 in these case)
Ym_print = compass.m.y;//(10000.0/1100.0); // Gain Y [LSB/Gauss] for selected sensor input field range
Zm_print = compass.m.z;//(10000.0/980.0 );  // Gain Z [LSB/Gauss] for selected sensor input field range

Serial.print(Xm_print); Serial.print(" "); Serial.print(Ym_print); Serial.print(" "); Serial.println(Zm_print);

Plot of raw data from .txt file.


I used my own slightly modified version of Magneto 1.2. You can download the C code here: https://forum.pololu.com/t/correcting-the-balboa-magnetometer/14315

The raw data that you supplied (after removing a bad character "J" from an X coordinate) were corrected by magneto using the following transformation:

Code: [Select]

Magneto 1.2
Input .csv file? magRaw.csv

Average magnitude of 1917 vectors (default Hm) = 7232.985931

Expected norm of local field vector Hm? (0 for default above) 512.

Combined bias vector B:
 -270.24 -1958.74  1821.14

Correction matrix Ainv, using Hm=512.000000:
  0.08189  -0.00671   0.00174
 -0.00671   0.07938   0.00424
  0.00174   0.00424   0.07808

Where Hcalc = Ainv*(H-B)

Code initialization statements...

 float B { -270.24,-1958.74, 1821.14};

 float Ainv {{  0.08189, -0.00671,  0.00174},
             { -0.00671,  0.07938,  0.00424},
             {  0.00174,  0.00424,  0.07808}};
 output filename for corrected values magRaw_corr.csv

RMS corrected vector length 512.000000

The resultant 3D scatterplot for the corrected data looks like this. It is perfectly spherical and well centered.

The raw data and the corrected raw data are attached in a zip file.

Arduino program showing how to apply the correction to new magnetometer readings:

Code: [Select]

// last update 3/4/2018
   magnetic compass program for Balboa by S. J. Remington
   This program assumes that a LIS3MDL Balboa is horizontal, with X pointing toward
   magnetic North for zero degrees heading
#include <Wire.h>
#include <LIS3MDL.h>

typedef struct vector
  float x, y, z;
} vector;

LIS3MDL compass;

// Code initialization statements from magneto program, compass correction bias and rotation matrix
// scaled and rotated vectors will have norm ~ 512 based on the example data.

 float B { -270.24,-1958.74, 1821.14};

 float Ainv {{  0.08189, -0.00671,  0.00174},
             { -0.00671,  0.07938,  0.00424},
             {  0.00174,  0.00424,  0.07808}};

void setup()
  if (!compass.init())
    Serial.println("Failed to detect and initialize magnetometer!");
    while (1);

// Returns a set of distortion-corrected magnetic readings from the LIS3MDL

void read_data(vector * m)
  static float x, y, z;
  x = (float) compass.m.x - B[0];
  y = (float) compass.m.y - B[1];
  z = (float) compass.m.z - B[2];
  m->x = Ainv[0][0] * x + Ainv[0][1] * y + Ainv[0][2] * z;
  m->y = Ainv[1][0] * x + Ainv[1][1] * y + Ainv[1][2] * z;
  m->z = Ainv[2][0] * x + Ainv[2][1] * y + Ainv[2][2] * z;

void loop()
  vector m;
  int heading;
//   for the curious...
  Serial.print(", ");
  Serial.print(", ");

// assumes Balboa horizontal

  Serial.print("Heading: ");
  heading = 180.*atan2(m.y, m.x) / PI;
  if (heading < 0) heading = heading + 360;

Go Up