LSM9DS1 can't extract yaw angle

Hi everyone,

I'm using a 9DOF IMU sensor (Adafruit LSM9DS1) for an RC vehicle. I managed to extract the sensor readings and calculate the roll and pitch angles, but I still can't calculate the yaw angle from my sensor readings. I'm getting an output from my reading but they don't make sense at this point ( differs between 0-75 or something). I have seen a lot of examples/videos calculating the yaw angle even with 6DOF sensors using only the accelerometer and the gyroscope ( for example: https://www.youtube.com/watch?v=yqFfmwVufMo&ab_channel=Matthias and https://www.youtube.com/watch?v=6OwN_HepaXY&ab_channel=square1a). How can I calculate the yaw angle preferably only using the accelerometer and the gyroscope? Here is my code, hope someone can help since I've been stuck at this point for months :confused: :

// Libraries
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_LSM9DS1.h>
#include <Adafruit_Sensor.h>  

// IMU filter constants
// ##########################################################
const int IMU_CALIBRATION_STEP = 1000;
const float GYRO_WEIGHT = 0.1; // Weight factor for the complementary filter
const float ALPHA_9DOF = 0.1;  // Weight for accelerometer data
const float GAMMA_9DOFX = 0.2; // Weight for magnetometer data
const float GAMMA_9DOFY = 0.2; // Weight for magnetometer data
const float BETA_9DOF = 0.02;  // Weight for gyroscope data
// ##########################################################

// IMU initialization
// ##########################################################
sensors_event_t a, m, g, temp;
float filteredAccelX = 0.0;
float filteredAccelY = 0.0;
float filteredMagnetometerY = 0;
float filteredMagnetometerX = 0;
float pitchAngle;
float rollAngle ;
float yawAngle  ;
// ##########################################################

struct Values {
  float calibratedPitchAngle;
  float calibratedRollAngle;
  float calibratedYawAngle;
  float OFFSET_YAW;
  float OFFSET_PITCH;
  float OFFSET_ROLL;
  bool readingGps;
};

Values readingImuValues;
Values offsets;

Adafruit_LSM9DS1 lsm = Adafruit_LSM9DS1();


void setupSensor()
{
  // 1.) Set the accelerometer range
  lsm.setupAccel(lsm.LSM9DS1_ACCELRANGE_2G);
  //lsm.setupAccel(lsm.LSM9DS1_ACCELRANGE_4G);
  //lsm.setupAccel(lsm.LSM9DS1_ACCELRANGE_8G);
  //lsm.setupAccel(lsm.LSM9DS1_ACCELRANGE_16G);
  
  // 2.) Set the magnetometer sensitivity
  // lsm.setupMag(lsm.LSM9DS1_MAGGAIN_4GAUSS);
  //lsm.setupMag(lsm.LSM9DS1_MAGGAIN_8GAUSS);
  lsm.setupMag(lsm.LSM9DS1_MAGGAIN_12GAUSS);
  //lsm.setupMag(lsm.LSM9DS1_MAGGAIN_16GAUSS);

  // 3.) Setup the gyroscope
  lsm.setupGyro(lsm.LSM9DS1_GYROSCALE_245DPS);
  //lsm.setupGyro(lsm.LSM9DS1_GYROSCALE_500DPS);
  //lsm.setupGyro(lsm.LSM9DS1_GYROSCALE_2000DPS);
}

Values calibrateOffsets() {

  float pitch_sum = 0;
  float roll_sum  = 0;
  float yaw_sum   = 0;

  Values values;

  Serial.println("Please keep the vehicle steady.");
  
  for (int i = 0; i < IMU_CALIBRATION_STEP; ++i) {
    
    if (i % (IMU_CALIBRATION_STEP/5)==0){
      Serial.println("Calibrating LSM9DS1...");
    }

    readImu();

    pitch_sum += pitchAngle;
    roll_sum  += rollAngle;
    yaw_sum   += yawAngle;

    delay(1);
  }

  // Average offsets
  values.OFFSET_PITCH = pitch_sum / IMU_CALIBRATION_STEP;
  values.OFFSET_ROLL  = roll_sum  / IMU_CALIBRATION_STEP;
  values.OFFSET_YAW   = yaw_sum   / IMU_CALIBRATION_STEP;
  
  Serial.println(""); Serial.println("Calibration done.");
  Serial.print("\n offset pitch : ");  Serial.print(values.OFFSET_PITCH); Serial.print("   offset roll : ");  Serial.print(values.OFFSET_ROLL);Serial.print("   offset yaw : "); Serial.println(values.OFFSET_YAW);
  // Serial.print("\n offset magnetometer X : ");  Serial.print(offsetMagnetX); Serial.print("   Y : ");  Serial.print(offsetMagnetY);Serial.print("   Z : "); Serial.println(offsetMagnetZ);
  
  delay(3000);
  return values;
}

void setup() 
{
  Serial.begin(9600);

  while (!Serial) {
    delay(1); // will pause Zero, Leonardo, etc until serial console opens
  }
  
  // Try to initialise and warn if we couldn't detect the chip
  if (!lsm.begin())
  {
    Serial.println("Oops ... unable to initialize the LSM9DS1. Check your wiring!");
    while (1);
  }
  Serial.println("Found LSM9DS1 9DOF");

  // helper to just set the default scaling we want, see above!
  setupSensor();
  offsets = calibrateOffsets();
}

void loop() 
{
  readingImuValues = writeImu();
  delay(50);
}

Values writeImu() {
  
  readImu();

  Values result;

  result.calibratedPitchAngle= pitchAngle - offsets.OFFSET_PITCH;
  result.calibratedRollAngle = rollAngle  - offsets.OFFSET_ROLL;
  result.calibratedYawAngle = yawAngle - offsets.OFFSET_YAW;

  Serial.print("P = ");
  Serial.print(result.calibratedPitchAngle);
  Serial.print("   R = ");
  Serial.print(result.calibratedRollAngle);
  Serial.print("   Y = ");
  Serial.println(result.calibratedYawAngle);
  return result;
  }
void readImu(){
  
  lsm.read();  // ask it to read in the data 

  // Get a new sensor event 
  lsm.getEvent(&a, &m, &g, &temp);

  // Calculte the filtered accelaration
  filteredAccelY = ALPHA_9DOF * (a.acceleration.y) + (1 - ALPHA_9DOF) * filteredAccelY;
  filteredAccelX = ALPHA_9DOF * (a.acceleration.x) + (1 - ALPHA_9DOF) * filteredAccelX;

  float accelAngleX = (atan((filteredAccelY)/sqrt(pow((filteredAccelX),2) + pow((a.acceleration.z),2)))*RAD_TO_DEG);
  float accelAngleY = (atan(-1*(filteredAccelX)/sqrt(pow((filteredAccelY),2) + pow((a.acceleration.z),2)))*RAD_TO_DEG);

  // Complementary filter to combine accelerometer and gyroscope data
  pitchAngle = GYRO_WEIGHT * (g.gyro.x + pitchAngle) + (1-GYRO_WEIGHT) * accelAngleX;
  rollAngle  = GYRO_WEIGHT * (g.gyro.y + rollAngle)  + (1-GYRO_WEIGHT) * accelAngleY;

  // Calculate yaw angle from magnetometer data
  filteredMagnetometerY = GAMMA_9DOFY * (m.magnetic.y) + (1 - GAMMA_9DOFY) * filteredMagnetometerY;
  filteredMagnetometerX = GAMMA_9DOFX * (m.magnetic.x) + (1 - GAMMA_9DOFX) * filteredMagnetometerX;

  yawAngle = atan2(filteredMagnetometerY, filteredMagnetometerX) * RAD_TO_DEG;
  
}```

You can't. Yaw is conventionally defined relative to North, so you need a reference measurement on the horizon.

I have seen a lot of examples/videos calculating the yaw angle even with 6DOF sensors

That angle is relative to some arbitrary origin. To measure yaw relative to North, you need the magnetometer, calibrated in place on the robot or the readings will be useless.

Best overview and tutorial on magnetometer calibration: Tutorial: How to calibrate a compass (and accelerometer) with Arduino | Underwater Arduino Data Loggers

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.