Hi,
I have a three wheel differential drive that I am trying to drive straight using only encoders(no other sensor). I am using a teensy 3.2 microcontroller, a sabertooth 2x12 motor driver, HC-05 Bluetooth module, two dc motors, and two external rotary/incremental encoders having 360ppr. I am new to Arduino and learning slowly.
The problem is that I am unable to get a stable RPM value for my motors. I have tried adjusting the gains but the result is always varying in a range of 140-160 RPM. I started with P first then D and then I, but got no success. After changing the gains again and again the best I got was a constant oscillation of RPM from 133 to 166 and some 150 values in between. Increasing D does not damp the oscillation. I am getting the same result with both motors but bigger amplitude in motor 2.
As a result of there is a difference in the speed of wheels every time and the bot deviates from a straight path. I am stuck here so please help. Here is my code and sorry for any mistakes. Do correct me.
#include <SoftwareSerial.h>
#include <SabertoothSimplified.h>
SoftwareSerial SWSerial1(4, 2); //Software Serial to communicate with the motor controller
SoftwareSerial SWSerial2(0,1); //communicating with HC-05
SabertoothSimplified ST1(SWSerial1);
int pinA1 = 11;
int pinB1 = 12;
int pinA2 = 7;
int pinB2 = 8;
int count1 = 0, count2 = 0;
int prevCount1=0, prevCount2 = 0;
unsigned long prevTime1=0,prevTime2=0,prevTime3=0,prevTime4=0,prevTime5=0;
float reqRPM1 = 0,reqRPM2 = 0,reqTheta = 0 ;
float actRPM1 =0,actRPM2= 0;
float baseSpeed = 0,I1=0,II1=0,II2 = 0,I2 = 0,I3=0,II3=0,motorInput1 = 0,motorInput2=0,correction = 0;
float kp1 = 0.25,ki1 = 0.015,ki2 = 0.008,kp3 = 1.2,ki3=0.01;
float error1=0,error2 = 0, errorRPM = 0,errorTheta = 0,lr = 0;
float Theta =0, DTheta = 0,lTheta = 0;
String t;
char driveBot;
#define TWOPI 6.2831853072
void Check2() // interrupt to count encoder2 ticks
{
if(digitalRead(pinB2) == digitalRead(pinA2))
count2--;
else
count2++;
}
void Check1() // interrupt to count encoder1 ticks
{
if(digitalRead(pinB1) == digitalRead(pinA1))
count1++;
else
count1--;
}
void setup()
{
Serial.begin(115200);
SWSerial1.begin(9600);
SWSerial2.begin(115200);
ST1.motor(2,0);
delay(1000);
pinMode(pinA1,INPUT);
pinMode(pinB1,INPUT);
pinMode(pinA2,INPUT);
pinMode(pinB2,INPUT);
attachInterrupt(digitalPinToInterrupt(pinA1),Check1,RISING);
attachInterrupt(digitalPinToInterrupt(pinA2),Check2,RISING);
baseSpeed = reqRPM1*kp1; // providing a minimum speed at startup
ST1.motor(2,baseSpeed);
ST1.motor(1,baseSpeed);
prevTime1 = micros();
prevTime2 = micros();
prevTime3 = micros();
prevTime4 = micros();
prevTime5 = micros();
}
void loop() {
if(SWSerial2.available())
{
t = SWSerial2.readStringUntil('\n');
driveBot = t.charAt(0);
SWSerial2.print(" driveBot = ");
SWSerial2.print(driveBot);
kp3 = (t.substring(2,6).toFloat());
SWSerial2.print(" kp3 = ");
SWSerial2.println(kp3);
ki3 = (t.substring(8).toFloat());
SWSerial2.print(" ki3 = ");
SWSerial2.println(ki3);
//resetting values each time I start driving
ST1.motor(1,0);
ST1.motor(2,0);
error1=0,error2=0,errorRPM=0;
I1=0,II1=0,II2 = 0,I2 = 0,I3=0,II3=0,motorInput1 = 0,motorInput2=0;
}
if(driveBot == 'f') reqRPM1 = -150,reqRPM2 = -150;
if(driveBot == 'b') reqRPM1 = 150,reqRPM2 = 150;
if(driveBot == 's')
{
reqRPM1 = 0,reqRPM2 = 0,Theta=0;
ST1.motor(1,0);
ST1.motor(2,0);
}
delay(100);
Drive();
}
void Drive()
{
if((micros()-prevTime1)>1000)
{
actRPM1 = getRPM1();
Pid1();
actRPM2 = getRPM2();
Pid2();
Pid3();
ST1.motor(1,motorInput1);
ST1.motor(2,motorInput2);
prevTime1 = micros();
}
}
int getRPM1()
{
long pps1 = long(count1 - prevCount1);
float ppr1 = 360;
prevTime2 = micros() - prevTime3;
double tt = prevTime2 / 6000.0 ;
double actRPM1 = ((pps1*10000.0)/(tt*ppr1));
prevTime3 = micros();
SWSerial2.print(" rpm1= " );
SWSerial2.print(count1);
prevCount1 = count1;
return actRPM1;
}
int getRPM2()
{
long pps2 = long(count2 - prevCount2);
float ppr2 = 360;
prevTime4 = micros() - prevTime5;
double tt = prevTime4 / 6000.0 ;
double actRPM2 = ((pps2*10000.0)/(tt*ppr2));
prevTime5 = micros();
SWSerial2.print(" rpm2= " );
SWSerial2.print(count2);
prevCount2 = count2;
return actRPM2;
}
void Pid1()
{
error1 = reqRPM1 - actRPM1;
I1 = error1 * ki1;
II1 = II1 + I1;
if(II1 > 127)II1 = 127;
if(II1 < -127) II1 = -127;
motorInput1 = baseSpeed + II1;
if(motorInput1 > 127) motorInput1 = 127;
}
void Pid2()
{
error2 = reqRPM2 - actRPM2;
I2 = error2 * ki2;
II2 = II2 + I2;
if(II2 > 127)II2 = 127;
if(II2 < -127) II2 = -127;
motorInput2 = baseSpeed + II2;
if(motorInput2 > 127) motorInput2 = 127;
}
void Pid3()
{
errorRPM = actRPM1-actRPM2;
DTheta=((100*3.14159265359*errorRPM)/348);
Theta = Theta + atan(DTheta);
errorTheta = reqTheta - Theta;
I3 = errorTheta * ki3;
II3 = II3 + I3;
correction = kp3*errorTheta+II3;
if(t != 's') lTheta = Theta;
SWSerial2.print(" LastTheta = ");
SWSerial2.print(lTheta);
SWSerial2.print(" Theta = ");
SWSerial2.println(Theta);
motorInput1 = motorInput1 + correction;
}
The Pid3 function keeps the bot straight in the beginning but after a while the difference in speed causes much deviation and it starts to deviate.