LSM303dhlc compass - accel

After a few weeks of off and on messing with this, The compass part works, I think.

I was moving it on the counter top. I think there are nails, screws, whatever that is messing with readings. I bring in a cardboard box and use that for the flat surface and I'm getting readings that are a complete 360 degrees.

If I hold it above the box and rotate it, the results aren't as good which leads me to believe that I need to implement the tilt compensation.

If anyone one can point me to how to do this it would be greatly appreciated. I'm not finding anything with searches.

Here's the code I'm using at this time.

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

/* Assign a unique ID to this sensor at the same time */
Adafruit_LSM303_Mag_Unified mag = Adafruit_LSM303_Mag_Unified(12345);

void setup(void) 
{
  Serial.begin(9600);
  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);
}

Thanks in advance!

All magnetometers need to be calibrated to be useful, in the environment where they will be most used.

Great overview on calibration here. For a practical application to the LIS3MDL compass, see this forum post.

I've gone thru the process and I was not successful!

I did not change any of the + or - signs in the code. Could that be what is causing my problems?

example:

combined bias = 4773.840021

in the code I left the minus sign (-4773.840021)

Thanks

What process?
What code?

The process is the calibration as described. The code is:

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

LSM303 compass;

const float alpha = 0.15;
float fXa = 0;
float fYa = 0;
float fZa = 0;
float fXm = 0;
float fYm = 0;
float fZm = 0;

void setup() {
Serial.begin(9600);
Wire.begin();
compass.init();
compass.enableDefault();
}

void loop()
{
compass.read();
float pitch, pitch_print, roll, roll_print, Heading, Xa_off, Ya_off, Za_off, Xa_cal, Ya_cal, Za_cal, Xm_off, Ym_off, Zm_off, Xm_cal, Ym_cal, Zm_cal, fXm_comp, fYm_comp;

// Accelerometer calibration
Xa_off = compass.a.x/16.0 + 30.733764; //X-axis combined bias (Non calibrated data - bias)
Ya_off = compass.a.y/16.0 - 52.817570; //Y-axis combined bias (Default: substracting bias)
Za_off = compass.a.z/16.0 + 206.131906; //Z-axis combined bias

Xa_cal =  0.986992*Xa_off + 0.001993*Ya_off - 0.004377*Za_off; //X-axis correction for combined scale factors (Default: positive factors)
Ya_cal =  0.025189*Xa_off + 0.998259*Ya_off - 0.000417*Za_off; //Y-axis correction for combined scale factors
Za_cal = -0.010287*Xa_off - 0.000417*Ya_off + 0.942771*Za_off; //Z-axis correction for combined scale factors

// Magnetometer calibration
Xm_off = compass.m.x*(100000.0/1100.0) - 4773.840021; //X-axis combined bias (Non calibrated data - bias)
Ym_off = compass.m.y*(100000.0/1100.0) - 19460.077738; //Y-axis combined bias (Default: substracting bias)
Zm_off = compass.m.z*(100000.0/980.0 ) + 31.402673; //Z-axis combined bias

Xm_cal =  0.000982*Xm_off + 0.006185*Ym_off + 0.015063*Zm_off; //X-axis correction for combined scale factors (Default: positive factors)
Ym_cal =  0.000039*Xm_off + 0.950124*Ym_off + 0.003084*Zm_off; //Y-axis correction for combined scale factors
Zm_cal =  0.000005*Xm_off + 0.003084*Ym_off + 0.880435*Zm_off; //Z-axis correction for combined scale factors

// Low-Pass filter accelerometer
fXa = Xa_cal * alpha + (fXa * (1.0 - alpha));
fYa = Ya_cal * alpha + (fYa * (1.0 - alpha));
fZa = Za_cal * alpha + (fZa * (1.0 - alpha));

// Low-Pass filter magnetometer
fXm = Xm_cal * alpha + (fXm * (1.0 - alpha));
fYm = Ym_cal * alpha + (fYm * (1.0 - alpha));
fZm = Zm_cal * alpha + (fZm * (1.0 - alpha));

// Pitch and roll
roll  = atan2(fYa, sqrt(fXa*fXa + fZa*fZa));
pitch = atan2(fXa, sqrt(fYa*fYa + fZa*fZa));
roll_print = roll*180.0/M_PI;
pitch_print = pitch*180.0/M_PI;

// Tilt compensated magnetic sensor measurements
fXm_comp = fXm*cos(pitch)+fZm*sin(pitch);
fYm_comp = fXm*sin(roll)*sin(pitch)+fYm*cos(roll)-fZm*sin(roll)*cos(pitch);

// Arctangent of y/x
Heading = (atan2(fYm_comp,fXm_comp)*180.0)/M_PI;
if (Heading < 0)
Heading += 360;

Serial.print("Pitch (X): "); Serial.print(pitch_print); Serial.print("  ");
Serial.print("Roll (Y): "); Serial.print(roll_print); Serial.print("  ");
Serial.print("Heading: "); Serial.println(Heading);
delay(250);
}

Where did the calibration constants that are in that code come from?

Neither of the following sets of matrix coefficients are correct. They must be symmetrical about the diagonal.

Xa_cal =  0.986992*Xa_off + 0.001993*Ya_off - 0.004377*Za_off; //X-axis correction for combined scale factors (Default: positive factors)
Ya_cal =  0.025189*Xa_off + 0.998259*Ya_off - 0.000417*Za_off; //Y-axis correction for combined scale factors
Za_cal = -0.010287*Xa_off - 0.000417*Ya_off + 0.942771*Za_off; //Z-axis correction for combined scale factors

In this one, it is very likely that the 0.000982 coefficient on Xm_off wrong by a couple of orders of magnitude. It should be about 1.

Xm_cal =  0.000982*Xm_off + 0.006185*Ym_off + 0.015063*Zm_off; //X-axis correction for combined scale factors (Default: positive factors)
Ym_cal =  0.000039*Xm_off + 0.950124*Ym_off + 0.003084*Zm_off; //Y-axis correction for combined scale factors
Zm_cal =  0.000005*Xm_off + 0.003084*Ym_off + 0.880435*Zm_off; //Z-axis correction for combined scale factors

What do you mean by "not successful"?

Note that one of these two equations is wrong:

// Pitch and roll
roll  = atan2(fYa, sqrt(fXa*fXa + fZa*fZa));
pitch = atan2(fXa, sqrt(fYa*fYa + fZa*fZa));

I prefer the definitions given here, correct for the R-xyz convention, which result in the following equations for pitch and roll (in degrees, not radians as above):

 roll = atan2(y_Buff , z_Buff) * 57.3;
  pitch = atan2((- x_Buff) , sqrt(y_Buff * y_Buff + z_Buff * z_Buff)) * 57.3;

The calibration info came from Magneto 1.2. The Norm is - 31,813.7 nT Zipp code 50441, Altitude is 1145.

This recommendation came from the suggestion in the original response.

I go the codes from this post.

By not working I mean that the only reading I get are from 250 - 275 degrees.

I appreciate your help!!!!!

See revised post #5. There are serious problems in your transfer of data from magneto to the code.

I go the codes from this post.

Not a good source of information. Some of it is just wrong.

So what is your suggestion to getting the right values? I can alter the tilt code as you suggested.

So what is your suggestion to getting the right values?

You could start by posting the output of magneto, so that other people can have a clue what you are doing.

Then figure out how to incorporate that output into your code, without making errors.

A good test is to have your code output "corrected" accelerometer/magnetometer values, and run those back through magneto.

If you have done all that correctly, the newly calculated corrections from magneto will have zero for the offsets and the correction matrix will have "1" on the diagonal and zero everywhere else.

Here is the magneto output:

And the code is:

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

void setup()
{
Serial.begin(9600);
Wire.begin();
compass.init();
compass.enableDefault();
Serial.println("Magnetometer Calibrated (Units in Nanotesla)"); 
}

void loop()
{
compass.read();
float Xm_off, Ym_off, Zm_off, Xm_cal, Ym_cal, Zm_cal;

Xm_off = compass.m.x*(100000.0/1100.0) - 4773.840021; //X-axis combined bias (Non calibrated data - bias)
Ym_off = compass.m.y*(100000.0/1100.0) - 19460.077738; //Y-axis combined bias (Default: substracting bias)
Zm_off = compass.m.z*(100000.0/980.0 ) + 31.402673; //Z-axis combined bias

/*Xm_cal =  0.000982*Xm_off + 0.006185*Ym_off + 0.015063*Zm_off; //X-axis correction for combined scale factors (Default: positive factors)
Ym_cal =  0.000039*Xm_off + 0.950124*Ym_off + 0.003084*Zm_off; //Y-axis correction for combined scale factors
Zm_cal =  0.000005*Xm_off + 0.003084*Ym_off + 0.880435*Zm_off; //Z-axis correction for combined scale factors
*/
Serial.print(Xm_cal, 10); Serial.print(" "); Serial.print(Ym_cal, 10); Serial.print(" "); Serial.println(Zm_cal, 10);
delay(125);
}

Rerun magneto with a much small value (e.g. 100) for the "norm of the magnetic or gravitational field", so that you get more significant figures in the correction matrix A-1.

Then put the 12 numbers from magneto into the code.

If you have used the "raw values" from the sensor as instructed, the scale factors (100000.0/1100.0), etc. below SHOULD NOT BE USED. Take them out of the code. They never should have been there in the first place.

compass.m.x*(100000.0/1100.0)

Rather than waste your time trying to correct the mistakes made by the author of the post linked in reply #6, you would be better off with this post and the examples attached to it.

Are these numbers in Magneto better? (see attachment)

I'm looking for code in link suggested.

Nothing attached.

OOPS - sorry for the delay my internet provide has tech issues, so I'm running on my phone as a hot spot right now.

You could use 1 instead of 100 for the "norm" and get even more reasonable results.

Are the data in the file "raw values.txt" truly the numbers that come directly from the sensor, without any modifications?

If so, the next step is to put the offsets and "correction for combined scale factors ..." into your program, similar to what I did for the examples posted on the Pololu forum.

// Code initialization statements from magneto

float B[3] = { 9955.15, -7948.26, 8511.80};

float Ainv[3][3] = {{  0.25060,  0.02942, -0.02955},
  {  0.02942,  0.31692,  0.00789},
  { -0.02955,  0.00789,  0.30592}

...

// Returns a set of scaled magnetic readings from the LIS3MDL
void read_data(vector * m)
{
  static float x, y, z;
  compass.read();
  x = compass.m.x - B[0];
  y = compass.m.y - B[1];
  z = 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;
}

};

Yes - no mods unless I had typo.

Edit - see my post above. The info is not coming from your code!!

I can't get your code to work.

I can't get your code to work.

Well, that is certainly a thoughtful, informative comment!

Please see the "How to use this forum" post for hints on what to do in order to get informed help.

Here is the code downloaded from the pololu site. If I replace LIS3MDL with LSM303 I can upload the code, but I get no data coming to the monitor.

Thanks for the help!!

#include <Wire.h>
#include <LIS3MDL.h>
// mods by S. James Remington 3/2018

LIS3MDL mag;
LIS3MDL::vector<int16_t> running_min = {32767, 32767, 32767}, running_max = { -32768, -32768, -32768};

char report[80];
LIS3MDL::vector<int16_t> m_off, m_scl;

void setup()
{
  Serial.begin(9600);
  Wire.begin();
  
  // enter keyboard character and hit "send" to start
  while (!Serial.available()); //wait for keyboard entry

  if (!mag.init())
  {
    Serial.println("Failed to detect and initialize magnetometer!");
    while (1);
  }

  mag.enableDefault();
  unsigned long start = millis();

  // calibration loop. Enter a keyboard character to start, then rotate Balboa in 3D, exploring all orientations
  // loop until no change in max/min withing 10 seconds

  Serial.println("Collecting calibration data");

  while (millis() - start < 10000UL) {
    delay(100);
    mag.read();
    snprintf(report, sizeof(report), "%+6d,%+6d,%+6d",
             mag.m.x, mag.m.y, mag.m.z);
    Serial.println(report);
    if (mag.m.x < running_min.x) {
      running_min.x = mag.m.x;
      start = millis();
    }
    if (mag.m.x > running_max.x) {
      running_max.x = mag.m.x;
      start = millis();
    }
    if (mag.m.y < running_min.y) {
      running_min.y = mag.m.y;
      start = millis();
    }
    if (mag.m.y > running_max.y) {
      running_max.y = mag.m.y;
      start = millis();
    }
    if (mag.m.z < running_min.z) {
      running_min.z = mag.m.z;
      start = millis();
    }
    if (mag.m.z > running_max.z) {
      running_max.z = mag.m.z;
      start = millis();
    }
  }

  Serial.println("Done collecting calibration data");
  snprintf(report, sizeof(report), "min: {%+6d, %+6d, %+6d}   max: {%+6d, %+6d, %+6d}",
           running_min.x, running_min.y, running_min.z,
           running_max.x, running_max.y, running_max.z);
  Serial.println(report);
  m_off.x = (running_max.x + running_min.x) / 2;
  m_off.y = (running_max.y + running_min.y) / 2;
  m_off.z = (running_max.z + running_min.z) / 2;
  m_scl.x = (running_max.x - running_min.x) / 2;
  m_scl.y = (running_max.y - running_min.y) / 2;
  m_scl.z = (running_max.z - running_min.z) / 2;
  snprintf(report, sizeof(report), "off: {%+6d, %+6d, %+6d}   scl: {%+6d, %+6d, %+6d}",
           m_off.x, m_off.y, m_off.z,
           m_scl.x, m_scl.y, m_scl.z);
  Serial.println(report);
  delay(10000); //show for 10 seconds
}

// print out some centered and scaled magnetometer values, while user rotates Balboa in space.
// This simple scaling approach does not work well due to severe hard iron distortion

void loop()
{
  float m[3];
  mag.read();
  m[0] = ((float) mag.m.x - m_off.x) / m_scl.x;
  m[1] = ((float) mag.m.y - m_off.y) / m_scl.y;
  m[2] = ((float) mag.m.z - m_off.z) / m_scl.z;
  for (int i = 0; i < 3; i++) {
    Serial.print(m[i]);
    Serial.print(",");
  }
  Serial.println();
  delay(200);
}
  // enter keyboard character and hit "send" to start
  while (!Serial.available()); //wait for keyboard entry