MPU6050 Pitch angle

Hello everyone, I'm working on a project where I'll use an MPU6050 sensor to get the inclination angle of a swing. I don't have much experience in Arduino and sensors but I was able to find some tutorials that help me to get very close to finishing the projects, but the problem is when I move the sensor in 90 degrees I get a reading of around 75 instead of 90. If anyone can help me fix it so I can get accurate readings I will appreciate it. Here is the code:

#include <Wire.h>



float RateRoll, RatePitch, RateYaw;
float Roll = 0.0, Pitch = 0.0, Yaw = 0.0;  // Current angles
float RateCalibrationRoll, RateCalibrationPitch, RateCalibrationYaw;
int RateCalibrationNumber;
unsigned long prevUpdateTime = 0;


void gyro_signals(void) {
  Wire.beginTransmission(0x68);
  Wire.write(0x1B);  // Register 0x1B is the Gyro Config register
  Wire.write(0x00);  // Set sensitivity to ±250°/s (default is ±131°/s)
  Wire.endTransmission();
  
  Wire.beginTransmission(0x68);
  Wire.write(0x43);
  Wire.endTransmission();
  Wire.requestFrom(0x68, 6);
  int16_t GyroX = Wire.read() << 8 | Wire.read();
  int16_t GyroY = Wire.read() << 8 | Wire.read();
  int16_t GyroZ = Wire.read() << 8 | Wire.read();
  RateRoll = (float)GyroX / 131.0;  // Adjust sensitivity from 65.5 to 131.0
  RatePitch = (float)GyroY / 131.0;
  RateYaw = (float)GyroZ / 131.0;
}

void calculate_angles(float dt) {
  Roll += RateRoll * dt;    
  Pitch += RatePitch * dt;  
  Yaw += RateYaw * dt;      
}

void setup() {
  Serial.begin(57600);
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
  Wire.setClock(400000);
  Wire.begin();
  delay(250);
  Wire.beginTransmission(0x68);
  Wire.write(0x6B);
  Wire.write(0x00);
  Wire.endTransmission();
  for (RateCalibrationNumber = 0;
       RateCalibrationNumber < 2000;
       RateCalibrationNumber++) {
    gyro_signals();
    RateCalibrationRoll += RateRoll;
    RateCalibrationPitch += RatePitch;
    RateCalibrationYaw += RateYaw;
    delay(1);
  }
  RateCalibrationRoll /= 2000;
  RateCalibrationPitch /= 2000;
  RateCalibrationYaw /= 2000;

}

void loop() {
  unsigned long currentTime = millis();
  float dt = (currentTime - prevUpdateTime) / 1000.0;  // Time in seconds since last update
  prevUpdateTime = currentTime;

  gyro_signals();
  RateRoll -= RateCalibrationRoll;
  RatePitch -= RateCalibrationPitch;
  RateYaw -= RateCalibrationYaw;

  calculate_angles(dt);


  Serial.println("Pitch Angle");
  Serial.println(Pitch);
 
  delay(50);
  
}

Sounds like the integration is not sufficiently accurate. Try increasing the gyro sample rate, and get rid of the 50 ms delay.

There is no point in integrating all three rotation rates, if only one angle is of interest.

Keep in mind that Euler angles interact, so the result of integrating all three values independently is meaningless.

What is the inclination angle of a swing ?
Are you making a digital spirit level ? Then you only need the accelerometer and a low-pass filter.

Your sketch in Wokwi simulation: MPU6050 Pitch angle - Wokwi ESP32, STM32, Arduino Simulator
If you start the simulation, then you can click on the MPU-6050 module to change the settings.

Hi, thanks for the replay. Yes, I'm only interested in the pitch angle for now as my main goal is to know what angle is the swing at. I tried to increase the sample up to 5000 but it still doesn't give me 90 degrees. I don't know if the problem is in my code or the sensor or both. Another problem I face is the drift but for now I want to ensure that the angle is accurate enough before I start fixing that. If you have any resources or tips I'll appreciate it. Thanks again for your time.

Hi, thanks for the replay. Yes I want to make a digital spirit level. I tried some codes I found online that only uses the accelerometer but they didn't give me the results I wanted. I want to calculate the the angle from 0 to 90 degrees, if you have any resources that can help me get that I'll appreciate it. The inclination angle of a swing seat would be from 0 to around 90 when it swing forward and -90 degrees when it swings backward. Thank you for your time.

I made a spirit level once with the accelerometer: Het Nederlandstalig Arduino forum - Bekijk onderwerp - Gebruik MPU-9250 9-DOF Gyro

// Waterpas 2
// ----------
// The most simple "spirit level" or "bubble level" or "angle measurement".
// No selftest, no calibration, only basic offset compensation.
// Not fully 3D, only for about maximum 45 tilting.
// With Arduino IDE 1.8.1, Arduino Mega 2560 + I2C level shifter + MPU-6050 / MPU-9250
// February 2017, Public Domain
//

// Tilt Sensing calculation:
//   NXP Freescale Application Note AN3461
//   The calculation seems to be called a "Farrell equation".
//   Discussion here:
//      http://stackoverflow.com/questions/3755059/3d-accelerometer-calculate-the-orientation
// Code based on: MPU-6050 Short Example Sketch
//   http://playground.arduino.cc/Main/MPU-6050#short
//

#include <Wire.h>
#include <EEPROM.h>
const int MPU_addr = 0x68; // I2C address of MPU-6050 / MPU-9250

float x, y, z;  // the acceleration
int16_t x_offset, y_offset, z_offset;
const uint16_t MARKER_ID = 0x23A2;   // a marker and a version for the EEPROM data


void setup()
{
  Serial.begin( 9600);
  Wire.begin();
 
  while(!Serial);    // For Leonardo/Micro/M0

  Serial.println();
  Serial.println(F( "Waterpas 2"));
  Serial.println(F( "Warning: this sketch is Quick and Dirty"));
  Serial.println(F( "Command 'c' to calibrate the offsets."));
  Serial.println(F( "Command 'w' to wipe the EEPROM."));

  uint16_t marker;
  EEPROM.get( 0, marker);
  EEPROM.get( 2, x_offset);
  EEPROM.get( 4, y_offset);
  EEPROM.get( 6, z_offset);   // z-axis offset is not used at the moment

  if( marker != MARKER_ID)
  {
    Serial.println(F( "The level is not offset compensated yet."));
    x_offset = 0;
    y_offset = 0;
    z_offset = 0;
  }

  Serial.print(F( "Offset (raw) x, y = "));
  Serial.print( x_offset);
  Serial.print(F( ", "));
  Serial.println( y_offset);

  init_sensor();

  // Start the filter with measured values.
  // This is to make the output accurate, right from the first sample.
  int16_t xi, yi, zi;
  get_accel( xi, yi, zi);
  x = float( xi - x_offset);
  y = float( yi - y_offset);
  z = float( zi);
}


void loop()
{
  if( Serial.available())
  {
    char c = Serial.read();
    if( isprint( c))
    {
      switch( c)
      {
      case 'c':
        Calibrate();

        // Reset the filter, after the calibration.
        // Assuming the angle is close to zero degrees now.
        x = 0.0;
        y = 0.0;
       
        break;
      case 'w':
        Serial.println(F( "EEPROM data wiped."));
        for( int i=0; i<8; i++)
        {
          EEPROM.write( i, 0xFF);
        }
        // Clear the offset as well.
        x_offset = 0;
        y_offset = 0;
        z_offset = 0;
        break;
      }
    }
  }
 
  int16_t xi, yi, zi;
  get_accel( xi, yi, zi);

  xi -= x_offset;
  yi -= y_offset;
  // z-axis is not used with offset ! It is pointing up.

  // Dampen the binary values of the sensor with a filter.
  // Very simple filter for now.
  // Perhaps a moving average is better ?
  // Perhaps filtering the Roll and Pitch is better ?
  x = 0.99 * x + 0.01 * float( xi);
  y = 0.99 * y + 0.01 * float( yi);
  z = 0.99 * z + 0.01 * float( zi);

  // Calculate the Roll and Pitch
  float Roll = atan2( y, z) * 180.0 / M_PI;
  float Pitch = atan2( x, sqrt( y*y + z*z)) * 180.0 / M_PI;

  // Update display with slower rate.
  static int count;
  count++;
  if( count >= 40)
  {
    count = 0;
//    Serial.print( "Roll=");
    Serial.print( Roll, 3);
    Serial.print( ", ");
//    Serial.print( "Pitch=");
    Serial.println( Pitch, 3);
  }

  delay(10);
}


void init_sensor()
{
  Wire.beginTransmission( MPU_addr);
  Wire.write(0x6B);     // PWR_MGMT_1 register
  Wire.write(0);        // set to zero (wakes up the MPU-6050 / MPU-9250)
  Wire.endTransmission();
}


void get_accel(int16_t & AcX, int16_t & AcY, int16_t & AcZ)
{
  Wire.beginTransmission( MPU_addr);
  Wire.write( 0x3B);    // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission();
  Wire.requestFrom( MPU_addr, 6);  // request only the accel registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)   
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
}


void Calibrate()
{
  const int num_samples = 1000;
 
  Serial.println(F( "Calibrating, please wait"));

  // Use long variables for averaging
  long xl = 0;
  long yl = 0;
  long zl = 0;
 
  for( int i=0; i<num_samples; i++)
  {
    int16_t xi, yi, zi;
    get_accel( xi, yi, zi);
    xl += long( xi);
    yl += long( yi);
    zl += long( zi);
    delay(1);
  }

  // Store the average offset in the global variables and in EEPROM.
  x_offset = int( xl / num_samples);
  y_offset = int( yl / num_samples);
  z_offset = int( zl / num_samples);

  EEPROM.put( 0, MARKER_ID);
  EEPROM.put( 2, x_offset);
  EEPROM.put( 4, y_offset);
  EEPROM.put( 6, z_offset);

  Serial.println(F( "Done"));
}

In Wokwi simulation: Spirit Level version 2 - Wokwi ESP32, STM32, Arduino Simulator
When the simulation is running, click on the MPU-6050 module to change the settings.
I think that it almost reaches the 90 and -90 degrees.

A real spirit level uses the earth gravity to keep the bubble pointing upward.
Therefor you only need the accelerometer to make a spirit level in my opinion.

Sorry, I don't know.

@jremington Do you know code for a spirit level that shows the angle of one axis all around in 360 degrees (or at least from -90 to +90) while the result is not too slow ?

The accelerometer approach will work for -90 to +90 degrees, as you posted above, but the sensor must be more or less stationary.

  // Calculate the Roll and Pitch
  float Roll = atan2( y, z) * 180.0 / M_PI;
  float Pitch = atan2( x, sqrt( y*y + z*z)) * 180.0 / M_PI;

Thank you so much for the replay I really appreciate it. Your code gives better results and has lower drift than my code. I also have another question, let's say I want to also calculate the yaw angle, is it possible to get good results using the MPU6050 alone? I've read that you will need a Magnetometer if you want reliable results for yaw. I already have HMC5883L sensor so should I use it with the MPU6050? Again, thanks for the help and have a great day.

Hello, thanks for the replay. Can I ask what you mean exactly by stationary? does it mean that the sensor should only rotate on the X-Y axis but not move in any direction? if so then that won't work with my swing project. Anyway thanks for your time and have a great day.

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