cancel
Showing results for 
Search instead for 
Did you mean: 

Help with calculating heading with LSM303

PMerc.1
Associate II

Hi. I am evaluating the LSM303 and trying to get the device working as an Compass. I have the AN3192 application note to assist with calculating the heading, but doesn't seem to give me the correct results. The raw data coming back when facing north is x = -44, y = +1, z = -363. When facing East I get x = +45, y = +122, z = -300. With these figures I can't seem to achieve 0 degrees and 90. Please can you point me in the right direction.

10 REPLIES 10
PMerc.1
Associate II

I have tried code supplied for the Arduino by Github and again am getting strange results. When I use their 'compass' project the heading is ranging from approximately 140-225 degrees

Eleon BORLINI
ST Employee

Hi @PMerc.1​ ,

please note that an important part of the eCompass algorithm running on LSM303 is related to the magnetometer compensation (hard and soft iron).

You can try to find some methodological hints in this MorionEC eCompass guide (real-time E-Compass library) in X-CUBE-MEMS1 expansion for STM32Cube.

I see also that there is an example for LSM303 in arduino frame on Github. Can you please check this procedure with your code, if there is something big missing?

It may be useful if you can share the code implementation of your algorithm.

-Eleon

PMerc.1
Associate II

Thanks for the feedback.

Here is the Arduino code I tried:-

#include <Wire.h>

#include <Adafruit_Sensor.h>

#include <Adafruit_LSM303AGR_Mag.h>

Adafruit_LSM303AGR_Mag_Unified mag = Adafruit_LSM303AGR_Mag_Unified(12345);

void setup(void) 

{

 Serial.begin(115200);

 Serial.println("Magnetometer Test"); Serial.println("");

  

 /* Initialise the sensor */

 if(!mag.begin())

 {

  /* There was a problem detecting the LSM303 ... check your connections */

  Serial.println("Ooops, no LSM303 detected ... Check your wiring!");

  while(1);

 }

}

void loop(void) 

{

 /* Get a new sensor event */ 

 sensors_event_t event; 

 mag.getEvent(&event);

  

 float Pi = 3.14159;

  

 // Calculate the angle of the vector y,x

 float heading = (atan2(event.magnetic.y,event.magnetic.x) * 180) / Pi;

  

 // Normalize to 0-360

 if (heading < 0)

 {

  heading = 360 + heading;

 }

 Serial.print("Compass Heading: ");

 Serial.println(heading);

 delay(500);

}

This gives the heading of 140-225 when running on an Arduino

PMerc.1
Associate II

I have read back the offset registers 0x45-0x4a and the offset is 0 - is this correct?

Hi @PMerc.1​ ,

but are you using a LSM303DLHC or a LSM303AGR eCompass device?

In general, before calculating the heading you should run the hard iron compensation (at least at software level, not necessary at embedded register level).

Hard iron compensation calculation (source hardironcalibration.ino:(

    float hardiron_x = (MagMaxX + MagMinX) / 2;
    float hardiron_y = (MagMaxY + MagMinY) / 2;
    float hardiron_z = (MagMaxZ + MagMinZ) / 2;

Heading algorithm (source orientation.cpp:(

  // Signs choosen so that, when axis is down, the value is + 1g
  float accl_x = -event_accl.acceleration.x;
  float accl_y = event_accl.acceleration.y;
  float accl_z = event_accl.acceleration.z;
 
  // Signs should be choosen so that, when the axis is down, the value is + positive.
  // But that doesn't seem to work ?...
  float magn_x = event_magn.magnetic.x - hardiron_x;
  float magn_y = -event_magn.magnetic.y - hardiron_y;
  float magn_z = -event_magn.magnetic.z - hardiron_z;  
  
  // Freescale solution
  roll = atan2(accl_y, accl_z);
  pitch = atan(-accl_x / (accl_y * sin(roll) + accl_z * cos(roll)));
  
  float magn_fy_fs = magn_z * sin(roll) - magn_y*cos(roll);
  float magn_fx_fs = magn_x * cos(pitch) + magn_y * sin(pitch) * sin(roll) + magn_z * sin(pitch) * cos(roll);
  
  yaw = atan2(magn_fy_fs, magn_fx_fs);
  
  roll = roll * RAD_CONV;
  pitch = pitch * RAD_CONV;
  yaw = yaw * RAD_CONV;
  
  heading = yawToHeading(yaw)

-Eleon

PMerc.1
Associate II

I am using the LSM303AGR.

PMerc.1
Associate II

OK so to make sure I understand you correctly:-

The hardiron_axis is taken by the (MAX_axis + Min_axis)/2.

Does this change or is it reasonable to assume that once the values have been found they will remain?

If this changes is it reasonable to store the previous stored value and adjust if required?

Taking real values from the device I get on the x axis:-

Max +180 and Min -60 i.e. hardiron_axis = (180 + -60)/2 = +60

Therefore the reading of raw 180 is actually 120 when taking the hardiron value off and the raw value of -60 is actually -120?

Thanks for your assistance.

Eleon BORLINI
ST Employee

Hi @PMerc.1​ ,

>> Does this change or is it reasonable to assume that once the values have been found they will remain?

Hard-iron distortion occurs when a magnetic object is placed near the magnetometer and appears as a permanent bias in the sensor's outputs. The hard-iron correction consists of compensating magnetic data from hard-iron distortion. Please note that the computation of the hard-iron distortion field should be performed by an external processor. After the computation of the hard iron-distortion field has been performed, the measured magnetic data can be compensated.

>> Therefore the reading of raw 180 is actually 120 when taking the hardiron value off and the raw value of -60 is actually -120?

Yes you are right. The "effective" magnetic field H_out can be calculated by the formula:

H_out = H_read - H_HI

Moreover, did you try the Magnetometer offset cancellation procedure described in the LSM303AGR datasheet at p.23?

-Eleon

PMerc.1
Associate II

I have purchased a LSM303DLH, and that works as expected. What are the main differences between the AGR and DLH other than the package?