TheSolderBenchnotes from my electronics bench

Log #27 · Sep 27, 2026

PID Control Explained Without Heavy Math

What the P, I and D terms actually do, shown with a robot that follows a line, plus Arduino code and a step-by-step tuning method.

Robotics · 4 min read · Intermediate

PID control is behind cruise control in cars, temperature control in 3D printers, drones that hover in place and robots that follow lines smoothly. The name sounds intimidating, but the idea fits in one sentence: measure how wrong you are, and correct based on how wrong you are now, how wrong you have been, and how fast it is changing. This guide explains each part with a robot example, gives you working code and shows a practical tuning routine.

The setup: error

Every control loop starts with an error, the difference between what you want and what you measure.

error = setpoint − measurement

For a line-following robot, the setpoint is "line centered" (0), and the measurement is the line's position under the sensors, from −100 to +100. For a robot keeping 20 cm from a wall with an ultrasonic sensor, the setpoint is 20 and the measurement is the distance reading.

The controller's job is to turn that error into an action, such as steering or motor speed.

P: react to the present

The proportional term corrects in proportion to the current error:

P = Kp × error

Far off the line? Steer hard. Slightly off? Steer gently. Proportional control alone is a huge improvement over simple on/off steering.

Its weakness: if Kp is high enough to react quickly, the robot overshoots, swings past the line, overcorrects and oscillates. If Kp is low, it is sluggish. And in some systems P alone leaves a small permanent error, because as the error shrinks, so does the correction.

D: react to the trend

The derivative term looks at how fast the error is changing:

D = Kd × (error − previous error)

When the robot is swinging back toward the line quickly, the error is shrinking fast, so the D term pushes against the motion, like a brake. It damps the oscillation that P causes. Think of it as anticipation: "I'm approaching the target quickly, ease off now."

Too much Kd makes the robot twitchy, because it amplifies sensor noise.

I: remember the past

The integral term adds up the error over time:

I = Ki × (sum of all past errors)

If the robot keeps sitting slightly to one side, perhaps because one motor is weaker, the sum keeps growing until the I term is large enough to push it back to center. It removes steady offsets that P and D cannot.

Too much Ki causes slow, large oscillations. The sum can also grow huge while the robot is stuck or lifted off the ground, a problem called integral windup, so real code limits it.

Graph of error over time for P only which oscillates, PD which settles faster, and PID which settles to zero
P alone oscillates. Adding D damps the swings. Adding I removes the last bit of steady error.

Putting it together

correction = Kp × error + Ki × integral + Kd × derivative

Arduino code

This loop runs a PID controller for a line follower. readError() returns the line position and drive() sets the two motors, as in the line-follower guide.

float Kp = 0.6, Ki = 0.0, Kd = 2.5;
const int BASE = 150;

float integral = 0;
int lastError = 0;

void loop() {
  int error = readError();                 // -100 .. +100

  integral += error;
  integral = constrain(integral, -1000, 1000);   // anti-windup
  int derivative = error - lastError;

  float correction = Kp * error + Ki * integral + Kd * derivative;
  lastError = error;

  int left  = constrain(BASE + correction, -255, 255);
  int right = constrain(BASE - correction, -255, 255);
  drive(left, right);

  delay(5);   // keep the loop timing steady
}

Two practical details matter here. First, the loop runs at a fixed rate. The D and I terms depend on time, so if the loop speed changes, the tuning changes too. Second, constrain() keeps the integral and the motor values within safe limits.

Tuning, step by step

There is math for calculating gains, but for hobby robots hands-on tuning works well:

  1. Set Ki and Kd to zero. Start with a small Kp.
  2. Increase Kp until the robot follows the line but oscillates noticeably. Then reduce it by about a third.
  3. Increase Kd gradually until the oscillation dies down and the robot tracks smoothly. If it starts to shake or buzz, back off.
  4. Add a small Ki only if the robot consistently sits to one side on straights. Many line followers never need it.
  5. Raise the base speed and repeat small adjustments. Faster robots usually need more Kd.

Change one value at a time, and write down what you tried. A potentiometer wired to an analog pin, as in our servo knob example, makes live tuning much faster than re-uploading code.

Symptoms and fixes

What you see Likely cause Try
Slow, lazy corrections, loses curves Kp too low Raise Kp
Fast side-to-side wobble Kp too high or Kd too low Lower Kp or raise Kd
Jittery, twitchy motion Kd too high or noisy sensor Lower Kd, filter readings
Always slightly off-center Steady offset Add a small Ki
Slow, big swings that grow Ki too high Lower Ki, check anti-windup

Beyond line following

The same code structure controls a motor's speed using an encoder, holds a balancing robot upright, keeps a heater at a set temperature, or keeps a DC motor turning at a steady speed under changing load. Once you have tuned one PID loop by hand, the others feel familiar.

— TheSolderBench · found a mistake? tell me

More pages from this notebook

How a Line-Following Robot Works

The sensors, logic and code behind a line follower, from a simple two-sensor bang-bang robot to smoother proportional steering.

→ wobbling? lower the gain

Choosing a Chassis for Your First Robot

Two-wheel, four-wheel, tracked or DIY? Compare robot chassis types by steering, terrain and difficulty, and learn how to check motor and battery fit.

→ pick the battery before the chassis

Adding Wheel Encoders to a Robot

How optical and magnetic encoders count wheel rotation, how to read them with interrupts, and how to measure distance and drive straight.

→ count in an interrupt, not in loop()

Using the MPU6050 Accelerometer and Gyroscope

Read tilt and rotation from the MPU6050, convert raw values to g and degrees per second, and combine them with a complementary filter.

→ gyro drifts, accel is noisy, blend both