How to get absolute angle readings from an MPU6050?

Good afternoon!

I'm building an automatic leveling system for our RV, and I need very precise angle readings to figure out where level is for system.

The I2Cdevlib MPU6050 example using the DMP here gives readings that are precise enough, but the sensor resets to showing "level" whenever it's powered on.

Could someone please point me in the right direction to get absolute (not relative to the power-on position) measurements from the MPU6050?

Thanks a lot!
Jadon

You don't need that library which has bugs and is bloated.

A plane with pitch, roll and yaw is dynamic. A spirit level is static. That is a different approach.

You only need the acceleration data and a low pass filter. Calculate the overall vector of the acceleration and that is pointing downwards.
For best results, you have to calibrate the sensor.

I tried something here: https://arduinoforum.nl/viewtopic.php?f=4&t=2337&start=30#p17820.
That forum is in Dutch, but the sketch is in English.
I use 'roll' and 'pitch' in that sketch, I doubt if I would use those terms for a spirit level today.
After calibration, it is possible that the sensor changes over time or due to temperature changes. I got 0.3 degrees difference after some time without touching the sensor.

You might not have a real MPU-6050 :face_vomiting: so if you want to have it "very precise", then buy a good sensor :money_mouth_face:

Whats a 'RV' ?

That is US English: https://en.wikipedia.org/wiki/Recreational_vehicle. They like abbreviations.
In the Netherlands we call it a 'Camper'.

The MPU-6050 was discontinued years ago, and any modern accelerometer will outperform it.

All 3D accelerometers give "absolute" measurements of stationary pitch and roll, provided that they are properly calibrated. See this tutorial on calibration.

OIC

Thanks for the input, but I can't find a sketch (yours included) that gives readings as good as the i2cdev DMP example without using the DMP. The DMP readings are much more repeatable and steady.

I think the best solution would be to use the DMP and find a way to make it persistent, but I'm also open to the idea of a Kalman filter or similar if that can help give good readings.

I'd really appreciate it if someone can point me in the right direction! Thanks!

I'm not sure, but I think that the example with the 'dmp' uses sensor fusion. It combines the accelerometer and the gyro to eliminate the disadvantages of the other. A accelerometer is noisy and a gyro drifts. Together the result is better. But that is dynamic behavior.

When you take the sensor in your hand and then put it down, and see how fast it settles, that is dynamic behavior.

I still think that you only need the accelerometer. The sensitivity can be set higher or lower and the low pass filter can be changed, but you can not get more or better data for a spirit level than what the accelerometer can give.
A spirit level app on a smartphone uses also the accelerometer.

Can you tell more about the readings ? What do you consider to be good.

Thanks for the clarification, and yes the DMP is part of the MPU-6050 and calculates that sensor fusion.

I need a reading that is repeatable to within 0.2 degrees, due to the length of our motorhome.

I'll do some more testing and research for just using the accelerometer, as that doesn't suffer the resetting issue.

If you have a link that explains how your filter works, that would be great! I'm not well-versed on how low-pass filters work, and while I can see what it does mathematically, I'm not sure quite how it works.

Thanks!

The most simple low pass filter is by adding, for example, 1% of the new value to a filtered value.

float filtered_value;

void loop()
{
  float new_value = ...
  filtered_value = (0.99 * filtered_value) + (0.01 * new_value);
}

Because only 1% of the new value is used and 99% of the old existing filtered value, it takes some time before the filtered value reaches the new value. That is a low pass filter.

A MEMS sensor is noisy. My granddad did not have that noise problem: https://en.wikipedia.org/wiki/Plumb_bob

You will need to average several hundred accelerometer readings to get anywhere near that precision, with the ancient, noisy MPU-6050. And, you MUST calibrate the accelerometer so that the 3 axial readings are on the same scale (they are not, out of the box). Calibration tutorial here: Tutorial: How to calibrate a compass (and accelerometer) with Arduino | Underwater Arduino Data Loggers

Here is the simplest code to get tilt and roll angles. It doesn't use the gyro, because for static measurements, the gyro just adds noise.

// minimal MPU-6050 tilt and roll (sjr). Works with MPU-9250 too.
// works perfectly with GY-521, pitch and roll signs agree with arrows on sensor module 7/2019
//
// Tested with 3.3V eBay Pro Mini with no external pullups on I2C bus (worked with internal pullups)
// Add 4.7K pullup resistors to 3.3V if required. A4 = SDA, A5 = SCL

#include<Wire.h>
const int MPU_addr1 = 0x68;
float xa, ya, za, roll, pitch;

void setup() {

  Wire.begin();                                      //begin the wire communication
  Wire.beginTransmission(MPU_addr1);                 //begin, send the slave adress (in this case 68)
  Wire.write(0x6B);                                  //make the reset (place a 0 into the 6B register)
  Wire.write(0);
  Wire.endTransmission(true);                        //end the transmission
  Serial.begin(9600);
}

void loop() {

  Wire.beginTransmission(MPU_addr1);
  Wire.write(0x3B);  //send starting register address, accelerometer high byte
  Wire.endTransmission(false); //restart for read
  Wire.requestFrom(MPU_addr1, 6); //get six bytes accelerometer data
  int t = Wire.read();
  xa = (t << 8) | Wire.read();
  t = Wire.read();
  ya = (t << 8) | Wire.read();
  t = Wire.read();
  za = (t << 8) | Wire.read();
// formula from https://wiki.dfrobot.com/How_to_Use_a_Three-Axis_Accelerometer_for_Tilt_Sensing
  roll = atan2(ya , za) * 180.0 / PI;
  pitch = atan2(-xa , sqrt(ya * ya + za * za)) * 180.0 / PI; //account for roll already applied

  Serial.print("roll = ");
  Serial.print(roll,1);
  Serial.print(", pitch = ");
  Serial.println(pitch,1);
  delay(400);
}

That's brilliant! Thanks for explaining how it works.

I know! Have you ever heard of the KISS acronym?

I'm getting the feeling that the MPU6050 is rather outdated, but which i2c accelerometer do you recommend for future projects?

Thanks for the code and tips! I'll do some experimentation and let you know what I figure out. I'm starting to think that if I fiddle with a low-pass filter I can get acceptable results without using the DMP.

The MPU-6050 was discontinued years ago, so any that you buy today are very likely to be rejects, recycled salvage or counterfeit.

Any accelerometer now in production by a reputable manufacturer will outperform it. Pololu, Sparkfun and Adafruit have selections.