Creating a Tilt-Compensated Compass

Hello, I read a previous thread about a tilt-compensated compass, however, it wasn't of much help to me.

I have a LSM303DLHC accelerometer and magnetometer. I want to be able to tell the compass heading (azimuth/yaw) and the altitude angle (elevation/pitch) at the same time. However, although the altitude reading works fine at all times, the compass heading gets thrown way off when not perfectly flat/horizontal. Therefore, for it to work, I would have to have the altitude be 0 for the heading to be accurate. This is not ideal. Does anyone else have a suggestion? Could it be converted using math?

P.S. I have 2 of these sensors, do you think they would work separately on one arduino (uno)? Also, I have the Adafruit Precision NXP 9-DOF sensor board that works decently well, but I don't think the hassle of working with it is worth it.

Thank you in advance! :slight_smile:

You must calibrate the magnetometer before it will be useful as a compass. Overview here.
Pololu has simple code for implementing a tilt compensated compass with previous versions of that sensor - slight modifications may be required. Check their site for the code.

Thanks for the reply!

I tried the above suggestions and input the guy's formula for a tilt compensated compass (see below). The roll and pitch seem to work just fine, but the compass heading prints a number like -45 for what i believe to be due north, and only seems to range from -90 to 90 as it is spun around in all axis directions.

P.S. I didn't calibrate the magnetic sensor because from my understanding it doesn't matter for just a simple test. Also, doesn't it come factory calibrated?

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

float Altitude=0;
float heading=0;
float xh=0;
float yh=0;
float Roll=0;

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

void displaySensorDetails(void)
{
  sensor_t sensor;
  accel.getSensor(&sensor);
  Serial.println("------------------------------------");
  Serial.print  ("Sensor:       "); Serial.println(sensor.name);
  Serial.print  ("Driver Ver:   "); Serial.println(sensor.version);
  Serial.print  ("Unique ID:    "); Serial.println(sensor.sensor_id);
  Serial.print  ("Max Value:    "); Serial.print(sensor.max_value); Serial.println(" m/s^2");
  Serial.print  ("Min Value:    "); Serial.print(sensor.min_value); Serial.println(" m/s^2");
  Serial.print  ("Resolution:   "); Serial.print(sensor.resolution); Serial.println(" m/s^2");
  Serial.println("------------------------------------");
  Serial.println("");
  delay(500);
}

void setup(void)
{
#ifndef ESP8266
  while (!Serial);     
#endif
  Serial.begin(9600);
  Serial.println("Accelerometer Test"); Serial.println("");

if(!accel.begin())
{
 /* There was a problem detecting the ADXL345 ... check your connections */
  Serial.println("Ooops, no LSM303 detected ... Check your wiring!");
  while(1);
}

 displaySensorDetails();
}

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

  Altitude = (atan(event.acceleration.x/event.acceleration.z));//*180/PI;
  Roll = atan(event.acceleration.y/event.acceleration.z);

  mag.getEvent(&event); 
   
  xh=event.magnetic.x*cos(Altitude)+event.magnetic.z*sin(Altitude);
  yh=event.magnetic.x*sin(Roll)*sin(Altitude)+event.magnetic.y*cos(Roll)-event.magnetic.z*sin(Roll)*cos(Altitude);

  heading=atan(yh/xh)*180/PI;
 
  Serial.print("Altitude: ");;
  Serial.println(Altitude*180/PI);
  Serial.print("Roll: ");
  Serial.println(Roll*180/PI);
  Serial.print("Direction from North: ");
  Serial.println(heading);
  delay(1000);
  
}

I didn't calibrate the magnetic sensor

That was your mistake, which explains the following:

The compass heading prints a number like -45 for what i believe to be due north, and only seems to range from -90 to 90 as it is spun around in all axis directions.

You also have to correct for the local magnetic declination, in order to determine true North.