Using PID control with Line Following robot

Hello all,

I have code which makes a nice line following robot and I've calibrated it so it follows the line etc, but it is nowhere near as smooth as I would like.

Ideally I'd like to use PID control as it makes a much more effective robot (using the PID library from arduino, so where you just say set, desired, ki, kp etc)

Here is my initial code, using the constrain and map functions to drive the motors.

//Code for Line Following Robot project
//Robot has to follow a white line on dark floor, drive up a ramp and push a tin can off the top without 
//going off the end.
//Chris Hann
//March 15
//Uses output of Wheatstone bridge to control differential drive


const int sensorMin = 0;
const int sensorMax = 1023;
int leftMotor=5; //Left motor pin
int rightMotor=11; //Right motor Pin
int leftValue = 0; //Intial Motor Value
int rightValue = 0; //Initial Motor Value
int analogPin = 0; //Sensor Head Input
int sensorValue = 0; //Sensor Value
int range = 0; //Range of values for switch loop

void setup() {
  // initialize the Left Motor and Right Motor pins as outputs:
  pinMode(leftMotor, OUTPUT);
  pinMode(rightMotor, OUTPUT); 
  // initialize serial communications:
  Serial.begin(115200);
}

void loop () {
  
   
  // read the analog in value:
sensorValue = analogRead(analogPin);
  // limits the output of the sensor so it doesnt go crazy along the line
sensorValue = constrain(sensorValue, 400, 634);

 //maps the sensor value to left and right motor so it is even either side of line
leftValue = map(sensorValue, 400, 634, 10, 105);   
rightValue = map(sensorValue, 634, 400, 10, 105);
analogWrite(leftMotor, leftValue); 
analogWrite(rightMotor, rightValue); 

 // print the results to the serial monitor:

  Serial.print(sensorValue);
  Serial.print("\t");
  Serial.print(leftValue);
  Serial.print("\t");
  Serial.println(rightValue); 
 
 delay(200);

}

I understand the concepts of the control, I'm just struggling to think how to implement it. Would I need to do it twice (once for each motor), or set the desired output as the middle reading off the sensor and it would do its work??

Thanks :slight_smile:

You probably just need two loops, one to control the overall speed,
one to control the differential component of the drive. You sum and difference
the outputs to get left and right motor drive signals.

Yea, been thinking about this sort of thing.

So using the code I have already it would go something like this:

  • Set up PID control - setpoint/desired = middle value of sensor
  • Read sensor input
  • Run PID control
  • Take the output from the PID and map it to the motors, and do it like I have opposite values going to each motor to control it.

I can then run it and tune Ki and Kp until I get a good response!

Here is how I have edited my code - the part I am not sure about is then sending the desired output to each motor like I have done in my original code by mapping one ‘forward’ and one 'backward.

If I put an outputMin and outputMax in as variables would I be able to do the same?

//Code for Line Following Robot project, 2nd Year MEng
//Robot has to follow a white line on dark floor, drive up a ramp and push a tin can off the top without 
//going off the end.
//Chris Hann
//March 15
//Uses output of Wheatstone bridge to control differential drive

#include <PID_v1.h>

const int sensorMin = 0;
const int sensorMax = 1023;
const int OutputMin = 0;
const int OutputMax = 1023;
int leftMotor=5; //Left motor pin
int rightMotor=11; //Right motor Pin
int leftValue = 0; //Intial Motor Value
int rightValue = 0; //Initial Motor Value
int analogPin = 0; //Sensor Head Input
int sensorValue = 0; //Sensor Value
int range = 0; //Range of values for switch loop
double setPoint; //Desired output from PID control
double Input; //Input from Sensor
double Output; //Output from PID control

//Set up the PID Control
PID myPID(&Input, &Output, &setPoint, 0, 0, 0, DIRECT); 

void setup() {
  // initialize the Left Motor and Right Motor pins as outputs:
  pinMode(leftMotor, OUTPUT);
  pinMode(rightMotor, OUTPUT); 
  // initialize serial communications:
  Serial.begin(115200);
  
 // PID Input and SetPoint
  Input = sensorValue;
  setPoint = 512;
  
  // Turn on the PID Control
  myPID.SetMode(AUTOMATIC);
}

void loop () {
  
   
  // read the analog in value:
sensorValue = analogRead(analogPin);
  // limits the output of the sensor so it doesnt go crazy along the line

myPID.Compute();


 //maps the sensor value to left and right motor so it is even either side of line
leftValue = map(Output, 0, 1023, 10, 105);
rightValue = map(Output, 1023, 0, 10, 105);
analogWrite(leftMotor, leftValue); 
analogWrite(rightMotor, rightValue); 

 // print the results to the serial monitor:

  Serial.print(sensorValue);
  Serial.print("\t");
  Serial.print(leftValue);
  Serial.print("\t");
  Serial.println(rightValue); 
 
 delay(200);


}

Make things differential - set point should be zero for straight ahead, then you do something like

analogWrite (leftMotor, base_value + Output) ;
analogWrite (rightMotor, base_value - Output) ;

The scaling happens in the PID coefficients, the PID output and input are signed values
centred on zero (outputs almost always are because the first thing in a PID loop is
taking the difference between set point and actual value to give a signed error value).

I practice you may need to adjust base_value somehow, and adjust the
steering gain according to forward speed to keep things stable.