Self-balancing robot problem

Hello everyone,

I'm new here but hope I can find what I need in the shortest time as possible as I'm in a rush.
Let me introduce my project firstly: what I want to create is a self-balancing robot using Arduino Mega 2560 + MPU6050. I attached the code.

Now the problem is that the robot is blocking after a few seconds (the DC Motors are just keeping a constant speed instead of trying to balancing).

I'm not sure where is the problem, but I would really appreciate if someone would guide me.

Thank you!

Regards,
Harvey13

licenta.ino (2.74 KB)

Ok...... so the code that you provided in the attachment has been pasted into the frame below....

#include "MPU6050.h"

#define pi 3.14159
#define Angle_offset 0
#define Gry_offset 0 
#define Gyr_Gain 0.00763358   
#define RMotor_offset 0
#define LMotor_offset 0

long data;
int x, y;
float kp, ki, kd; 
float r_angle, f_angle, omega;
float Turn_Speed = 0, Turn_Speed_K = 0;
float Run_Speed = 0, Run_Speed_K = 0, Run_Speed_T = 0;
float LOutput,ROutput;

unsigned long preTime = 0;
float SampleTime = 0.08;
unsigned long lastTime;
float Input, Output;
float errSum, dErr, error, lastErr;
int timeChange; 

MPU6050 accelgyro;
int16_t ax, ay, az;
int16_t gx, gy, gz;





void setup()
{
   accelgyro.initialize();
   Serial.begin(115200);
  }

void loop()
{
   accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
   r_angle = (atan2(ay, az) * 180 / pi + Angle_offset); Serial.print(" r_angle="); Serial.print(r_angle); Serial.print('\n');
   omega =  Gyr_Gain * (gx +  Gry_offset);  Serial.print("  omega="); Serial.print(omega); Serial.print('\n');
   if (abs(r_angle)<45){
    myPID();
    PWMControl();
  }
  else
  {
    digitalWrite(9, LOW);
    digitalWrite(10, LOW);

    digitalWrite(3, LOW);
    digitalWrite(4, LOW);
    }
  
  }


  void myPID(){
  kp = 57.3;  //Serial.print("  kp=");Serial.print(kp);Serial.print('\n');
  kd = analogRead(A0)*1.0;  //Serial.print("  kd=");Serial.print(kd);
  //ki = analogRead(A3)*0.001;  Serial.print("  ki=");Serial.print(ki);

  //kp = 0;  Serial.print("  kp=");Serial.print(kp);
  //kd = 0;  Serial.print("  kd=");Serial.print(kd);
  ki = 0;  //Serial.print("  ki=");Serial.print(ki);
   
  unsigned long now = millis();
  float dt = (now - preTime) / 1000.0;  
  preTime = now;
  float K = 0.8;
  float A = K / (K + dt);
  f_angle = A * (f_angle + omega * dt) + (1 - A) * r_angle;  Serial.print("  f_angle=");Serial.print(f_angle);Serial.print('\n');
  
  timeChange = (now - lastTime);
  if(timeChange >= SampleTime){
    Input = f_angle;
    error = Input;
    errSum += error * timeChange;
    dErr = (error - lastErr) / timeChange;
    Output = kp * error + ki * errSum + kd * dErr; Serial.print("  Output=");Serial.print(Output);Serial.print('\n');
    lastErr = error;
    lastTime = now;
  }
}

void PWMControl(){
  
  if(Output > 0){
    analogWrite(8,min(255, abs(Output) + LMotor_offset));
    digitalWrite(11, LOW);
    digitalWrite(9, HIGH);
    digitalWrite(10, HIGH);

    analogWrite(5, min(255, abs(Output) + LMotor_offset));
    digitalWrite(2, LOW);
    digitalWrite(3, HIGH);
    digitalWrite(4, HIGH);

     }
  else if(Output <= 0){
    
    analogWrite(11,min(255, abs(Output) + LMotor_offset));
    digitalWrite(8, LOW);
    digitalWrite(9, HIGH);
    digitalWrite(10, HIGH);

    analogWrite(2, min(255, abs(Output) + LMotor_offset));
    digitalWrite(5, LOW);
    digitalWrite(3, HIGH);
    digitalWrite(4, HIGH);
  }
  
    
}

Yes.... I'm very familiar with it. The code has issues with it. Whoever first slapped it together had left mistakes in it.

For example.... there is a line " timeChange = (now - lastTime); ", but "lastTime" is not even initialised.

And.... this line " if(timeChange >= SampleTime){ ". Here, you can eventually find that "timeChange" and "SampleTime" don't even have the same units of time.

To find out certain errors that can be fixed within it... just read about it at this link:

http://www.instructables.com/id/InstaBots-Upright-Rover/

That should help you set you on a better path. And try setting K to 0.98 to begin with ( instead of 0.8 )

I also want to point out that I use a screw-driver to adjust three potentiometers...

kp = analogRead(A0)*0.1;
kd = analogRead(A2)*1.0;
ki = analogRead(A1)*0.001;

Note that kd is adjusted by 'A2'. In the original software, A0 is linked to kp. A1 is linked to ki, and A2 is linked to kd.

The original code (that whoever first supplied to the public) looked like:

kp = analogRead(A0)*0.1; Serial.print(" kp=");Serial.print(kp);
kd = analogRead(A2)*1.0; Serial.print(" kd=");Serial.print(kd);
//ki = analogRead(A3)*0.001; Serial.print(" ki=");Serial.print(ki);

It had 'A3' linked to ki, which is totally wrong.... since it should be 'A1', not A3.

It's best to use a screw-driver to set the pid values. You normally set them all to zero to begin with.....then wind up the kp a bit to get some responsiveness, then increase kd to help the bot ease up a bit.

Some sensor calibration is also recommended. This means manually making the bot stand vertically upright (ie. prop up the bot with a stand, with motors de-activated, or no powers to motors) and creating a separate piece of software that allows you to obtain (log) the data, so that you can do some mathematical averaging of the data. All part of the calibration. In the end, you get some 'offset' values that you can use for the calibration corrections. For example:

#define Gry_offset -77.86   //initially 0 //-77.86
#define Gyr_Gain 0.00763358  //degree per sec
#define Angle_offset -0.25   //initially 0
#define RMotor_offset 21
#define LMotor_offset 20
#define pi 3.14159 

#define ay_offset -264.09  // 0
#define az_offset -485.774  // 0

long data;
int x, y;
float kp, ki, kd; 
float r_angle = 0;
float f_angle = 0;
float omega;

float Turn_Speed = 0, Turn_Speed_K = 0;
float Run_Speed = 0, Run_Speed_K = 0, Run_Speed_T = 0;
float LOutput,ROutput;

unsigned long preTime = 0;
float SampleTime = 0.001;  //normally 0.08
unsigned long lastTime = 0;
float Input, Output;
float errSum, dErr, error, lastErr;
int timeChange;

So..... in your main loop, you might have code looking like this..... (which applies those calibration offsets)...

r_angle = (atan2(ay+ay_offset, az+az_offset) * 180 / pi + Angle_offset);
omega = Gyr_Gain * (gx + Gry_offset); //Gry_offset is gx_offset

A couple more things are - using motor voltage offsets (eg. RMotor_offset and LMotor_offset) overcomes some problems with 'motor deadzone'. And, if you notice any real-time sensor readings (such as displayed on your monitor screen) don't look right, or coming up as all zeros etc, then you may need to see if your sensors are working, or broken. This happened to me once ..... turned out to be a faulty MPU.

Responding to your private message .......

The original code will actually work (despite the flaws in it). However, instead of setting PID values, I think it is better (at first) to use the screw-driver and potentiometers to set them. A procedure is used to set those values. Best to start with kp, followed by kd, then ki, in that order.

The code I have included in the frame below is nearly the same as the original one - except I have removed the 'receive' (remote control) code....since we don't need that yet.

The 'lastTime' parameter is now initialised to zero (at least). Also note - the part that says " if(timeChange >= SampleTime) " will always be true, so that part of the code will always be executed (regardless), because the author incorrectly used different time units for "timeChange" and "SampleTime". But that's ok.... it'll still work even if you don't fix it. Otherwise, you can simply remove that 'if' statement (and its associated brackets), which will allow the code within it to execute every time. But, as mentioned, it'll execute every time already.

The original code comes from: SainSmart | Desktop CNC, 3D Printing & DIY Tools | Power to the Makers – SainSmart.com

I changed some things in the code - shown in the frame below. However, accelerometer calibrations should be done too. So, apart from needing a correction value for one of the gyroscope values (Gry_offset), the same kind of correction values need to be found for ay and az readings. These can only be obtained through actual measurements done on the MPU6050 accelerometers. So, before even doing anything with the bot, accelerometer measurements need to be made to ensure that this sensor device is working properly - giving sensible readings.

Getting the bot to balance well requires both the hardware and the software control algorithms to be working properly. After doing the sensor testing, it is usually best to remove all the Serial.print commands, which can probably slow the control system and impact the performance.

#include "Wire.h"
#include "SPI.h"  
#include "Mirf.h"
#include "nRF24L01.h"
#include "MirfHardwareSpiDriver.h"
#include "I2Cdev.h"
#include "MPU6050.h"

MPU6050 accelgyro;
int16_t ax, ay, az;
int16_t gx, gy, gz;

#define Gry_offset 0 
#define Gyr_Gain 0.00763358  
#define Angle_offset 0
#define RMotor_offset 20
#define LMotor_offset 20
#define pi 3.14159 

long data;
int x, y;
float kp, ki, kd; 
float r_angle, f_angle, omega;
float Turn_Speed = 0, Turn_Speed_K = 0;
float Run_Speed = 0, Run_Speed_K = 0, Run_Speed_T = 0;
float LOutput,ROutput;

unsigned long preTime = 0;
float SampleTime = 0.08;
unsigned long lastTime = 0; //initialised
float Input, Output;
float errSum, dErr, error, lastErr;
int timeChange; 

int TN1=3;
int TN2=4;
int ENA=9;

int TN3=5;
int TN4=6;
int ENB=10;

void setup() {
  Wire.begin();
  accelgyro.initialize();
  pinMode(TN1,OUTPUT);
  pinMode(TN2,OUTPUT);
  pinMode(TN3,OUTPUT);
  pinMode(TN4,OUTPUT);
  pinMode(ENA,OUTPUT);
  pinMode(ENB,OUTPUT);
  
  Mirf.spi = &MirfHardwareSpi;   
  Mirf.init();
  Mirf.setRADDR((byte *)"serv1");
  Mirf.payload = sizeof(long);
  Mirf.config();
  Serial.begin(115200);
}

void loop() {
  accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
  r_angle = (atan2(ay, az) * 180 / pi + Angle_offset);
  omega =  Gyr_Gain * (gx +  Gry_offset);  Serial.print("  omega="); Serial.print(omega);
  if (abs(r_angle)<45){
    myPID();
    PWMControl();
  }
  else{
    analogWrite(ENA, 0);
    analogWrite(ENB, 0);
  }
}


void myPID(){
  kp = analogRead(A0)*0.1;  Serial.print("  kp=");Serial.print(kp);
  kd = analogRead(A2)*1.0;  Serial.print("  kd=");Serial.print(kd);
  ki = analogRead(A1)*0.001;  Serial.print("  ki=");Serial.print(ki);

  //kp = 0;  Serial.print("  kp=");Serial.print(kp);
  //kd = 0;  Serial.print("  kd=");Serial.print(kd);
  //ki = 0;  Serial.print("  ki=");Serial.print(ki);
   
  unsigned long now = millis();
  float dt = (now - lastTime) / 1000.0;  //changed preTime to lastTime
  lastTime = now; //changed preTime to lastTime
  float K = 0.98; //modified
  float A = K / (K + dt);
  f_angle = A * (f_angle + omega * dt) + (1 - A) * r_angle;  Serial.print("  f_angle=");Serial.print(f_angle);
  
  timeChange = (now - lastTime);
  if(timeChange >= SampleTime){
    Input = f_angle;
    error = Input;
    errSum += error * timeChange;
    dErr = (error - lastErr) / timeChange;
    Output = kp * error + ki * errSum + kd * dErr;
    LOutput = Output + Run_Speed + Turn_Speed;  Serial.print("  LOutput=");Serial.print(LOutput);
    ROutput = Output + Run_Speed - Turn_Speed;  Serial.print("  ROutput=");Serial.println(ROutput);
    lastErr = error;
    lastTime = now;
  }
}

void PWMControl(){
  if(LOutput > 0){
    digitalWrite(TN1, HIGH);
    digitalWrite(TN2, LOW);
  }
  else if(LOutput < 0){
    digitalWrite(TN1, LOW);
    digitalWrite(TN2, HIGH);
  }
  else{
    digitalWrite(TN1, HIGH);
    digitalWrite(TN2, HIGH);
  }
  if(ROutput > 0){
    digitalWrite(TN3, HIGH);
    digitalWrite(TN4, LOW);
  }
  else if(ROutput < 0){   
    digitalWrite(TN3, LOW);
    digitalWrite(TN4, HIGH);
  }
  else{
    digitalWrite(TN3, HIGH);
    digitalWrite(TN4, HIGH);
  }
    analogWrite(ENA, min(255, abs(LOutput) + LMotor_offset));
    analogWrite(ENB, min(255, abs(ROutput) + RMotor_offset));
}

hi i soran i have a problem from my self balancing boot .it cant balancing in two wheel and may component its arduino nano and mpu 6050 drivers h bridge 298 and two dc motor and blutooth hc 05

it cant be balancing i do not know whats the problem please help.