A PID controller is a must-have for any control system aiming for stability. I’ve introduced how to implement PID using an Arduino microcontroller. Now, I’ll be applying PID in the design of a beginner robot project, a line follower.

A line follower does what it’s named after. An optical sensor distinguishes a black line from a lighter background. Only two sensors are needed at a minimum but the best line-followers often use more. The information from the sensors is then used by a microcontroller to steer the robot.

The simplest logic for a line follower robot is the following:

Is the black line on the right? Then move to the right

Is the black line on the left? Then move to the left

Is there no black line? (Or line in the middle?) Go

All are black (all sensors trigger)? Stop

This “bang-bang” logic is enough for a slow-moving robot with simple tracks. But you’ll notice something odd with this algorithm. See the video below:

While the robot indeed does follow the line, it bobs left and right as it goes. Here is another example video:

Why does this happen? Consider the illustration below:

As the robot passes a curve, it rotates towards the direction of the line. The rotation or correction is the same throughout. This makes the robot overshoot and thus keeps correcting itself even if it’s out of the curve and in a straight line.

Correcting the robot depending on how far the line is from the center is much better. While this requires more sensors, it is a better design.

Here we have 6 sensors. If the two sensors in the middle trigger, this means the robot goes straight. As the line moves towards the right, the magnitude of correction increases. When the line triggers only the rightmost sensor, the robot has to make the greatest correction.

This algorithm is not bang-bang anymore but is known as *proportional control*.

But as I’ve mentioned in this post, proportional control tends to produce an offset. This is when the controlled variable moves away from its original set point. In the case of our line-follower, our set point is the robot being in the middle of the line. When the robot’s correction overshoots due to a disturbance, it will tend to bob as it did with bang-bang control. See the diagram below:

Since the amount of correction is fixed according to the amount of error (distance from the black line), the robot will find it very hard to move straight in a straight black line again.

Adding Integral and Derivative controllers will drastically improve the performance of the line-follower robot. The integral controller will reduce the offset as it monitors all past error values. As long as the offset is there, the Integral controller will keep producing values to make it zero.

The derivative controller, meanwhile, will look at how fast the error is changing. This effect is particularly evident in different degrees of curves. A sharper curve means the line follower must react faster compared to a flatter curve. The error (distance from the center) changes value as the robot follows the curve. The derivative controller deals with this by adjusting the speed of the turn depending on how fast the error changes.

**PID Control Implementation**

To implement PID control in our line-follower robot, we need to combine the Proportional, Integral, and Derivative components. This combination ensures that the robot adjusts its movement based on current, past, and future errors, leading to smoother and more accurate line following.

**Proportional (P) Control** - The Proportional control component adjusts the robot's direction based on the current distance from the center of the line. The greater the distance, the larger the correction. This helps to reduce the immediate error but can still cause oscillations if used alone.

**Integral (I) Control** - The Integral control component sums up all past errors and adjusts the robot's direction to eliminate any accumulated offset. This helps to correct any persistent deviation from the line over time, ensuring the robot stays centered.

**Derivative (D) Control** - The Derivative control component predicts future errors by analyzing the rate of change of the current error. This helps to dampen the oscillations caused by the Proportional control, leading to smoother movement.

**Combining PID Components**

By combining these three components, we can fine-tune the robot's response to line deviations. The formula for the PID controller is:

where K_{p}, K_{i}, and K_{d} are the coefficients for the Proportional, Integral, and Derivative components, respectively.

**Implementing PID Control on Arduino**

Here's a sample code snippet to implement PID control on an Arduino:

```
long sensor[] = {0, 1, 2, 3, 4}; //leftmost - 0, rightmost - 4
int rmf = 9;
int rmb = 6;
int lmf = 10;
int lmb = 11;
//speeds
int rspeed;
int lspeed;
const int base_speed = 255;
int pos;
long sensor_average;
int sensor_sum;
int button = 3; //to be pressed to find set point
float p;
float i;
float d;
float lp;
float error;
float correction;
float sp;
float Kp = 5;
float Ki = 0;
float Kd = 40;
void pid_calc();
void calc_turn();
void motor_drive(int , int );
void setup()
{
//sensors
pinMode(A0, INPUT);
pinMode(A1, INPUT);
pinMode(A2, INPUT);
pinMode(A3, INPUT);
pinMode(A4, INPUT);
//motors
pinMode(rmf, OUTPUT);
pinMode(rmb, OUTPUT);
pinMode(lmf, OUTPUT);
pinMode(lmb, OUTPUT);
Serial.begin(9600);
//finding set point
while(button)
{
sensor_average = 0;
sensor_sum = 0;
for (int i = -2; i <= 2; i++)
{
sensor[i] = analogRead(i);
sensor_average += sensor[i] * i * 1000; //weighted mean
sensor_sum += int(sensor[i]);
}
pos = int(sensor_average / sensor_sum);
Serial.print(sensor_average);
Serial.print(' ');
Serial.print(sensor_sum);
Serial.print(' ');
Serial.print(pos);
Serial.println();
delay(2000);
}
sp = pos;
}
void loop()
{
pid_calc();
calc_turn();
}
void pid_calc()
{
sensor_average = 0;
sensor_sum = 0;
i = 0;
for(int i = -2; i <= 2; i++)
{
sensor[i]=analogRead(i);
sensor_average = sensor[i]*i*1000; //weighted mean
sensor_sum += sensor[i];
}
pos = int(sensor_average / sensor_sum);
error = pos-sp;
p = error;
i += p;
d = p - lp;
lp = p;
correction = int(Kp*p + Ki*i + Kd*d);
}
void calc_turn()
{
rspeed = base_speed + correction;
lspeed = base_speed - correction;
//restricting speeds of motors between 255 and -255
if (rspeed > 255)
rspeed = 255;
if (lspeed > 255)
lspeed = 255;
if (rspeed < -255)
rspeed = -255;
if (lspeed < -255)
lspeed = -255;
motor_drive(rspeed,lspeed);
}
void motor_drive(int right, int left){
if(right>0)
{
analogWrite(rmf, right);
analogWrite(rmb, 0);
}
else
{
analogWrite(rmf, 0);
analogWrite(rmb, abs(right));
}
if(left>0)
{
analogWrite(lmf, left);
analogWrite(lmb, 0);
}
else
{
analogWrite(lmf, 0);
analogWrite(lmb, abs(left));
}
}
```

Here, the robot uses an array of sensors (*long sensor[] = {0, 1, 2, 3, 4}*) to detect the line and motors controlled by specific pins (*rmf = 9, rmb = 6, lmf = 10, lmb = 11*) to adjust its direction. The setup function initializes the sensors and motors, and a button press determines the set point (sp), representing the desired position of the robot on the line. The PID controller's gains (*Kp, Ki, Kd*) are defined, with initial dummy values for proportional, integral, and derivative terms, respectively.

In the loop, the *pid_calc(*) function calculates the error between the current position (*pos*) and the set point, updating the PID terms and computing the correction. This correction adjusts the motor speeds (*rspeed* and *lspeed*), ensuring the robot follows the line accurately. The *calc_turn()* function determines the appropriate motor speeds based on the correction and limits them within the range of -255 to 255. Finally, *motor_drive(int right, int left)* applies these speeds to the motors, ensuring the robot's movement aligns with the PID controller's output. The result is a smoother and more accurate line-following behavior compared to simpler control methods.

**Tuning PID Coefficients**

Tuning the K_{p}, K_{i}, and K_{d} coefficients is crucial for optimal performance. This process often involves trial and error:

- Start with K
_{p}: Increase K_{p} until the robot starts oscillating around the line. - Adjust K
_{i}: Increase K_{i}to eliminate any steady-state error. - Fine-tune with K
_{d}: Increase K_{d} to dampen the oscillations.

**Conclusion**

Using a PID controller significantly improves the performance of a line-follower robot compared to simple bang-bang control. By considering the current error, accumulated past errors, and the rate of error change, the robot can follow the line more accurately and smoothly. This project not only demonstrates the effectiveness of PID control but also provides a solid foundation for more advanced robotics projects.

Happy building and coding!