PID Controller Intro. Controlling a Self-Driving Car to... | by Jeremy Zhang | Towards Data Science

PID controller is a way to regulate an application to smoothly follow a value or a path. I first learnt the idea here, which introduced how to drive a car to follow a specific path. Although a full application explanation can be extensively complex, it summarised the math behind it in a super elegant and concise way, and made the idea can easily extended to many real life problems. In this post, let’s follow the key structure and explore how PID controller works.
In this post, we will follow the path:
P: ProportionalPD: Proportional-DerivativePID: Proportional-Derivative–Integral
P: Proportional
PD: Proportional-Derivative
PID: Proportional-Derivative–Integral
Let’s learn the idea through an implementation of a problem. Suppose we have a robot car object, and we hope the car to control itself to follow a path, which is going to be a straight line in this case, and the only way for the car to control itself is by adjusting its steering or direction so that it can gradually and gracefully approaching the path and follow that path once reach there. In a nutshell, the problem is to:
Control a self-driving car to follow a straight line by changing its steering
The full implementation of the car object is here(explanation of the whole object is behind our scope). Basically the robot object follows the structure:
The set function sets the car’s initial coordinates and direction. And what we need to know of the object is that each time we need to give instruction of steering to the robot so that it can adjust itself to approach the path.
P stands for proportional. The thing is that our robot sits in a 2 dimensional world with coordinates (x, y) . Assume the goal path is y = 0 , then the current error towards the goal is y — 0 = y , and based on the error, we should adjust the car’s steering accordingly. So how to adjust the steering so that we can decrease the error quickly? What would you do if you are driving a car which is away from your target path?
One straight forward way is to adjust steering proportionally to error, which means the further you are away from the target the larger angle you steer your car. With this in mind, we have our first solution:
We adjust the car in n steps, and in each step we steer the car proportionally to error robot.y :
steering = -tau*robot.y
where tau is the weight we assign to the error. (Note: the car moves with speed = 1 , and since timestamp is 1, so distance = speed * 1 = speed )
Let’s plot the trajectory of the car:
We set our robot start from (0, 1) heading horizontally. The trajectory oscillates through out the target path. It looks like that our steering is too drastic that the car overshoots our target and when it tries to steer back it overshoots again. Clearly it gonna be a headache sitting inside such a car. So can we decrease our steering angle when we approach the target?
The question leads to PD control, where D stands for derivative. The idea is that as the error reduces, our car should counter steers in order to avoid overshooting instead of constantly steering down which results in oscillating.
So the new term we should add into our controlling process is δy/δt , where δt is 1, so δy = current_y — last_y . The implementation would be:
The derivative term here is dev = robot.y — prev_cte and tau_d is the weight of it. Result trajectory would look like:
Looks perfect! Now everything looks all good, but in fact this model is not robust. What I mean is what if there is an inherent system defect? What if your wheel at the beginning has a little glitch and always shift to another direction, is PD able to overcome this?
The answer is no. Let’s get to an example:
Here we set_steering_drift adds some noise to the car’s orientation, the result turns to be:
It oscillates far away from our target path. The reason is that although our car is steering towards the goal, but it can not offset the inherent system bias.
To overcome the problem, we need the third term I, which stands for integral, it helps to monitor the error, and if the error is not reduced in a certain period of time, the car should add more steering.
The integral in our case is actually summation of errors over each step, and the implementation would be:
At each step, current error robot.y is added to sum_cte and tau_i is the weight of this term. So by adding this term, the car avoids the chance of oscillating in somewhere far away the target as this term will continuously grow larger and the steering angle would adjust accordingly.
This gives the rectified result:
With the same steering noise, this time the robot car is still able to make its way to the goal.
Another thing that need to notice is the weights of P, I and D. These parameters need to be tuned carefully, and different weight selection can result in significant different performance. You can test and play it out yourself by looking into the full implementation.
All in all, PID controller is actually quite interesting, especially being explained in such intuitive and simple way. You can think of the three components P, I, and D as three different persons in charge of three different aspects of one task:
P: the major force to push the application to the goal state
I: monitors the whole path and help to jump out the dilemma if get stuck
D: monitors P and makes counter measurements when necessary
Lastly, application of PID controller definitely not just limited for this use case and you can apply and explore more fun!