The Hidden Magic of PID Controllers
An interactive dive into proportional, integral, derivative (PID) control systems.
You’ve probably benefited from multiple PID controllers today without realizing it. They’re in your car’s cruise control, your home thermostat, and the robotic arm that assembled your phone. PID controllers are everywhere because they provide an elegantly simple solution to a common problem: how do you get something to go where you want, and stay there?
In this post, we’re going to build an intuition for how PID controllers work. We’ll start with the simplest possible controller and gradually add complexity. By the end, you’ll understand not just what each term does, but why it’s needed.
Pointer to Target
Let’s start with a concrete example. Imagine you have a motor attached to a pointer, like a speedometer needle. Your goal is to get the pointer to a target angle. Sounds simple enough: just power the motor until it arrives, right?
The catch is inertia. The pointer has mass, and mass resists changes in motion. If you cut the power the moment you reach the target, your momentum carries you right past it.
Try dragging the orange handle to set a new target angle and see how the system responds:
The motor has no idea it should slow down as it approaches the target! It provides full torque up to the target angle, then switches to full reverse torque. Without a way to slow down near the target, the pointer will oscillate right around it forever. We need a smarter control system.
Proportional Control
The first insight is simple: the further you are from the target, the harder you should push. If you’re 90° away, apply a lot of power. If you’re only 2° away, apply just a little. This is called proportional control because the output is proportional to the error.
Where , and is a gain that determines how aggressive the response is.
Here’s how the control loop works:
The controller continuously compares setpoint vs measured position, calculates error, and outputs torque command
The loop runs 60 times per second:
- Measure the current position (feedback)
- Compute:
- Output: torque command proportional to the error
This creates a closed-loop system: the controller constantly adjusts based on what it observes. Now let’s see it in action:
Play with the slider and target angle. Notice that there’s a tradeoff:
- Too low: Sluggish response, and any resistance will leave you stuck short of the target (steady-state error)
- Too high: Fast response, but overshoots and oscillates—potentially unstable
This is the fundamental tradeoff of proportional control. Higher gain gets you there faster but makes it more likely to overshoot and oscillate aggressively.
Derivative Control
The proportional term looks at where you are. But what if we could also look at how fast you’re approaching the target? If you’re racing toward the target at high speed, you should start braking early. If you’re crawling toward it, you don’t need as much braking.
This is what the derivative term does: it responds to the rate of change of the error. This is the “D” in PID.
If you’re approaching the target quickly, the derivative term applies a braking force. The purple arrow shows the contribution of the D term to the total output.
Like magic, the oscillation is almost entirely gone! The system now settles smoothly to the target instead of bouncing back and forth. Try setting to 0 to see the difference: the oscillation returns immediately.
Steady-State Error
We’ve solved the oscillation problem with PD control, but there’s another issue lurking. Let’s add a mass to the end of the pointer. Watch what happens: the pointer can’t reach the target:
This is steady-state error. Proportional control only pushes when there’s error. To counteract the constant pull of gravity, some error must remain — otherwise there’s nothing to push against. Higher shrinks the gap but can’t close it.
Neither P nor D can fix this problem. We need something that remembers the past and builds up force over time.
Integral Control
Here’s the key insight: if the error persists, we should keep adding force. Even a small error, given enough time, should result in a large correction. This is what the integral term does: it sums up all the past errors.
The integral term keeps track of persistent error. If an error has been there for a while, the integral builds up and eventually overwhelms the disturbance. Watch the term increase to counteract the constant pull of gravity:
The integral term eliminates steady-state error beautifully! Now the pointer reaches the target exactly, even with the mass pulling it down.
Temperature Control
Let’s see how the same principles apply to a completely different system: controlling the temperature of an oven. This is close to what you’d find in a real kitchen oven, a reflow soldering station, or an industrial furnace.
Notice something interesting: this controller only uses P and I - no derivative term at all! Why does that work here?
The key is that thermal systems have natural damping. Heat dissipates gradually, and the thermal mass of the oven acts like a built-in low-pass filter. There’s no “momentum” that would cause the temperature to overshoot wildly like our motor pointer. The system is inherently sluggish.
This sluggishness means:
- D isn’t needed for damping—the physics provides it
- I is essential—we need to eliminate steady-state error from heat loss
- P provides the basic responsiveness
Try opening the door to see disturbance rejection! Watch how the integral term builds up to compensate for the increased heat loss.
Inverted Pendulum
For our final example, let’s look at one of the most dramatic demonstrations of PID control: balancing an inverted pendulum. This is the classic “broom balancing on your palm” problem, and it’s the same principle behind Segways and rocket landing.
Click the cart to poke it
The pendulum balances! But wait, watch what happens over time. Click the cart to poke it and observe: eventually the cart crashes into the edge of the track. The PD controller is doing its job of keeping the pendulum upright, but it has no concept of where the cart is on the track.
This is the classic problem of cascaded control. We have one controller for the angle, but we need another controller to manage position. The solution is to add a second PD loop:
Both loops use Σ to compute error. The position loop's bias shifts the nominal 0° to create a target θ.
Without the outer position loop, the inner angle loop always tries to keep the pendulum at 0° (vertical). The additional outer loop biases that target slightly off-vertical to push the cart back toward center:
// Outer loop: where should the pendulum lean?
positionError = targetPosition - cartPosition
targetAngle = Kp_pos * positionError + Kd_pos * cartVelocity
// Inner loop: achieve that lean angle
angleError = targetAngle - pendulumAngle // NOT just "0 - angle"!
motorForce = Kp_angle * angleError + Kd_angle * angularVelocity
If the cart drifts right, the position loop outputs a small negative target angle, telling the pendulum to lean slightly left. This creates a force that pushes the cart back. The inner angle loop doesn’t care why it’s being told to lean; it just does its job of achieving that angle smoothly. Try moving around the target position and play with the coefficients to see the response:
Click cart to poke · Drag handle to set target
Now we have two control loops working together:
- Angle Control (Kpθ, Kdθ): Fast inner loop that keeps the pendulum at whatever angle it’s told
- Position Control (Kpx, Kdx): Slower outer loop that adjusts that target angle to control cart position
The position controller adds a gentle bias to the angle controller’s target, nudging the cart back toward center without destabilizing the balance. Try removing the derivative term from the angle controller and see how quickly the system becomes unstable.
This cascaded structure - an inner loop for fast dynamics (angle) and an outer loop for slower dynamics (position) - is extremely common in real control systems. Quadcopters use it (attitude inner loop, position outer loop), as do robotic arms and self-balancing robots.
The PID Controller
Now you’ve seen all three terms in action:
| Term | Looks at | Effect | Fixes | Can cause |
|---|---|---|---|---|
| P | Present error | Push toward target | Slow response | Overshoot, oscillation |
| D | Future error (rate) | Brake before arrival | Overshoot, oscillation | Noise sensitivity |
| I | Past error (sum) | Eliminate steady-state error | Steady-state error | Windup, slow response |
Tuning Challenge
The art of PID control is choosing Kp, Ki, and Kd to work in harmony. Each system is different: the mass of the pointer, the power of the motor, the amount of friction all affect what gains work best.
Can you tune a PID controller? Try to tune the controller for each challenge. Can you find values that work for all challenges?
Hold within 1° of the target for 0.5s to complete
Comparison Playground
Finally, here’s a playground where you can compare P, PD, PI, and PID controllers side-by-side. All four controllers are trying to reach the same target—watch how they differ:
Compare P, PD, and PID responses
Click the Randomize Target button to see how each handles step changes. Increase the Mass to add a disturbance and observe:
- P (blue): Gets close but oscillates, and has steady-state error when mass is added
- PD (purple): Settles quickly with minimal overshoot, but still has steady-state error with mass
- PI (amber): Eliminates steady-state error but oscillates more than PD—no damping from D term
- PID (green): The best of both worlds—smooth response AND no steady-state error
Notice how PI and PID both eliminate steady-state error (they reach the target exactly), but PID does it more smoothly because the D term provides damping. This is the power of combining all three terms: P provides responsiveness, D provides damping, and I eliminates persistent error.
What We Learned
Let’s recap the intuition we’ve built:
Proportional (P): “Push toward the target, harder when far away”
- Good for: fast initial response
- Bad at: reaching the target exactly (steady-state error), not overshooting
Derivative (D): “If we’re approaching fast, start braking”
- Good for: reducing overshoot, stabilizing oscillation
- Bad at: handling noisy measurements
Integral (I): “If we’ve been wrong for a while, try harder”
- Good for: eliminating persistent error, overcoming disturbances
- Bad at: responding quickly (can cause windup and slow oscillation)
The magic of PID is that these three simple terms, combined properly, can control an enormous variety of systems—from car engines to chemical plants to the autopilot in aircraft.
Going Further
PID is just the beginning. Once you understand these fundamentals, there’s a whole world of control theory to explore:
- Feedforward control: Don’t just react to error—predict what input you’ll need
- Gain scheduling: Use different gains depending on operating conditions
- Model predictive control: Optimize over a future time horizon
- State-space control: Control multiple variables simultaneously
But PID remains the workhorse of industrial control for good reason: it’s simple, robust, and with proper tuning, good enough for the vast majority of applications.
This post was inspired by the interactive explanations at samwho.dev and ciechanow.ski.