MPU6050 PID with DMP

Hi!

I have done some research and looked into others project to create my “own” flight controller. Right now I am having trouble with the yaw drift. I have read that yo can use the INT port on the MPU6050 to eliminate yaw drift but I cannot find a smart way to inplement in in my code. Any help or tips/soruces would be very appriciated!

Here is my code:

/*
  Gyro - Arduino uno
  VCC  -  5V
  GND  -  GND
  SDA  -  A4
  SCL  -  A5

  LCD  - Arduino Uno
  VCC  -  5V
  GND  -  GND
  SDA  -  A4
  SCL  -  A5
*/

//Include LCD and I2C library
#include <LiquidCrystal_I2C.h>
#include <Wire.h>

//Declaring some global variables
float elapsedTime, time, timePrev;
int gyro_x, gyro_y, gyro_z;
long acc_x, acc_y, acc_z, acc_total_vector;
int temperature;
long gyro_x_cal, gyro_y_cal, gyro_z_cal;
long loop_timer;
int lcd_loop_counter;
float angle_pitch, angle_roll;
int angle_pitch_buffer, angle_roll_buffer;
boolean set_gyro_angles;
float angle_roll_acc, angle_pitch_acc;
float angle_pitch_output, angle_roll_output;




float input_throttle = 1200;



//////////////////////////////PID FOR ROLL///////////////////////////
float roll_PID, pid_throttle_L_F, pid_throttle_L_B, pid_throttle_R_F, pid_throttle_R_B, roll_error, roll_previous_error;
float roll_pid_p = 0;
float roll_pid_i = 0;
float roll_pid_d = 0;
///////////////////////////////ROLL PID CONSTANTS////////////////////
double roll_kp = 0.7; //3.55
double roll_ki = 0.006; //0.003
double roll_kd = 1.2; //2.05
float roll_desired_angle = 0;     //This is the angle in which we whant the

//////////////////////////////PID FOR PITCH//////////////////////////
float pitch_PID, pitch_error, pitch_previous_error;
float pitch_pid_p = 0;
float pitch_pid_i = 0;
float pitch_pid_d = 0;
///////////////////////////////PITCH PID CONSTANTS///////////////////
double pitch_kp = 0.72; //3.55
double pitch_ki = 0.006; //0.003
double pitch_kd = 1.22; //2.05
float pitch_desired_angle = 0;     //This is the angle in which we whant the

//Initialize the LCD library
LiquidCrystal_I2C lcd(0x27, 16, 2);


void setup() {
  Wire.begin();                                                        //Start I2C as master
  Serial.begin(57600);                                                 //Use only for debugging
  pinMode(13, OUTPUT);                                                 //Set output 13 (LED) as output

  setup_mpu_6050_registers();                                          //Setup the registers of the MPU-6050 (500dfs / +/-8g) and start the gyro

  digitalWrite(13, HIGH);                                              //Set digital output 13 high to indicate startup

  lcd.begin();                                                         //Initialize the LCD
  lcd.backlight();                                                     //Activate backlight
  lcd.clear();                                                         //Clear the LCD

  lcd.setCursor(0, 0);                                                 //Set the LCD cursor to position to position 0,0
  lcd.print("  MPU-6050 IMU");                                         //Print text to screen
  lcd.setCursor(0, 1);                                                 //Set the LCD cursor to position to position 0,1
  lcd.print("     V1.0");                                              //Print text to screen

  delay(1500);                                                         //Delay 1.5 second to display the text
  lcd.clear();                                                         //Clear the LCD

  lcd.setCursor(0, 0);                                                 //Set the LCD cursor to position to position 0,0
  lcd.print("Calibrating gyro");                                       //Print text to screen
  lcd.setCursor(0, 1);                                                 //Set the LCD cursor to position to position 0,1
  for (int cal_int = 0; cal_int < 2000 ; cal_int ++) {                 //Run this code 2000 times
    if (cal_int % 125 == 0)lcd.print(".");                             //Print a dot on the LCD every 125 readings
    read_mpu_6050_data();                                              //Read the raw acc and gyro data from the MPU-6050
    gyro_x_cal += gyro_x;                                              //Add the gyro x-axis offset to the gyro_x_cal variable
    gyro_y_cal += gyro_y;                                              //Add the gyro y-axis offset to the gyro_y_cal variable
    gyro_z_cal += gyro_z;                                              //Add the gyro z-axis offset to the gyro_z_cal variable
    delay(3);                                                          //Delay 3us to simulate the 250Hz program loop
  }
  gyro_x_cal /= 2000;                                                  //Divide the gyro_x_cal variable by 2000 to get the avarage offset
  gyro_y_cal /= 2000;                                                  //Divide the gyro_y_cal variable by 2000 to get the avarage offset
  gyro_z_cal /= 2000;                                                  //Divide the gyro_z_cal variable by 2000 to get the avarage offset

  lcd.clear();                                                         //Clear the LCD

  lcd.setCursor(0, 0);                                                 //Set the LCD cursor to position to position 0,0
  lcd.print("Pitch:");                                                 //Print text to screen
  lcd.setCursor(0, 1);                                                 //Set the LCD cursor to position to position 0,1
  lcd.print("Roll :");                                                 //Print text to screen

  digitalWrite(13, LOW);                                               //All done, turn the LED off

  loop_timer = micros();                                               //Reset the loop timer
}


void loop() {

  timePrev = time;                                                     // the previous time is stored before the actual time read
  time = millis();                                                     // actual time read
  elapsedTime = (time - timePrev) / 1000;

  read_mpu_6050_data();                                                //Read the raw acc and gyro data from the MPU-6050

  gyro_x -= gyro_x_cal;                                                //Subtract the offset calibration value from the raw gyro_x value
  gyro_y -= gyro_y_cal;                                                //Subtract the offset calibration value from the raw gyro_y value
  gyro_z -= gyro_z_cal;                                                //Subtract the offset calibration value from the raw gyro_z value

  //Gyro angle calculations
  //0.0000611 = 1 / (250Hz / 65.5)
  angle_pitch += gyro_x * 0.0000611;                                   //Calculate the traveled pitch angle and add this to the angle_pitch variable
  angle_roll += gyro_y * 0.0000611;                                    //Calculate the traveled roll angle and add this to the angle_roll variable

  //0.000001066 = 0.0000611 * (3.142(PI) / 180degr) The Arduino sin function is in radians
  angle_pitch += angle_roll * sin(gyro_z * 0.000001066);               //If the IMU has yawed transfer the roll angle to the pitch angel
  angle_roll -= angle_pitch * sin(gyro_z * 0.000001066);               //If the IMU has yawed transfer the pitch angle to the roll angel

  //Accelerometer angle calculations
  acc_total_vector = sqrt((acc_x * acc_x) + (acc_y * acc_y) + (acc_z * acc_z)); //Calculate the total accelerometer vector
  //57.296 = 1 / (3.142 / 180) The Arduino asin function is in radians
  angle_pitch_acc = asin((float)acc_y / acc_total_vector) * 57.296;    //Calculate the pitch angle
  angle_roll_acc = asin((float)acc_x / acc_total_vector) * -57.296;    //Calculate the roll angle

  //Place the MPU-6050 spirit level and note the values in the following two lines for calibration
  angle_pitch_acc -= 0.0;                                              //Accelerometer calibration value for pitch
  angle_roll_acc -= 0.0;                                               //Accelerometer calibration value for roll

  if (set_gyro_angles) {                                               //If the IMU is already started
    angle_pitch = angle_pitch * 0.9996 + angle_pitch_acc * 0.0004;     //Correct the drift of the gyro pitch angle with the accelerometer pitch angle
    angle_roll = angle_roll * 0.9996 + angle_roll_acc * 0.0004;        //Correct the drift of the gyro roll angle with the accelerometer roll angle
  }
  else {                                                               //At first start
    angle_pitch = angle_pitch_acc;                                     //Set the gyro pitch angle equal to the accelerometer pitch angle
    angle_roll = angle_roll_acc;                                       //Set the gyro roll angle equal to the accelerometer roll angle
    set_gyro_angles = true;                                            //Set the IMU started flag
  }

  //To dampen the pitch and roll angles a complementary filter is used
  angle_pitch_output = angle_pitch_output * 0.9 + angle_pitch * 0.1;   //Take 90% of the output pitch value and add 10% of the raw pitch value
  angle_roll_output = angle_roll_output * 0.9 + angle_roll * 0.1;      //Take 90% of the output roll value and add 10% of the raw roll value



  write_LCD();                                                         //Write the roll and pitch values to the LCD display

  ////////// P I D/ ////////////

  roll_error = roll_desired_angle - angle_roll_output;
  pitch_error = pitch_desired_angle - angle_pitch_output;

  // Prospect
  roll_pid_p = roll_kp * roll_error;
  pitch_pid_p = pitch_kp * pitch_error;

  // Integral
  if (-3 < roll_error < 3)
  {
    roll_pid_i = roll_pid_i + (roll_ki * roll_error);
  }
  if (-3 < pitch_error < 3)
  {
    pitch_pid_i = pitch_pid_i + (pitch_ki * pitch_error);
  }

  // Derivate
  roll_pid_d = roll_kd * ((roll_error - roll_previous_error) / elapsedTime);
  pitch_pid_d = pitch_kd * ((pitch_error - pitch_previous_error) / elapsedTime);

  // PID sum
  roll_PID = roll_pid_p + roll_pid_i + roll_pid_d;
  pitch_PID = pitch_pid_p + pitch_pid_i + pitch_pid_d;

  // Regulate PID output for ESCs
  if (roll_PID < -400) {
    roll_PID = -400;
  }
  if (roll_PID > 400) {
    roll_PID = 400;
  }
  if (pitch_PID < -4000) {
    pitch_PID = -400;
  }
  if (pitch_PID > 400) {
    pitch_PID = 400;
  }

  // Set the throttle for each motor
  pid_throttle_R_F = input_throttle - roll_PID - pitch_PID;
  pid_throttle_R_B = input_throttle - roll_PID + pitch_PID;
  pid_throttle_L_B = input_throttle + roll_PID + pitch_PID;
  pid_throttle_L_F = input_throttle + roll_PID - pitch_PID;

  // Regulate throttle for ESCs
  //Right front
  if (pid_throttle_R_F < 1100)
  {
    pid_throttle_R_F = 1100;
  }
  if (pid_throttle_R_F > 2000)
  {
    pid_throttle_R_F = 2000;
  }

  //Left front
  if (pid_throttle_L_F < 1100)
  {
    pid_throttle_L_F = 1100;
  }
  if (pid_throttle_L_F > 2000)
  {
    pid_throttle_L_F = 2000;
  }

  //Right back
  if (pid_throttle_R_B < 1100)
  {
    pid_throttle_R_B = 1100;
  }
  if (pid_throttle_R_B > 2000)
  {
    pid_throttle_R_B = 2000;
  }

  //Left back
  if (pid_throttle_L_B < 1100)
  {
    pid_throttle_L_B = 1100;
  }
  if (pid_throttle_L_B > 2000)
  {
    pid_throttle_L_B = 2000;
  }

  Serial.print("Right Front: ");
  Serial.println(pid_throttle_R_F);
  Serial.print("Right Back: ");
  Serial.println(pid_throttle_R_B);
  Serial.print("Left Back: ");
  Serial.println(pid_throttle_L_B);
  Serial.print("Left Front: ");
  Serial.println(pid_throttle_L_F);

  //////////////////////////////
  
  while (micros() - loop_timer < 4000);                                //Wait until the loop_timer reaches 4000us (250Hz) before starting the next loop
  loop_timer = micros();                                               //Reset the loop timer
}


void read_mpu_6050_data() {                                            //Subroutine for reading the raw gyro and accelerometer data
  Wire.beginTransmission(0x68);                                        //Start communicating with the MPU-6050
  Wire.write(0x3B);                                                    //Send the requested starting register
  Wire.endTransmission();                                              //End the transmission
  Wire.requestFrom(0x68, 14);                                          //Request 14 bytes from the MPU-6050
  while (Wire.available() < 14);                                       //Wait until all the bytes are received
  acc_x = Wire.read() << 8 | Wire.read();                              //Add the low and high byte to the acc_x variable
  acc_y = Wire.read() << 8 | Wire.read();                              //Add the low and high byte to the acc_y variable
  acc_z = Wire.read() << 8 | Wire.read();                              //Add the low and high byte to the acc_z variable
  temperature = Wire.read() << 8 | Wire.read();                        //Add the low and high byte to the temperature variable
  gyro_x = Wire.read() << 8 | Wire.read();                             //Add the low and high byte to the gyro_x variable
  gyro_y = Wire.read() << 8 | Wire.read();                             //Add the low and high byte to the gyro_y variable
  gyro_z = Wire.read() << 8 | Wire.read();                             //Add the low and high byte to the gyro_z variable

}

void write_LCD() {                                                     //Subroutine for writing the LCD
  //To get a 250Hz program loop (4us) it's only possible to write one character per loop
  //Writing multiple characters is taking to much time
  if (lcd_loop_counter == 14)lcd_loop_counter = 0;                     //Reset the counter after 14 characters
  lcd_loop_counter ++;                                                 //Increase the counter
  if (lcd_loop_counter == 1) {
    angle_pitch_buffer = angle_pitch_output * 10;                      //Buffer the pitch angle because it will change
    lcd.setCursor(6, 0);                                               //Set the LCD cursor to position to position 0,0
  }
  if (lcd_loop_counter == 2) {
    if (angle_pitch_buffer < 0)lcd.print("-");                         //Print - if value is negative
    else lcd.print("+");                                               //Print + if value is negative
  }
  if (lcd_loop_counter == 3)lcd.print(abs(angle_pitch_buffer) / 1000); //Print first number
  if (lcd_loop_counter == 4)lcd.print((abs(angle_pitch_buffer) / 100) % 10); //Print second number
  if (lcd_loop_counter == 5)lcd.print((abs(angle_pitch_buffer) / 10) % 10); //Print third number
  if (lcd_loop_counter == 6)lcd.print(".");                            //Print decimal point
  if (lcd_loop_counter == 7)lcd.print(abs(angle_pitch_buffer) % 10);   //Print decimal number

  if (lcd_loop_counter == 8) {
    angle_roll_buffer = angle_roll_output * 10;
    lcd.setCursor(6, 1);
  }
  if (lcd_loop_counter == 9) {
    if (angle_roll_buffer < 0)lcd.print("-");                          //Print - if value is negative
    else lcd.print("+");                                               //Print + if value is negative
  }
  if (lcd_loop_counter == 10)lcd.print(abs(angle_roll_buffer) / 1000); //Print first number
  if (lcd_loop_counter == 11)lcd.print((abs(angle_roll_buffer) / 100) % 10); //Print second number
  if (lcd_loop_counter == 12)lcd.print((abs(angle_roll_buffer) / 10) % 10); //Print third number
  if (lcd_loop_counter == 13)lcd.print(".");                           //Print decimal point
  if (lcd_loop_counter == 14)lcd.print(abs(angle_roll_buffer) % 10);   //Print decimal number
}

void setup_mpu_6050_registers() {
  //Activate the MPU-6050
  Wire.beginTransmission(0x68);                                        //Start communicating with the MPU-6050
  Wire.write(0x6B);                                                    //Send the requested starting register
  Wire.write(0x00);                                                    //Set the requested starting register
  Wire.endTransmission();                                              //End the transmission
  //Configure the accelerometer (+/-8g)
  Wire.beginTransmission(0x68);                                        //Start communicating with the MPU-6050
  Wire.write(0x1C);                                                    //Send the requested starting register
  Wire.write(0x10);                                                    //Set the requested starting register
  Wire.endTransmission();                                              //End the transmission
  //Configure the gyro (500dps full scale)
  Wire.beginTransmission(0x68);                                        //Start communicating with the MPU-6050
  Wire.write(0x1B);                                                    //Send the requested starting register
  Wire.write(0x08);                                                    //Set the requested starting register
  Wire.endTransmission();                                              //End the transmission
}

Thanks beforehand!
Best regards Max

Please do not keep starting new threads on the same topic.

The MPU-6050 cannot measure yaw. You need an external yaw reference, such as the magnetometer that is included in the 9DOF sensors used by most people.

The approach you have cobbled together above won’t work well, and was abandoned long ago.

Ok, what do you mean with the “wont work well” part? What is it that is outdated? What “new” is it to learn and approach in that case? I have ordered a MPU9250 acc gyro and magnetometer, I will probably wait for it as it seems like the most efficient and easiest way.

The math is mostly wrong, but in some special circumstances will “sort of” work. Experiment with it for while to see what I mean.

Then turn your attention to the modern approach, a 3D fusion filter like Mahony, or Madgwick.

Ok, what is the advantage of a 3d fusion filter vs my code?

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