I am having troubles with tilt compensating my compass.

I am using the LSM303DLHC accelerometer/compass in combination together with the Adafruit_LSM303_U.h.

I was able to get my compass to the right direction withthis little snippet:

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

I also was able to get my roll and tilt angle with converting my X and Y of my accelerometer to angles:

   headingNew = asin(eventA.acceleration.x/10);
   float tilt = headingNew*180/PI/90*100;
   headingNew = asin(eventA.acceleration.y/10);
   float roll = headingNew*180/PI/90*100;

Now I need to compensate my angle to get a new x and y for inside my heading calculation but the problem is that I can't seem to get the right functions. Online I found the functions below from a large tutorial about the sensor but this gives me completely wrong values.

    //Xh = event.magnetic.x*cos(tilt)+event.magnetic.z*sin(tilt);
    //Yh = event.magnetic.x*sin(roll)*sin(tilt)+event.magnetic.y*cos(roll)-event.magnetic.z*sin(roll)*cos(tilt);

or in the code itself:

   Xh = event.magnetic.x*(eventA.acceleration.z/10)+event.magnetic.z*(eventA.acceleration.x/10);
   Yh = event.magnetic.x*(eventA.acceleration.y/10)*(eventA.acceleration.x/10)+event.magnetic.y*(eventA.acceleration.z/10)-event.magnetic.z*(eventA.acceleration.y/10)*(eventA.acceleration.z/10);

I actually get the right angle on a flat surface. But it is even more inaccurate at an angle as the normal heading. Does anybody have any experience with this sensor? Already a big thanks for the help.


The sin() and cos() functions expect their arguments to be in radians. If you feed them with anything else, be it degrees, percent slope, or whatever, expect the result to be just crap.

Edit: The OP changed the question, invalidating my answer. So here comes a new answer...

You have mistaken eventA.acceleration.y for cos(tilt), eventA.acceleration.x for sin(tilt), and so on. They are not the same. You may think they are the same up to some multiplicative factor, and that's right. However, the factor doesn't cancel out as soon as you do computations like sin(roll)*sin(tilt). Don't rush your math: if you want to transform known-good formulas to more practical ones, check every step of the transformation carefully.

  • Actually for the sinus of the tilt I use my Accelerometer.x, for the cosinus of tilt the accelerometer.y. Also for the sin of the roll I use the accelerometer.Y. These are values from 0 to 1 that are the same as the cos/sin of a unit circle
    – Roel
    Nov 21 '19 at 9:24
  • @Roel: Re “for the cosinus of tilt [I use] the accelerometer.y”: I can't see that in the code you posted. If you are asking a question about a different piece of code, then please edit the question and make sure the code you show is the one that matches your question. Nov 21 '19 at 9:39
  • I edited the question to show you how the code really looks like in my project.
    – Roel
    Nov 21 '19 at 9:52
  • I just want to know what Is wrong with the final function because I am not able to get the right result.
    – Roel
    Nov 21 '19 at 10:11
  • @Roel: What's wrong: it uses eventA.acceleration.y instead of cos(tilt), etc. Solution: use the formula as published, with the angles in radians. Nov 21 '19 at 10:32

Via this Youtube movie I found a different library and made this code:

#include <SPI.h>
#include <Wire.h>
#include <LSM303.h>
const int centreX  = 160;
const int centreY  = 120;
const int diameter = 100;
#define STMPE_CS 8
#define TFT_CS 10
#define TFT_DC 9

int last_dx, last_dy, dx, dy;
LSM303 compass;
void setup(){
Serial.println("meters Test"); Serial.println("");

compass.m_min = (LSM303::vector<int16_t>){-602, -704, -511}; //values from 
compass.m_max = (LSM303::vector<int16_t>){+557, +436, +529}; //values from 
last_dx = centreX;
last_dy = centreY;
void loop() {
double angle;
float heading = compass.heading();

angle= heading; //use heading from compass
dx = (diameter * cos((angle-90)*3.14/180)) + centreX;    // calculate X position
dy = (diameter * sin((angle-90)*3.14/180)) + centreY;    // calculate Y position
last_dx = dx;
last_dy = dy;
Serial.print("angle:"); Serial.println(angle);
Serial.println("tilt:"); Serial.println(compass.a.x);

Now everything works. Sometimes you just need to choose your battles.

  • can you explain those value const int centreX = 160; const int centreY = 120; const int diameter = 100; Oct 12 at 16:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.