LSM303DLHC Heading never correct

Breakout boards, sensors, other Adafruit kits, etc.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
jeff11111
 
Posts: 5
Joined: Thu Dec 06, 2012 2:55 pm

LSM303DLHC Heading never correct

Post by jeff11111 »

Hi,

Just got around to working with my LSM303DLHC and am having issues getting a proper reading from the magnetometer. I am found this post :

http://forums.adafruit.com/viewtopic.ph ... 0&hilit=ls

and got the python code from here:

https://bitbucket.org/shigeru_kawaguchi ... 03_for_rpi

The code runs fine unmodified and finds the device and prints the output example listed below. The problem is the heading is never accurate. While keeping the LSM on the breadboard flat on the desk and slowly rotating 360 deg at most I get a 70 degree change. I also implemented code in C++ to read from the mag register and calculate using arctan2 and keeping it flat to avoid tilt adjusting(which I also tried some code for that too). Examples c code below: However the heading when the board is rotated 90deg changes from about -20 to -60. I cant figure it out can anyone suggest some tips to try next?

Thanks
Jeff

void IMU::readMAG()
{

i2c->selectDevive(MAG_ADDRESS);

blockm[0] = i2c->read_byte(0x80 | LSM303_OUT_X_H_M);
blockm[1] = i2c->read_byte(0x80 | LSM303_OUT_X_L_M);
blockm[2] = i2c->read_byte(0x80 | LSM303DLHC_OUT_Y_H_M);
blockm[3] = i2c->read_byte(0x80 | LSM303DLHC_OUT_Y_L_M);
blockm[4] = i2c->read_byte(0x80 | LSM303DLHC_OUT_Z_H_M);
blockm[5] = i2c->read_byte(0x80 | LSM303DLHC_OUT_Z_L_M);

mag_raw[0] = (int16_t) (blockm[0] << 8 | blockm[1]);
mag_raw[1] = (int16_t) (blockm[2] << 8 | blockm[3]);
mag_raw[2] = (int16_t) (blockm[4] << 8 | blockm[5]);

std::cout << "mag x,y,z: " << mag_raw[0] << "," << mag_raw[1] << ","
<< mag_raw[2] << "";


float heading = 180 * atan2(mag_raw[1], mag_raw[0]) / M_PI; // assume pitch, roll are 0
std::cout << "heading: " << heading << "";
if (heading < 0)
heading += 360;

python output

Timestamp: 2013-08-02T17:29:02.127233
Accel X: -0.116 G, Y: -0.016 G, Z: 1.024 G
Mag X: 0.164 gauss, Y: -0.343 gauss, Z: -0.291 gauss
Temp: 18.750 C
Heading: -64.461
I2C: Device 0x19 returned 0x40 from reg 0x28
DBG: accel X lo: 0x0040 (64)
I2C: Device 0x19 returned 0xF9 from reg 0x29
DBG: accel X hi: 0x00F9 (249)
I2C: Device 0x19 returned 0x80 from reg 0x2A
DBG: accel Y lo: 0x0080 (128)
I2C: Device 0x19 returned 0xFF from reg 0x2B
DBG: accel Y hi: 0x00FF (255)
I2C: Device 0x19 returned 0x40 from reg 0x2C
DBG: accel Z lo: 0x0040 (64)
I2C: Device 0x19 returned 0x40 from reg 0x2D
DBG: accel Z hi: 0x0040 (64)
I2C: Device 0x1E returned 0x8C from reg 0x04
debug: True
DBG: mag X lo: 0x008C (140)
I2C: Device 0x1E returned 0x00 from reg 0x03
DBG: mag X hi: 0x0000 (0)
I2C: Device 0x1E returned 0xDB from reg 0x08
DBG: mag Y lo: 0x00DB (219)
I2C: Device 0x1E returned 0xFE from reg 0x07
DBG: mag Y hi: 0x00FE (254)
I2C: Device 0x1E returned 0x07 from reg 0x06
DBG: mag Z lo: 0x0007 (7)
I2C: Device 0x1E returned 0xFF from reg 0x05
DBG: mag Z hi: 0x00FF (255)
I2C: Device 0x1E returned 0x60 from reg 0x32
DBG: Temp lo: 0x0060 (96)
I2C: Device 0x1E returned 0x00 from reg 0x31
DBG: Temp hi: 0x0000 (0)
I2C: Device 0x1E returned 0x8C from reg 0x04
debug: True
DBG: mag X lo: 0x008C (140)
I2C: Device 0x1E returned 0x00 from reg 0x03
DBG: mag X hi: 0x0000 (0)
I2C: Device 0x1E returned 0xDB from reg 0x08
DBG: mag Y lo: 0x00DB (219)
I2C: Device 0x1E returned 0xFE from reg 0x07
DBG: mag Y hi: 0x00FE (254)
I2C: Device 0x1E returned 0x07 from reg 0x06
DBG: mag Z lo: 0x0007 (7)
I2C: Device 0x1E returned 0xFF from reg 0x05
DBG: mag Z hi: 0x00FF (255)
Timestamp: 2013-08-02T17:29:02.419883
Accel X: -0.108 G, Y: -0.008 G, Z: 1.028 G
Mag X: 0.164 gauss, Y: -0.343 gauss, Z: -0.291 gauss
Temp: 18.750 C
Heading: -64.461
I2C: Device 0x19 returned 0x80 from reg 0x28
DBG: accel X lo: 0x0080 (128)
I2C: Device 0x19 returned 0xF8 from reg 0x29
DBG: accel X hi: 0x00F8 (248)
I2C: Device 0x19 returned 0x80 from reg 0x2A
DBG: accel Y lo: 0x0080 (128)
I2C: Device 0x19 returned 0xFF from reg 0x2B
DBG: accel Y hi: 0x00FF (255)
I2C: Device 0x19 returned 0x00 from reg 0x2C
DBG: accel Z lo: 0x0000 (0)
I2C: Device 0x19 returned 0x40 from reg 0x2D
DBG: accel Z hi: 0x0040 (64)
I2C: Device 0x1E returned 0x8C from reg 0x04
debug: True
DBG: mag X lo: 0x008C (140)
I2C: Device 0x1E returned 0x00 from reg 0x03
DBG: mag X hi: 0x0000 (0)
I2C: Device 0x1E returned 0xDB from reg 0x08
DBG: mag Y lo: 0x00DB (219)
I2C: Device 0x1E returned 0xFE from reg 0x07
DBG: mag Y hi: 0x00FE (254)
I2C: Device 0x1E returned 0x05 from reg 0x06
DBG: mag Z lo: 0x0005 (5)
I2C: Device 0x1E returned 0xFF from reg 0x05
DBG: mag Z hi: 0x00FF (255)
I2C: Device 0x1E returned 0x60 from reg 0x32
DBG: Temp lo: 0x0060 (96)
I2C: Device 0x1E returned 0x00 from reg 0x31
DBG: Temp hi: 0x0000 (0)
I2C: Device 0x1E returned 0x8C from reg 0x04
debug: True
DBG: mag X lo: 0x008C (140)
I2C: Device 0x1E returned 0x00 from reg 0x03
DBG: mag X hi: 0x0000 (0)
I2C: Device 0x1E returned 0xDB from reg 0x08
DBG: mag Y lo: 0x00DB (219)
I2C: Device 0x1E returned 0xFE from reg 0x07
DBG: mag Y hi: 0x00FE (254)
I2C: Device 0x1E returned 0x05 from reg 0x06
DBG: mag Z lo: 0x0005 (5)
I2C: Device 0x1E returned 0xFF from reg 0x05
DBG: mag Z hi: 0x00FF (255)
Timestamp: 2013-08-02T17:29:02.715707
Accel X: -0.120 G, Y: -0.008 G, Z: 1.024 G
Mag X: 0.164 gauss, Y: -0.343 gauss, Z: -0.294 gauss
Temp: 18.750 C
Heading: -64.461
I2C: Device 0x19 returned 0x40 from reg 0x28
DBG: accel X lo: 0x0040 (64)
I2C: Device 0x19 returned 0xF9 from reg 0x29
DBG: accel X hi: 0x00F9 (249)
I2C: Device 0x19 returned 0x40 from reg 0x2A
DBG: accel Y lo: 0x0040 (64)
I2C: Device 0x19 returned 0xFF from reg 0x2B
DBG: accel Y hi: 0x00FF (255)
I2C: Device 0x19 returned 0x40 from reg 0x2C
DBG: accel Z lo: 0x0040 (64)
I2C: Device 0x19 returned 0x41 from reg 0x2D
DBG: accel Z hi: 0x0041 (65)
I2C: Device 0x1E returned 0x8D from reg 0x04
debug: True
DBG: mag X lo: 0x008D (141)
I2C: Device 0x1E returned 0x00 from reg 0x03
DBG: mag X hi: 0x0000 (0)
I2C: Device 0x1E returned 0xDA from reg 0x08
DBG: mag Y lo: 0x00DA (218)
I2C: Device 0x1E returned 0xFE from reg 0x07
DBG: mag Y hi: 0x00FE (254)
I2C: Device 0x1E returned 0x06 from reg 0x06
DBG: mag Z lo: 0x0006 (6)
I2C: Device 0x1E returned 0xFF from reg 0x05
DBG: mag Z hi: 0x00FF (255)
I2C: Device 0x1E returned 0x60 from reg 0x32
DBG: Temp lo: 0x0060 (96)
I2C: Device 0x1E returned 0x00 from reg 0x31
DBG: Temp hi: 0x0000 (0)
I2C: Device 0x1E returned 0x8D from reg 0x04
debug: True
DBG: mag X lo: 0x008D (141)
I2C: Device 0x1E returned 0x00 from reg 0x03
DBG: mag X hi: 0x0000 (0)
I2C: Device 0x1E returned 0xDA from reg 0x08
DBG: mag Y lo: 0x00DA (218)
I2C: Device 0x1E returned 0xFE from reg 0x07
DBG: mag Y hi: 0x00FE (254)
I2C: Device 0x1E returned 0x06 from reg 0x06
DBG: mag Z lo: 0x0006 (6)
I2C: Device 0x1E returned 0xFF from reg 0x05
DBG: mag Z hi: 0x00FF (255)
Timestamp: 2013-08-02T17:29:03.014059
Accel X: -0.108 G, Y: -0.012 G, Z: 1.044 G
Mag X: 0.165 gauss, Y: -0.344 gauss, Z: -0.292 gauss
Temp: 18.750 C
Heading: -64.378

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: LSM303DLHC Heading never correct

Post by adafruit_support_bill »

The calculation looks right. What are the readings scaled to micro-Teslas? They should be in the range of +/- 60 micro-Teslas.

waltr
 
Posts: 306
Joined: Wed Jun 12, 2013 5:01 pm

Re: LSM303DLHC Heading never correct

Post by waltr »

I have found offset errors for that magnetometer that must be calibrated out to get a good heading.
To get the offset errors:
record the maximum and minimum value on each axis.
Add the min and max (will be zero if the offset error is zero) of each axis.
divide by two.
subtract this from the axis measurement to correct for the offset error.
Now use the corrected measurement in the heading equation.

Secondly: beware of any nearby magnetic fields either from electrical or iron material as these will cause a different error.

jeff11111
 
Posts: 5
Joined: Thu Dec 06, 2012 2:55 pm

Re: LSM303DLHC Heading never correct

Post by jeff11111 »

I converted to micro tesla (I think) with the following code pulling the conversion from an adafruit lib. I ran a sample every 10ms so the list below is an extract of some of the values when they change. Does it look like I converted correctly?

#define SENSORS_GAUSS_TO_MICROTESLA (100) /**< Gauss to micro-Tesla multiplier */

void LSM303DLHC::readMAG()
{
i2c->selectDevive(MAG_ADDRESS);

blockmag[0] = i2c->read_byte(0x80 | LSM303_OUT_X_H_M);
blockmag[1] = i2c->read_byte(0x80 | LSM303_OUT_X_L_M);
blockmag[2] = i2c->read_byte(0x80 | LSM303DLHC_OUT_Y_H_M);
blockmag[3] = i2c->read_byte(0x80 | LSM303DLHC_OUT_Y_L_M);
blockmag[4] = i2c->read_byte(0x80 | LSM303DLHC_OUT_Z_H_M);
blockmag[5] = i2c->read_byte(0x80 | LSM303DLHC_OUT_Z_L_M);

mag_raw[0] = (int16_t) (blockmag[0] << 8 | blockmag[1]);
mag_raw[1] = (int16_t) (blockmag[2] << 8 | blockmag[3]);
mag_raw[2] = (int16_t) (blockmag[4] << 8 | blockmag[5]);

float _lsm303Accel_MG_LSB = 0.001F; // 1, 2, 4 or 12 mg per lsb
float _lsm303Mag_Gauss_LSB_XY = 1100.0F; // Varies with gain
float _lsm303Mag_Gauss_LSB_Z = 980.0F; // Varies with gain

float x = mag_raw[0] / _lsm303Mag_Gauss_LSB_XY * SENSORS_GAUSS_TO_MICROTESLA;
float y = mag_raw[1] / _lsm303Mag_Gauss_LSB_XY * SENSORS_GAUSS_TO_MICROTESLA;
float z = mag_raw[2] / _lsm303Mag_Gauss_LSB_Z * SENSORS_GAUSS_TO_MICROTESLA;

std::cout << "tesla x,y,z: " << x << "," << y << "," << z << "\n";
}

tesla x,y,z: 20.3636,-5.09091,-26.6327
tesla x,y,z: 20.7273,-5.09091,-26.3265
tesla x,y,z: 20.5455,-5.09091,-26.5306
tesla x,y,z: 20.2727,-4.90909,-26.4286
tesla x,y,z: 20.6364,-5.18182,-26.4286
tesla x,y,z: 20.4545,-4.90909,-26.3265
tesla x,y,z: 20.7273,-5.18182,-26.4286
tesla x,y,z: 20.7273,-5.18182,-26.4286
tesla x,y,z: 20.6364,-5,-26.2245
tesla x,y,z: 20.4545,-5,-26.5306
tesla x,y,z: 20.4545,-5.18182,-26.4286
tesla x,y,z: 20.8182,-5.09091,-26.4286
tesla x,y,z: 20.6364,-5.18182,-26.7347
tesla x,y,z: 20.7273,-5,-26.5306
tesla x,y,z: 20.7273,-5.09091,-26.4286
tesla x,y,z: 20.5455,-5,-26.3265
tesla x,y,z: 16.0909,-1.09091,-26.3265
tesla x,y,z: 12.5455,0.636364,-26.4286
tesla x,y,z: 12,0.727273,-26.3265

jeff11111
 
Posts: 5
Joined: Thu Dec 06, 2012 2:55 pm

Re: LSM303DLHC Heading never correct

Post by jeff11111 »

Forgot to note the last post readings are not the same as teh extract in the first post from the python script.

jeff11111
 
Posts: 5
Joined: Thu Dec 06, 2012 2:55 pm

Re: LSM303DLHC Heading never correct

Post by jeff11111 »

I think I understand the calibration and did the following thems values I used as a muliplier to scale the raw values in mag_raw. I got the max and min values by checking each row while i rotate the lsm303 in all axis. The heading looks a bit better and sometimes is what i think it should be but then i rotate it flat 90 degrees and the heading changes from 85 to 71. I have a laptop on my desk about 16 inches away from it but that is it. I dont have any ohter magnetic devices on the desk.

float m_max_x = 384;
float m_max_y = 253;
float m_max_z = 372;
float m_min_x = -282;
float m_min_y = -431;
float m_min_z = -276;

// shift and scale
float msx = (mag_raw[0] - m_min_x) / (m_max_x - m_min_x) * 2 - 1.0;
float msy = (mag_raw[1] - m_min_y) / (m_max_y - m_min_y) * 2 - 1.0;
float msz = (mag_raw[2] - m_min_z) / (m_max_z - m_min_z) * 2 - 1.0;

float heading2 = 180 * atan2((mag_raw[1]*msy),(mag_raw[0]*msx)) / M_PI; // assume pitch, roll are 0
//std::cout << "heading: " << heading << "";
if (heading2 < 0)
heading2 += 360;

waltr
 
Posts: 306
Joined: Wed Jun 12, 2013 5:01 pm

Re: LSM303DLHC Heading never correct

Post by waltr »

You don't quite have the corrections right. There is also no reason to convert the raw values to any other unit (scaling) to calculate a heading since the atan function takes a ratio of the two input values.
Try this:
x_error = (m_max_x + m_min_x )/2;
y_error = (m_max_y + m_min_y)/2;

real_x = raw_x - x_error;
real_y = raw_y - y_error;

PI = 3.1416;
heading = atan2( real_y, real_x) *180/PI; // heading in degrees, assuming magnetometer is level

If you can capture the raw values as you slowly rotate the magnetometer (or write code to plot) then plot the y verse x values on a graph. These should give you a circle that is offset (error) from the graph origin.
If the circle is oval then there is a magnetic field near by. Try again at a different place to see if the offset errors change.

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: LSM303DLHC Heading never correct

Post by adafruit_support_bill »

Your micro-tesla readings look ok. As waltr says, it is not necesssary to scale before calculating the heading (in fact the extra manipulation can only add error). But it is good to convert to some standard units just to verify that the sensor & driver protocols are working properly.

The instability around the 90 degree mark is likely a calibration issue.

jeff11111
 
Posts: 5
Joined: Thu Dec 06, 2012 2:55 pm

Re: LSM303DLHC Heading never correct

Post by jeff11111 »

Thanks Walt that was it. After calibrating then using your lines of code I can rotate around my desk and get from 0 to 180 and -180 to 0 and it matches up to the compass I have in my hand. Thanks so much.

waltr
 
Posts: 306
Joined: Wed Jun 12, 2013 5:01 pm

Re: LSM303DLHC Heading never correct

Post by waltr »

Ok. Isn't it great when it finally works. ;)

User avatar
nickseifert
 
Posts: 12
Joined: Sat Mar 09, 2013 11:57 am

Re: LSM303DLHC Heading never correct

Post by nickseifert »

I used some code someone else posted here, combined with Adafruit's tutorial for the LSM303 breakout (http://www.adafruit.com/products/1120) to normalizes the mag readings from (-)1 to (+)1. This can then be operated on to find heading.

For my application the Z and X axis are in the horizontal plane, so i've used

Code: Select all

float heading = (atan2(XN,ZN) * 180) / Pi;
but you'll want to change this depending on how your sensor is oriented.


also, this section:

Code: Select all

float XMin = -73;
float XMax = 48;
float YMin = -62;
float YMax = 57;
float ZMin = -12;
float ZMax = 104;
will need to be modified to give your min/max values. These can be output via serial by uncommenting this section of the sketch.


OK, here it is:

Code: Select all

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_LSM303.h>

/* Assign a unique ID to these sensors */
Adafruit_LSM303_Mag mag = Adafruit_LSM303_Mag(12345);

float MagMinX, MagMaxX;
float MagMinY, MagMaxY;
float MagMinZ, MagMaxZ;

// Maximum and minimum accelrations recorded by the LSM303. These values can be read off from the serial monitor using this sketch 
float XMin = -73;
float XMax = 48;
float YMin = -62;
float YMax = 57;
float ZMin = -12;
float ZMax = 104;

// These are parameters that will be used to normalize meaured accelerations
float XMean = (XMax + XMin)/2;
float YMean = (YMax + YMin)/2;
float ZMean = (ZMax + ZMin)/2;
float XLeng = (XMax - XMin)/2;
float YLeng = (YMax - YMin)/2;
float ZLeng = (ZMax - ZMin)/2;

long lastDisplayTime;

void setup(void) 
{
  Serial.begin(9600);
  Serial.println("LSM303 Calibration"); Serial.println("");
  
  /* Initialise the magnetometer */
  if(!mag.begin())
  {
    /* There was a problem detecting the LSM303 ... check your connections */
    Serial.println("Ooops, no LSM303 detected ... Check your wiring!");
    while(1);
  }
  lastDisplayTime = millis();
}

void loop(void) 
{
  /* Get a new sensor event */ 
  sensors_event_t magEvent; 
  
  mag.getEvent(&magEvent);

  if (magEvent.magnetic.x < MagMinX) MagMinX = magEvent.magnetic.x;
  if (magEvent.magnetic.x > MagMaxX) MagMaxX = magEvent.magnetic.x;
  
  if (magEvent.magnetic.y < MagMinY) MagMinY = magEvent.magnetic.y;
  if (magEvent.magnetic.y > MagMaxY) MagMaxY = magEvent.magnetic.y;

  if (magEvent.magnetic.z < MagMinZ) MagMinZ = magEvent.magnetic.z;
  if (magEvent.magnetic.z > MagMaxZ) MagMaxZ = magEvent.magnetic.z;

   if ((millis() - lastDisplayTime) > 1000)  // display once/second
  {
  /* // uncomment this section to get the calibration values. Be sure to rotate through every possible orientation slowly to get the min/max values  
  Serial.print("Mag Minimums: "); Serial.print(MagMinX); Serial.print("  ");Serial.print(MagMinY); Serial.print("  "); Serial.print(MagMinZ); Serial.println();
  Serial.print("Mag Maximums: "); Serial.print(MagMaxX); Serial.print("  ");Serial.print(MagMaxY); Serial.print("  "); Serial.print(MagMaxZ); Serial.println(); Serial.println();
  */
  float XN = (magEvent.magnetic.x - XMean)/XLeng; // normalized x-component of acceleration
  float YN = (magEvent.magnetic.y - YMean)/YLeng; // normalized y-component of acceleration
  float ZN = (magEvent.magnetic.z - ZMean)/ZLeng; // normalized z-component of acceleration
  float AN = sqrt(pow(XN,2)+pow(YN,2)+pow(ZN,2));  // normalized magnitude of acceleration vector, should be close to 1 if chip is stationary
  
  // The AN should be close to 1, and the other components should not exceed 1
  Serial.print(XN);Serial.print(" ");Serial.print(YN); Serial.print(" ");Serial.print(ZN);Serial.print(" ");Serial.print(AN);
  Serial.print('\n');
  
  float Pi = 3.14159;
  
  // Calculate the angle of the vector y,x
  float heading = (atan2(XN,ZN) * 180) / Pi;
  
  // Normalize to 0-360
  if (heading < 0)
  {
    heading = 360 + heading;
  }
  Serial.print("Compass Heading: ");
  Serial.println(heading);
    
  lastDisplayTime = millis();
  /*
  Serial.println(magEvent.magnetic.x);
  Serial.println(magEvent.magnetic.y);
  Serial.println(magEvent.magnetic.z);
  */
  }
  
}

The last thing worth knowing is that the heading output has not been rotated to match north == 0 or anything like that. You'd need to calibrate this rotation for your application.

Locked
Please be positive and constructive with your questions and comments.

Return to “Other Products from Adafruit”