Whether you know it or not, PID is found in the machines and gadgets all around you. In the motors of an electric fan or the temperature settings of your water heater, chances are there is some form of PID at work. So what is PID?
Formally, PID Control is a feedback-loop control system that stands for Proportional-Integral-Derivative Control and uses the following formula:
\[ CO(t) = K_{p}e(t) + K_{i}\int_0^t e(T) \,dT + K_{d} e'(t) \]
Now back up. If you don't know calculus, it's really confusing, and even if you are, it may not be exactly clear as to what it's supposed to mean. Hopefully, you'll gain an understanding for how the formula above works and an intuition for why PID does what it does.
A quick note on prerequisites. Some knowledge on calculus would be helpful, but as I will try to explain the calculus concepts as well as I can, all you really need to have down is some algebra and a healthy dose of thinking. There's some physics in the example, but all you really need to know is that acceleration is change in velocity. As for coding, I'll be using pseudocode, but some knowledge in any programming language is always helpful.
________
Control System Basics
Before we get into actual PID, we need to talk about what a control system even is. Whenever a machine needs to get to a certain target value, whether it be speed, pressure, or temperature, it still often have to use a control system to get there.
For example, let's an air tank needs to get to a certain pressure. The compressor can't just immediately go to that pressure; it can only really pump in or release air. While you could use fancy math to decide exactly how much air to pump in to get a specific pressure, the air tank can just determine whether to pump in or release air based on the current pressure and continually adjust until it reaches that desired pressure. This is called a feedback loop, where the control system, be it PID or something else, outputs a value to the plant, or the thing that does the physical actuation or motion. The plant then returns its current state back to the control system for it to determine another output, hopefully narrowing it down to the target value.
Let's get down some terminology. The setpoint (SP) is just a fancy name for the target value, and the proccess variable (PV) is an even fancier name for the current value. The difference between the setpoint and process variable is the error (e). The goal of any control system is to reduce the error to as close to 0 as possible, which would mean \( PV = SP \). Because all these values may be constatly changing over time, it's usually written as a function of time instead of as a variable. You can drag the bars on the graph to see how it works.
\[ e(t) = SP(t) - PV(t) \]
It's extremely important that you get the concept of the error. The error will be positive if \( PV < SP \) and negative if \( PV > SP \), and the larger the difference between the two, the farther from 0 the error will be.
Before delving into actual PID, let's take a look at a simpler but less effective control system as a example: bang-bang (BB) control, also known as on-off control. In BB control, when the error is positive, the controller output (CO) will be set to positive constant, and when the error is negative, the controller output will be set to a negative constant. This means that our controller only has two discreet states. (Technically 3, but an error of zero can be ignored in real applications). Think about it for a second: the output is basically pushing against the direction of the error to reduce it.
\[ CO(t) = \begin{cases} -C, & e(t)<0 \\ C, & e(t)>0 \\ 0, & e(t)=0 \end{cases} \]
Let's use a car as our model, which will try to get to a certain position. Our car will be able to control its acceleration, but cannot directly control its speed or position. It's kind of like how we apply gas or the brakes to accelerate or decelerate an actual car. (Let's pretend that cruise control and self-driving cars don't exist.)
Take a look at BB Control in action in this simulation! You can set the setpoint with the slider under the car, and then press play to watch the car go. The vectors above the car represent the direction and magnitude of the error and acceleration. Remember, the error is just the signed distance between the car and setpoint, and you should be able to see how it relates with the acceleration. Try playing around will the acceleration slider and see what happens!

Controls
As you can see, BB Control tends to be kind of a blunt hammer. With just 2 states, it tends to overshoot and then over-correct itself, oscillating out of control. Even changing the acceleration doesn't remedy the problem as it just doesn't seem to be able to settle on the setpoint whether it goes fast or slow. We need something that has more precision and can slow down as it approaches a setpoint. As a result, BB control is only used for things like thermostats, where efficiency and precision aren't too important.
While the animations are nice, graphs are a better way of visualising the motion of the car over. The red line will represent the PV, and the blue graph will represent the SP.

Controls
With a graph, the oscillatory motion becomes much clearer and easier to quantify. As we introduce PID, pay attention to the shape of the graph and how it changes as you adjust parameters.
Often, we will want to use control block diagrams to show the flow of a control system. In this diagram, the control system calculates the error, and figures out whether to output a positive or negative value. After sending this value to the plant, it will spit out its PV for the controller to calculate the error all over again.
As we move on to more complicated control systems, make sure you understand how the control loop feeds into itself, continually attempting to drive the error to 0.
________
P Controller: Getting There
Now let's start with just a P controller and leave out the I and D. The P stands for proportional, meaning the control output will be proportional to the error. This means that if the error is very large, the controller output will likewise be large, and if the error is small, the output will be small. The same applies for a negative error, outputing a negative value instead. Theoretically, this should help limit the oscillation from BB control, as it wouldn't keep pushing full speed as the error approaches 0, right?
In mathematical terms, we could set the controller output structly equal to the error. However, we will probably want to multiply the error by some constant to scale it up or down, tuning it to just the right value. After all, a car's position would be much different from a pool's water level, so different constants would be needed. This constant that we multiply by is called the proportional gain or p-gain, notated with \(K_{p}\).
Now we have the first part of the equation for a P controller. As you can see, the controllers output (\(CO(t)\)) is directly proportional to the error.
\[ CO(t) = K_{p}e(t) \]
Try getting a feel for a P controller in this simulation, and look out for how the error relates to the acceleration. What happens when you increase and decrease the proportional gain?

Controls
Eh, not much better is it? P Control is slightly advantageous to BB-Control as it oscillates less frequently, making it preferrable where some stability is needed. (Fun fact: the oscillations will have the same frequency regardless of what starting error!) However, it never quite reaches the setpoint, which is a problem for us trying to minimize ther error as quickly and efficiently as possible.
Here is where we introduce damping, or a loss of energy over time. Almost any mechanical system will experience some sort of damping; in our example, it could be caused by air resistance or kinetic friction. These resistive forces are often out of our control, but we can still use it to our advantage, adjusting our p-gain to account for it.
We'll use a pretty simple model for damping, just multiplying the velocity by a constant. A damping of 0 means that the velocity will not be changed, whereas a damping of 1 means that the car will not move at all. Damping should be close to 0, but it's definitely not negligible. Technically, this is not how friction works, but it gets the point across. Other model may have different types of damping such as heat, where heat is constantly escaping out of the system.
We will also implement a minimum acceleration. This is a small threshold that the acceleration must cross over in order to start moving. This could caused by static friction, imperfections in the motors, or backlash somewhere. (Backlash is where components don't fit to gether exactly due to there needing to be a bit of wiggle room.) Like damping, it is often an uncontrollable factor and we have to tune our gains to overcome it.
Let's look at an example! Adjust the minimum acceleration and damping sliders and see how the curve behaves. Remember, in the real world, we can only do so much to try to limit the resistive forces, and we have to tune our gains to overcome it.

Controls
Now we're getting somewhere! As you can see damping forces can actually be helpful for us to narrow down onto one value! However, if the damping and minimum acceleration are substantial, then we might run into another problem: really slow convergence onto the setpoint and possibly a steady-state error, a persistent non-zero error at the end.
Steady state error can be quite a nuisance where precision is needed. Since the error is very close to 0, the controller will output a very small value. In a perfect world, this would be no problem, but we have friction to get in the way in the real world. The small output from the P-controller is unable to overcome the resistive forces and we end up with slow convergence or even that steady-state error in the worst case.
However, not all hope is lost! The integral part of our PID controller is designed to help that.
Let's take a look at our current progress through a control diagram. You should be able to trace the feedback loop.
________
PI Controller: A Little Push
Now, the integral, which is what the I in PI stands for, is a pretty complex idea. If you are already familiar with calculus, feel free to skim or skip ahead. Otherwise, here's the gist.
Put simply, the integral is the area under the graph of a function. Integrals are often notated with a weird S-shape:
\[ \int_a^b f(x) \, dx \]
The two little numbers are the bounds of our integral. It just means the area from x=a to x=b. You can just ignore the little \(dx\) for now. Let's say that the following graph represents the function \(f(x)\). The integral is just the area between the curve and the x-axis.

Simple right? There's one caveat. If the curve goes under the x-axis, the integral on that interval will be negative. The total integral of an interval is the sum of the areas of its little segments.

You can also think of the integral as an accumulation of the function, a way to sum up all the values of a function over a certain range. For now, we're going to put off actually calculuating the integral to a later section and just assume that we can calculate it.
If you're still a bit confused about the concept, I would recommend checking out 3Blue1Brown and their Essence of Calculus series. Chapter 8 talks specifically about integrals, but the entire series is great for getting an intuition for basic calculus.
We are now ready for PI COntrol. For our updated PI control system, here is the equation:
\[ CO(t) = K_{p}e(t) + K_{i}\int_0^t e(T) \, dT \]
Now slow down! As you can see, we've taken the P term from our P Controller and just added an integral term to the end. We also have a similar \(K_{i}\) constant, which we multiply with the integral in order to give it more or less weight. This is called the integral gain
Now you might be thinking, what is the integral supposed to do? Play around in the simulation and see if you can figure it out yourself before the explanation. Because the proportional and integral terms are separate, I've separated the components of acceleration from P and I. Try seeing if you can overcome the frictional forces only adjusting the integral term.

Controls
Have an idea of what the I term does? Remember, the integral is like a sum of the values of a function. Over time, it sums up all the cumulative errors, and if the error is positive, these values will add up in the integral. The same thing will happen for negative errors but in the opposite direction.
Can you see how this could get rid of our pesky steady-state error? With PI Control, the steady state error will continue to increase the area under the curve even if the error stays the same. As a result, it will gruadually drive the acceleration to overcome friction. In a sense, the integral term is way to keep pushing the controller. By the time the error is actually close enough to 0, the integral stops changing, and the acceleration components should balance out.
That's why for many systems, PI controllers are enough, from temperature control to fluid flow regulation. For its simplicity, PI controllers are the default for many industrial applications.
Tuning it is a big balancing act, as you could probably tell. An i-gain too small could lead to the error being reduced too slowly, with a steady state error basically coming back. On the other hand, if the i-gain is too big, it could spiral out of control, overshooting so much as to start oscillating or even diverging. Even though it's exxagerated in this model, you can see how the i-gain causes the controller to wobble quite a bit, which is a problem with PI Controllers. Generally, some overshoot is good in a control system as it gets to the setpoint faster and allows the system to adjust, but sometimes, speed and minimizing overshoot are most important, and that's where the D term comes in.
But first, we can add another element to our control block diagram:
________
PID Controller: Predicting the Future
For those applications where a simple PI controller just won't cut it, here's where the Derivative "D" term decomes useful.
The derivative is another one of those calculus terms, but this one is a little simpler. Graphically, it's just the slope of the tangent line at a given point on a function, denoted by \( f'(x) \). You can think of it as how the dependent variable changes as the independent variable changes. Again, 3Blue1Brown's Essence of Calculus series highly recommended.
Here's a little Desmos interactive to get a feel for derivatives. The blue graph is the function we're taking the derivative of, and notice how the tangent slope changes as you drag around the little blue point. The red graph actually represents the derivative of the blue graph. When the red graph is positive, the slope of the blue graph is positive, and vice versa.
Like before, we will add an additional derivative term to our equation: our final equation for PID! Again, the derivative is multiplied by the derivative gain. Can you guess what the derivative will do?
\[ CO(t) = K_{p}e(t) + K_{i}\int_0^t e(T) \,dT + K_{d} e'(t) \]
If you read the section title, you can probably guess. The derivative term is there to rein the controller back, to tell the controller to slow down before it creashes into a lamppost or something. Still not sure? Try a simulation:

Controls
Taking a look at the vectors, you can see that the derivative action often works against the error, which seems at first counterintuitive, but balancing it with the P and I terms can achieve a nice effect. As the error is decreasing, meaning the derivative is negative, the derivative term itself will be negative, pushing against the motion of the car. The larger the speed, the harder the derivative term pushes. This effectively limits the speed from going too high, so the overshoot will not be as large as the error approaches 0. While there may be a longer response time with PID compared to PI, there is significantly more stability and a lot less oscillation. It's basically artificial damping!
The derivative term has a fatal flaw though: noise. With any mechical system, there will be some random uncontrollable variations in the data whether it be from shaking, external forces, or just imprecise measurements. Because the derivative term is based on how the error is changing, small alterations in the measurements could be amplified by the derivative term and increase instability.
To simulate noise, let's multiply the alter the PV randomly by a small amount.

Controls
Try turning on and off the Derivative gain. You can see that the derivative component fluctuates wildly as the data trembles, creating lots of instability. In cases where the data may vary like that, it's probably better to just use a PI controller.
And that's our overview of PID! To recap, here's a little control block diagram:
But now that we've learned the theoretical bits, time to figure out how to actually implement what we've learned.
________
Implementing PID
Knowing PID is one thing, but implementing it is just a little bit more. You know how we just assumed that we could calculate the integral and derivative terms earlier? Well, here's where that comes into play.
Let's start with the integral. How do we even begin to calculate it? Well integrals are calculated with something called a Riemann Sum. Take the range where you want to integrate, and split it up into discrete chunks with a width of \( dx \). Now over each of those chunks, draw a rectangle with a width of dx and a height of f(x). The sum of each of the areas of the rectangles (accounting for negativity) approximates the integral. As the interval is split into more pieces (and \( dx \) becomes smaller), this approximation gets better. This is what the integral notation means: the curvy sign represents a sum, adding up many heights on the function and multiplying them by width \( dx \) to get the area of each rectangle.
In our program, we'll take advantage of the small time slices. When PID is actually used, the feedback loop doesn't go infinitely fast, so each time the controller cycles, we can update the integral by just adding the another small slice of area. For our purposes, there are 3 ways to do this. We'll use the Left Riemann Sums since they're the easiest to program, but all three mathods approximate the integral given sufficiently small timesteps:
For the derivative, it's the same idea. the \( f'(x) \) notation is actually shorthand for \( \frac{df(x)}{dx} \), or the change in the function over the change in x, which should be familiar as the formula for slope. At each time step of our feedcback loop, our derivative can be calculated as the change in \( e(t) \) (current error - last error) divided by the change in time, or the time step. This is effectively taking the slope of the error between each iteration. You can see that this blue slope approximates the green true derivative as you drag timestep smaller:
Let's get coding! I'll be using pseudocode, but you can probably apply the general idea to all programming languages.
First we just need to set up our variables and set up our update function that will be called in the feedback loop:
Kp = 1.0
Ki = 0.3
Kd = 0.5
update(sp, pv, dt)
return something
Each time we update, we want to calculate an error based on the setpoint and process variable. The proccess variable would come from whatever plant you're using. That could be from the pressure guage on an air tank or a speedometer, anything to measure your current PV. The plant would then take whatever is returned from this function to process. As for dt, it's just the time between cycles. You could have it be set on a timer to run on a certain frequency or have it run as often as possible and measure with an internal clock.
First, let's implement P:
Kp = 1.0
Ki = 0.3
Kd = 0.5
update(sp, pv, dt)
error = sp - pv
pTerm = Kp * error
return pTerm
Simple. We're just taking the error and multiplying it by our proportional gain. Next, I:
Kp = 1.0
Ki = 0.3
Kd = 0.5
integral = 0
update(sp, pv, dt)
error = sp - pv
integral += error * dt
pTerm = Kp * error
iTerm = Ki * integral
return pTerm + iTerm
The integral is something that is persistent throughout the PID process, so we should define it outside of the function. Each time, we add the area of a little rectangle to approximate the integral. Note that if you want to use Right or Trapezoidal Riemann Sums, you have to keep track of the last error as well, and use that to help calculate the area of the rectangles or trapezoids respectively. We will have to use the last error for the derivative anyway.
Speaking of which, the derivative:
Kp = 1.0
Ki = 0.3
Kd = 0.5
integral = 0
lastError = 0
update(sp, pv, dt)
error = sp - pv
integral += error * dt
derivative = (error - lastError) / dt
pTerm = Kp * error
iTerm = Ki * integral
dTerm = Kd * derivative
return pTerm + iTerm + dTerm
And that's it. Change in error over change in time. That's it for coding PID. Of course there's much more you can do such as a bias terms and feed-forward, and there may be specific tweaks in specific languages such as in object-oriented languages. I recommend doing some of your own research on PID and even take a look at some examples. It never hurts to take a look at the work others have done!
If you're interested in trying out some PID programming, check out JanisMac's Control Challenges. See if you can implement PID and even try tuning it!
________
Tuning PID
Now we come to the crux of PID: tuning. Even if you have the most advanced control system in the world, it will amount to nothing if you don't tune it to its specific condition. For PID, you have to tune the three gains, adjusting them to suit whatever need your system requires.
Because the constants of PID are not connected to any physical properties, they must be determined experimentally rather than analytically, and these values often vary wildly with different systems. There are many algorithms that take experimental data to tune optimal gains such as the Ziegler–Nichols method as well as many softwares that can automatically find the optimal PID parameters. You can find more about these online. For this, we'll just go with manual tuning. Note that this doesn't mean everything is done by hand, just the determination of the gains; almost all applications of PID are electronic, and it's very useful to use softwhere that can graph data for you to look at.
I've made a little Java program to help graph and PID. Check it out here if you're interested!
Also, remember that different systems will have different needs to meet and may require different tuning methods. This will just be a very general guide (that I totally did not steal from Wikipedia), but always use application-specific knowledge and past experience as guides.
- Set all the gains to 0 as a baseline. The plant should have no response.
- Slowly increase the p-gain until the PV begins to oscillate around the setpoint in a wave. Note that you're not dynamically changing the gains as they run, just adjusting the gains in between trials.
- Approximately cut the p-gain in half. This should lead to a decay in the amplitude.
- Now slowly increase the i-gain to overcome any steady-state error or damping. The error should hopefully converge onto 0. Be careful, as too much i-gain can lead to oscillation or divergence.
- If needed, slowly increase the d-gain until satisfaction. Note that while this should decrease the overshoot and make it less wobbly, it could increase the response time if too high. It all depends on what you value: precision or speed.
- Adjust your gains or do it all over again until its as perfect as you need!
These animations illustrate how tuning different values might change the shape of the curve:

![]() |
![]() |
![]() |
And remember, if there is a better way to tune for your specific application, go for that! Sometimes there are defaults that you can set the gains to and then adjust from there.
This article is just a basic overview of the process of PID, but I hope it sparked some interest, if not at least educated you a bit. There are always more levels to go down, more things you can do with PID and even controls systems far beyond with crazy linear algebra. I encourage you to go ahead and explore for yourself if you're interested!
Finally, here's one last simulation with everything I can think of:
