Issues with a MPU6050 and Servo

Hi, i'm having troubles with a school project and I'm pretty amateur with programming and electronics.
Basically, I'm developing the control system for an experimental aircraft, and I need to control it's response to the pitch movement moving the elevator with a Servo.
Until now the servo isn't moving and I've tried changing the baud values, pins and if/else logic.
This is the code so far:

#include <Servo.h>
#include <Wire.h>

Servo timon;

//variables de acel. y posc. del giroscopio

int16_t acc_rx, acc_ry, acc_rz, gyr_rx, gyr_ry, gyr_rz;

float ang_acc[2];
float ang_gyr[2];
float ang_tot[2];

float ttrans, t, tprev;
int i;
float rad_grad = 180/3.141592654;

float PID, PWM, error, errorprev;
float pid_p=0;
float pid_i=0;
float pid_d=0;

double kp=-58.02;
double ki=-58.67;
double kd=-11.71;

double center=1500;
float ang_des=0;

void setup(){
  Wire.begin();
  Wire.beginTransmission(0x68);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission(true);
  Serial.begin(250000);
  timon.attach(7);

  t=millis();
  delay(5000);
}

void loop(){

  tprev = t;
  t = millis();
  ttrans = (t-tprev) / 1000;

  Wire.beginTransmission(0x68);
  Wire.write(0x3B); 
  Wire.endTransmission(false);
  Wire.requestFrom(0x68,6,true); 

  acc_rx=Wire.read()<<8|Wire.read();
  acc_ry=Wire.read()<<8|Wire.read();
  acc_rz=Wire.read()<<8|Wire.read();

  /*---X---*/
  ang_acc[0] = atan((acc_ry/16384.0)/sqrt(pow((acc_rx/16384.0),2) + pow((acc_rz/16384.0),2)))*rad_grad;
  /*---Y---*/
  ang_acc[1] = atan(-1*(acc_rx/16384.0)/sqrt(pow((acc_ry/16384.0),2) + pow((acc_rz/16384.0),2)))*rad_grad;

  Wire.beginTransmission(0x68);
  Wire.write(0x43); 
  Wire.endTransmission(false);
  Wire.requestFrom(0x68,4,true); 

  gyr_rx=Wire.read()<<8|Wire.read();
  gyr_ry=Wire.read()<<8|Wire.read();

   /*---X---*/
  ang_gyr[0] = gyr_rx/131.0; 
  /*---Y---*/
  ang_gyr[1] = gyr_ry/131.0;

  /*---X---*/
  ang_tot[0] = 0.98 *(ang_tot[0] + ang_gyr[0]*ttrans) + 0.02*ang_acc[0];
  /*---Y---*/
  ang_tot[1] = 0.98 *(ang_tot[1] + ang_gyr[1]*ttrans) + 0.02*ang_acc[1];

  error=ang_tot[1] - ang_des;

  pid_p = kp * error;

  if(-3 < error < 3){ 
    pid_i = pid_i + (ki * error);
  }

  pid_d = kd * ((error - errorprev) / ttrans);

  PID = pid_p + pid_i + pid_d;

  if(PID < -1000)
  {
    PID=-1000;
  }
  if(PID > 1000)
  {
    PID=1000;
  }
  
  if(PWM < 1000){
    PWM = 1000;
  }
  if(PWM > 2000){
    PWM = 2000;
  }

   if(error > ang_des)
  {
    PWM = center - PID;
  }
  if(error < ang_des)
  {
    PWM = center + PID;
  }
  else{
    PWM = center;
  }

  timon.writeMicroseconds(PWM);
  errorprev=error;

}

Attempt to display what I'm doing

The servo and the IMU are connected to a common header

Thanks to anyone who could help me :slight_smile:

These constants are very surely incorrect. Look up "PID tuning" for tutorials on how to adjust them.

double kp=-58.02;
double ki=-58.67;
double kd=-11.71;

Here is a short program that will give you meaningful pitch and roll angles from the MPU-6050, corresponding to one of the standard conventions. What you posted (found in lots of places on the web), is just plain wrong.

// 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);
}

1 Like

Hi, friend, thanks for your interest.

This is part of my thesis, I wasn't suppose to get involved in this part of the job in the first place but my proffesor leave me in the dust with this one and I have to come up with a solution anyway I can. I know I shouldt be involved with this one but it led me here. And no, it can't hurt anyone.

I'm testing with the Arduino UNO, when it's all done im doing it in a Pro Mini.

Conections WERE like this:

IMU to Arduino
VCC--------5 v
SCL--------A5
SDA-------A4
GND------GND

But it turns out I't didn't work with the test so I had to connect ADO-------GND

I'll make sure to print the PWM values to see if they make sense.

Thanks again.

Thanks for your interest friend.

I was afraid the program my teacher told me to use was a dead end. I'll make sure to watch the tutorials for PID tuning.

Servo and MPU were not directly connected to the 5V pin. They were both connected to a common header that I welded.

Servo-----Arduino
GND------GND
VCC-------5V (Header)
Signal----Pin 8

And now its

IMU--------Arduino
VCC--------5 v (Header)
SCL--------A5
SDA-------A4
ADO------GND

without a ground connection.

ADO is used to select the I2C address. Have you seen any data at all from the '6050?

Try adding ground.

Use an I2C scanner sketch to see if it is responding.

 arduino i2c scanner

a7

I've been testing with other programs to see if it's working and so far it does with this configuration.

example 1:

#include<Wire.h>
const int MPU_addr1 = 0x68;
float xa, ya, za, 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();
  ya = (t << 8) | Wire.read();
  t = Wire.read();
  xa = (t << 8) | Wire.read();
  t = Wire.read();
  za = (t << 8) | Wire.read();
  pitch = atan2(xa , sqrt(xa * xa + za * za)) * 180.0 / PI; //account for roll already applied

  Serial.print("pitch angle = ");
  Serial.println(pitch,1);
  delay(500);
}

example 2:

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

Adafruit_MPU6050 mpu;
int x = 0;
int y = 0;
int z = 0;

Servo servo1; 


int value  = 0;

void setup(void) {
  Serial.begin(9600);

  // Try to initialize!
  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);
    }
  }
  Serial.println("MPU6050 Found!");

  // set accelerometer range to +-8G
  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);

  // set gyro range to +- 500 deg/s
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);

  // set filter bandwidth to 21 Hz
  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);

  delay(100);
  servo1.attach(7);

  servo1.write(0);

}

void loop() {
  /* Get new sensor events with the readings */
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);

  x = a.acceleration.x;
  y = a.acceleration.y;
  z = a.acceleration.z;

//Serial.print(x);Serial.print(" ");Serial.println(y);
if (x < 10 && x > 0 && y < 4 && y > -4){
  Serial.println("up");
   value = map(x,  0, 10, 0, 180);
   servo1.write(value);
   Serial.print(value);
  }

}

Do not power a servo from the Arduino 5V.

It is unlikely to work well, if at all and you can damage the Arduino just by trying. Use a separate power supply, for example 4xAA will work for one small servo.

1 Like

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