Difficulties acquiring Angle Values from MPU6050

I'm attempting to acquire angle values from an MPU6050 IMU sensor, using an ESP32-DevKitM-1 microcontroller.


ESP32-DevKitM-1 datasheet:
https://docs.espressif.com/projects/esp-idf/en/stable/esp32/hw-reference/esp32/user-guide-devkitm-1.html

MPU6050 board listing & chip datasheet:
https://www.amazon.com/dp/B07V67DQ5N?ref=ppx_yo2ov_dt_b_product_details&th=1


My setup contains the ESP32 board, the MPU6050 board and an SD card reader to save the readings from the sensor:


I'm also using the Arduino IDE v2.3.0.

I adapted the following testing code excerpt from another post about sensor, using the newer Adafruit_MPU6050 library and the SD functions:

#include <Wire.h>
#include "SPI.h"

#include <Adafruit_Sensor.h>
#include <Adafruit_MPU6050.h>

#include "SD.h"

const int PIN_IMU_SCL = 22;
const int PIN_IMU_SDA = 21;
const int PIN_SD_CS = 23;
const int PIN_SD_MISO = 10;
const int PIN_SD_MOSI = 18;
const int PIN_SD_CLK = 5;

const int IMU_GyroScale = 131;

Adafruit_MPU6050 IMU;
double IMU_Time;
double IMU_TimePrev = -1;
double IMU_GyroRotX;
double IMU_GyroRotY;
double IMU_GyroRotZ;

File DataFile;

void IMU_GetData(double& angleX, double& angleY, double& angleZ, double& temp)
{
  bool firstTime = false;

  if (IMU_TimePrev == -1)
    firstTime = true;

  // Calculating the elapsed time...
  IMU_TimePrev = IMU_Time;
  IMU_Time = millis();
  double timeDelta = (IMU_Time - IMU_TimePrev) / 1000;

  // Acquiring the readings from the accelerometer & gyroscope...
  sensors_event_t accelData;
  sensors_event_t gyroData;
  sensors_event_t tempData;
  IMU.getEvent(&accelData, &gyroData, &tempData);

  // Calculating the accelerometer angle...
  double accelRotX = (180 / PI) * atan(accelData.acceleration.x / sqrt(pow(accelData.acceleration.y, 2) + pow(accelData.acceleration.z, 2))); 
  double accelRotY = (180 / PI) * atan(accelData.acceleration.y / sqrt(pow(accelData.acceleration.x, 2) + pow(accelData.acceleration.z, 2)));
  double accelRotZ = (180 / PI) * atan(sqrt(pow(accelData.acceleration.x, 2) + pow(accelData.acceleration.y, 2)) / accelData.acceleration.z);

  // Calculating the gyroscope angle...
  double gyroRotX = (gyroData.gyro.x / IMU_GyroScale);
  double gyroRotY = (gyroData.gyro.y / IMU_GyroScale);
  double gyroRotZ = (gyroData.gyro.z / IMU_GyroScale);

  // Combining the two (complementary filter)...
  if (firstTime)
  {
    IMU_GyroRotX = accelRotX;
    IMU_GyroRotY = accelRotY;
    IMU_GyroRotZ = accelRotZ;
  }
  else
  {
    IMU_GyroRotX += timeDelta * gyroRotX;
    IMU_GyroRotY += timeDelta * gyroRotY;
    IMU_GyroRotZ += timeDelta * gyroRotZ;
  }

  angleX = (0.96 * accelRotX) + (0.04 * IMU_GyroRotX);
  angleY = (0.96 * accelRotY) + (0.04 * IMU_GyroRotY);
  angleZ = (0.96 * accelRotZ) + (0.04 * IMU_GyroRotZ);

  temp = tempData.temperature;
}

bool IMU_Init()
{
  bool status = IMU.begin();

  if (status)
  {
    IMU.setAccelerometerRange(MPU6050_RANGE_8_G);
    IMU.setGyroRange(MPU6050_RANGE_500_DEG);
    IMU.setFilterBandwidth(MPU6050_BAND_21_HZ);
  }

  IMU_Time = millis();

  delay(100);

  return status;
}

void setup() 
{
  Serial.begin(9600);
  Serial.println("Initializing...");

  Wire.begin(PIN_IMU_SDA, PIN_IMU_SCL);
  IMU_Init();

  SPI.begin(PIN_SD_CLK, PIN_SD_MISO, PIN_SD_MOSI, PIN_SD_CS);

  while (!SD.begin(PIN_SD_CS))
  {
    Serial.println("Unable to open SD!");
    delay(500);
  }

  DataFile = SD.open("/angles.txt", FILE_WRITE);
  DataFile.close();

  delay(100);
  Serial.println("Initialized!");
}

void loop() 
{
  double angleX;
  double angleY;
  double angleZ;
  double temp;
  IMU_GetData(angleX, angleY, angleZ, temp);
  String anglesStr = String(angleX) + ":" + String(angleY) + ":" + String(angleZ);

  DataFile = SD.open("/angles.txt", FILE_APPEND);
  DataFile.println(anglesStr);
  DataFile.close();

  Serial.println(anglesStr);

  delay(200);
}

Where IMU_GetData is the function responsible for acquiring the angle data from the gyroscope and the accelerator sensors on the MPU6050 board, and implementing a filter to attain a set of filtered angle values.

I then simply save the filtered angle values to the SD card. I communicate with the sensor using the Wire I2C library, and with the SD card using the SPI library.


The main issue I'm running into is the fact that the resultant angle values do not appear to accurately represent the rotation of the sensor.

As a test, I rotated the sensor 360° slowly around each one of the 3 axes, and saved the continuous angle readings during each rotation:

anglesX
anglesY
anglesZ

The time interval between each reading in the plots above is ~0.2s.


The first issue is that a rotation about the Z-axis visible in the last graph above appears to not cause any appreciable change in any of the angle readings.

I initally assumed that could be caused by the value of the IMU_GyroScale constant that the gyroscope angle values get multiplied by during the calculations carried out in the IMU_GetData function. I assumed that all MPU6050 chips will use that same scaling factor, but then I tried several of the other listed scaling factors listed in the datasheet, with no appreciable difference.

Is there something visibly wrong in my code with the angle calculations for the Z-axis? Could this be a sign of a defective sensor board?


The second thing I was curious about was the format of the filtered angle values.

I initially assumed that the filtered angle values I would be getting from the sensor would be the Euler angles; Roll, Pitch & Yaw. However, these appear to not represent those.

In fact, one of the main sources listed in the original posting that describes in detail the process of calculating the filtered angle values, also provides a visualization of the angle values acquired from the sensor:

Are these "tilt" values related to Euler angles? Is it possible to convert between the two?


Thanks for reading my post, any guidance is appreciated.

The angle is the integral of the gyro angular rate and thus is very sensitive to offsets. You may get more stable angle results from the acceleration sensor.

My understanding of how these "tilt" angles work is that, in order to attain a stable "filtered" reading you need to compbine the readings from both the gyroscope and the accelerometer. That is in fact how the source listed in the original posting describes it.

Is my method of doing that not correct?

The code you found somewhere is not correct. That code keeps cropping up on the forum, always with complaints about how it does not work.

For valid, reasonably accurate 3D orientation measurements, you need a properly calibrated 9DOF sensor running a modern sensor fusion algorithm.

X and Y accelerations (forces) don't change during rotation around the Z axis. What you see is the 4% gyro angle change, as found in your code.

Right, for calibration I'm assuming I take readings when the sensor first powers up while it's level, and subtract them from each subsequent reading.

As for the fusion algorithm, I think I heard of this before.

There's a paper that appears to describe the process in detail:

However, all of these papers appear to use another sensor, a "magnetometer":
image

Is that something simillar to a compass?

Would something like this work?

Also, if this device relies on magnetic fields, doesn't that mean that any electrical components, such as DC motors (which I plan on using near these sensors in my use case), would throw off the readings of these sensors?

Right, that makes sense. I wonder why the original author of the post marked that as the accepted answer then...

That is included in a 9DOF (degrees for freedom) sensor, and gives the North reference for the yaw angle used in navigation.

any electrical components, such as DC motors (which I plan on using near these sensors in my use case), would throw off the readings of these sensors?

Yes, the magnetometer is usually calibrated in place, which corrects those errors.

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

Most people use the Mahony or Madgwick sensor fusion algorithms. Libraries area available for most modern 9DOF MEMS sensors.

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