How to remove the gravity from accelerometer

Hi all,
I am looking for a way to delete totally the contribution of gravity during the registration of LSM6DS3 sensor mounted in Arduino Uno wifi rev2. This sensor has an accelerometer and a gyroscope, not the magnetometer!
When I put the board on a flat surface acc_z=0.99, acc_x=0.03, acc_y=-0.00.
I think that is not accurate to calibrate the offset from this kind of situation.
That's the reason why I found the Madwick Filter to use. I would like to have a easy sketch with some numbers if it could be possible and an explanation because I didn't understand it totally.
Thank you for any replies

You can't remove the influence of gravity, unless you send it in an orbit into space. Maybe clarify what you want to accomplish.

1 Like

First calibrate the offsets by measuing the outputs in all 6 othogonal directions and averaging the +g and -g readings - for instance you see 0.99 above, if with the board inverted you saw -1.01, you'd know that the z offset is -0.01. Repeat for x and y being vertical.

Then you can calibrate the gains (my example above has a nice 1.00 for the z-gain, if the x and y are different you need to scale them to match).

Now you have nice calibrated readings. So you know gravity should be a 3D unit vector whenever the sensor is stationary on the earth's surface. If its going to be at different orientations you need to determine the orientation to remap that vector before subtracting it off. If the sensor is accelerating too, that acceleration will be the remainder after subtracting gravity.

When coding this stuff you always need to be careful to know which frame of reference you mean, as there is the global frame and the sensor frame, and converting between the two is commonly needed in this kind of code. The sensor only knows about sensor frame of reference of course...

1 Like

Indeed I was thinking to remove the offset, calculating how much the values in each axes directions differ from 1 if I apply a 360° rotation to z,x,y axes. By the way, I was thinking also that is not accurate in my case. The board with the sensor LSM6DS3 has to be mounted on the arm, think about then the movement of an arm during a walking gait. In other words, my global system is that one of the floor. Do you think that your technique (add or reduce the values to 1 for each ax) is sufficient?

The so called "linear acceleration" is calculated by subtracting the predicted value of the gravity vector from the total acceleration.

In turn the predicted value of gravity is obtained by transforming the world frame vector [0 0 1] into the body frame, which requires a full 3D orientation matrix or quaternion.

Doing so won't do you much good with consumer grade sensors, because the estimated 3D orientation is too inaccurate, as discussed here: Library | CH Robotics

1 Like

Could the roll,pitch, yaw help me to reach my object to have always a right orientation of imu?

/**
   What is this file?
   This is an arduino example that calculates accurate roll,pitch,yaw from raw gyro/accelerometer data
   It has a calibration stage which removes most of the gyro drift and a complementary filter
   that combines gyro and accelerometer angles to produce roll/pitch angles that don't drift (like the gyro angle) and aren't noisy
   (like the accel angles). As there is no magnetic compass on the nano iot 33, it's not possible to 'complement' the yaw
   - hence yaw will drift and is 'relative'.
*/
#include <Arduino_LSM6DS3.h>
#include <Wire.h>

float accelX,            accelY,             accelZ,            // units m/s/s i.e. accelZ if often 9.8 (gravity)
      gyroX,             gyroY,              gyroZ,             // units dps (degrees per second)
      gyroDriftX,        gyroDriftY,         gyroDriftZ,        // units dps
      gyroRoll,          gyroPitch,          gyroYaw,           // units degrees (expect major drift)
      gyroCorrectedRoll, gyroCorrectedPitch, gyroCorrectedYaw,  // units degrees (expect minor drift)
      accRoll,           accPitch,           accYaw,            // units degrees (roll and pitch noisy, yaw not possible)
      complementaryRoll, complementaryPitch, complementaryYaw;  // units degrees (excellent roll, pitch, yaw minor drift)

long lastTime;
long lastInterval;

void setup() {

  Serial.begin(57600);
  pinMode(LED_BUILTIN, OUTPUT);

  // this sketch will wait until something connects to serial!
  // this could be 'serial monitor', 'serial plotter' or 'processing.org P3D client' (see ./processing/RollPitchYaw3d.pde file)
  while (!Serial);

  if (!IMU.begin()) {
    Serial.println("Failed to initialize IMU!");
    while (1);
  }

  calibrateIMU(250, 250);

  lastTime = micros();

}

/*
  the gyro's x,y,z values drift by a steady amount. if we measure this when arduino is still
  we can correct the drift when doing real measurements later
*/
void calibrateIMU(int delayMillis, int calibrationMillis) {

  int calibrationCount = 0;

  delay(delayMillis); // to avoid shakes after pressing reset button

  float sumX, sumY, sumZ;
  int startTime = millis();
  while (millis() < startTime + calibrationMillis) {
    if (readIMU()) {
      // in an ideal world gyroX/Y/Z == 0, anything higher or lower represents drift
      sumX += gyroX;
      sumY += gyroY;
      sumZ += gyroZ;

      calibrationCount++;
    }
  }

  if (calibrationCount == 0) {
    Serial.println("Failed to calibrate");
  }

  gyroDriftX = sumX / calibrationCount;
  gyroDriftY = sumY / calibrationCount;
  gyroDriftZ = sumZ / calibrationCount;

}

/**
   Read accel and gyro data.
   returns true if value is 'new' and false if IMU is returning old cached data
*/
bool readIMU() {
  if (IMU.accelerationAvailable() && IMU.gyroscopeAvailable() ) {
    IMU.readAcceleration(accelX, accelY, accelZ);
    IMU.readGyroscope(gyroX, gyroY, gyroZ);
    return true;
  }
  return false;
}

// the loop function runs over and over again forever
void loop() {

  if (readIMU()) {
    long currentTime = micros();
    lastInterval = currentTime - lastTime; // expecting this to be ~104Hz +- 4%
    lastTime = currentTime;

    doCalculations();
    printCalculations();

  }

}

/**
   I'm expecting, over time, the Arduino_LSM6DS3.h will add functions to do most of this,
   but as of 1.0.0 this was missing.
*/
void doCalculations() {
  accRoll = atan2(accelY, accelZ) * 180 / M_PI;
  accPitch = atan2(-accelX, sqrt(accelY * accelY + accelZ * accelZ)) * 180 / M_PI;

  float lastFrequency = 1000000 / lastInterval;
  gyroRoll = gyroRoll + (gyroX / lastFrequency);
  gyroPitch = gyroPitch + (gyroY / lastFrequency);
  gyroYaw = gyroYaw + (gyroZ / lastFrequency);

  gyroCorrectedRoll = gyroCorrectedRoll + ((gyroX - gyroDriftX) / lastFrequency);
  gyroCorrectedPitch = gyroCorrectedPitch + ((gyroY - gyroDriftY) / lastFrequency);
  gyroCorrectedYaw = gyroCorrectedYaw + ((gyroZ - gyroDriftZ) / lastFrequency);

  complementaryRoll = complementaryRoll + ((gyroX - gyroDriftX) / lastFrequency);
  complementaryPitch = complementaryPitch + ((gyroY - gyroDriftY) / lastFrequency);
  complementaryYaw = complementaryYaw + ((gyroZ - gyroDriftZ) / lastFrequency);

  complementaryRoll = 0.98 * complementaryRoll + 0.02 * accRoll;
  complementaryPitch = 0.98 * complementaryPitch + 0.02 * accPitch;
}

/**
   This comma separated format is best 'viewed' using 'serial plotter' or processing.org client (see ./processing/RollPitchYaw3d.pde example)
*/
void printCalculations() {
  Serial.print(gyroRoll);
  Serial.print(',');
  Serial.print(gyroPitch);
  Serial.print(',');
  Serial.print(gyroYaw);
  Serial.print(',');
  Serial.print(gyroCorrectedRoll);
  Serial.print(',');
  Serial.print(gyroCorrectedPitch);
  Serial.print(',');
  Serial.print(gyroCorrectedYaw);
  Serial.print(',');
  Serial.print(accRoll);
  Serial.print(',');
  Serial.print(accPitch);
  Serial.print(',');
  Serial.print(accYaw);
  Serial.print(',');
  Serial.print(complementaryRoll);
  Serial.print(',');
  Serial.print(complementaryPitch);
  Serial.print(',');
  Serial.print(complementaryYaw);
  Serial.println("");
}

This is found in github but I have no a good idea about the plot

No. With consumer grade sensors, all 3D orientation methods have significant errors.

The simplest methods that work "well enough for most purposes" are the Madgwick and Mahony AHRS filters. The Mahony filter is best for 6DOF sensors. Here is a version for the MPU-6050 which could be adapted to the LSM6DS3 sensor: GitHub - jremington/MPU-6050-Fusion: Mahony 3D filter for accel/gyro and gyro-only integration

Be sure to calibrate the gyro.

Hey, @jremington thank you for the tip and disponibility. I just founded your code today during my researches. Anyway and unfortunately I am a rookie on this kind of task. I am quite a rookie also about C language. Can you teach me how your code could be adapted to the LSM6DS3 sensor? What do I have to change and above all why? I'm sorry if this request could be space off.
I am very confused about the way of reasoning. I found Mahony as Madgcwick as also Kalman.
This is not what I want I guess:

#include "Arduino_LSM6DS3.h"
#include "MadgwickAHRS.h"

// initialize a Madgwick filter:
Madgwick filter;
// sensor's sample rate is fixed at 104 Hz:
const float sensorRate = 104.00;

void setup() {
  Serial.begin(9600);
  // attempt to start the IMU:
  if (!IMU.begin()) {
    Serial.println("Failed to initialize IMU");
    // stop here if you can't access the IMU:
    while (true);
  }
  // start the filter to run at the sample rate:
  filter.begin(sensorRate);
}

void loop() {
  // values for acceleration and rotation:
  float xAcc, yAcc, zAcc;
  float xGyro, yGyro, zGyro;

  // values for orientation:
  float roll, pitch, heading;
  // check if the IMU is ready to read:
  if (IMU.accelerationAvailable() &&
      IMU.gyroscopeAvailable()) {
    // read accelerometer &and gyrometer:
    IMU.readAcceleration(xAcc, yAcc, zAcc);
    IMU.readGyroscope(xGyro, yGyro, zGyro);

    // update the filter, which computes orientation:
    filter.updateIMU(xGyro, yGyro, zGyro, xAcc, yAcc, zAcc);

    // print the heading, pitch and roll
    roll = filter.getRoll();
    pitch = filter.getPitch();
    heading = filter.getYaw();
//      Serial.print(xAcc);
//  Serial.print(',');
//  Serial.print(yAcc);
//  Serial.print(',');
//  Serial.print(zAcc);
//  Serial.println(',');
//  Serial.print(xGyro);
//  Serial.print(',');
//  Serial.print(yGyro);
//  Serial.print(',');
//  Serial.print(zGyro);
//  Serial.println(',');
 
    Serial.print(heading);
    Serial.print(',');
    Serial.print(pitch);
    Serial.print(',');
    Serial.println(roll);
  }
}

Try that code, after calibrating the gyro.

If you are a beginner, you simply cannot expect to jump into an advanced project like this without doing a lot of work learning the basics, including some college-level mathematics.

Otherwise, post on the Jobs and Paid Collaborations forum section. You might be asked to pay for the help.

1 Like

I didn't expect that you teach me the lesson, maybe I spoke not clearly, sorry. I studied The rotation matrices with Euler angles, as I studied the job of accelerometers, gyroscopes and magnetometers. But I am a rookie in the sensor that at the same time I am in trouble to write the theory as code, and above all starting from taking your code and change something to adapt it to LSM6DS3

What happens when you run the code posted in #8, with the gyro properly calibrated?

What I am trying to say is that for example, I am not sure about which one of your codes that you gently give me I have to use( MPU6050_MahonyIMU.ino or MPU6050_Mahony_calgyro.ino or MPU6050_gyro_only.ino). Furthermore, I think that I have to change the adresses of MPU that you use because my sensor is different, isn't right? And for this reason why, maybe I have to include also the LSM6DS3 library.
I know, maybe they could be stupid questions, but I am still not so flexible in this way. Forgive me.

My advice right now is to try the code in reply#8, as you do not yet have the skills or understanding to modify the code I posted.

I won't make those modifications because I do not have an LSM6DS3 to test it with.

@jremington Dear man, I really thank you anyway for the help that you showed. I hope to figure out with your code because I saw that with mine the not total accuracy influences so much what I want like result. I understand that maybe what I asked could sound weird and pretentious. Thank You for this!