What Is PID?

A PID controller is a feedback algorithm that continuously adjusts an output (motor voltage) based on the difference between a desired value (target position) and the current value (measured position). That difference is called the error.

output = kP ร— error + kI ร— โˆซerror dt + kD ร— d(error)/dt

Proportional term (kP)

Output is proportional to the current error. If the robot is 10 inches from the target it gets more power than if it is 1 inch away. Simple and effective, but can overshootโ€”the robot coasts past the target.

Derivative term (kD)

Output reacts to the rate of change of error (how fast it is shrinking). As the robot approaches the target it's decelerating, so the error change becomes negative and the D-term subtracts from the output, reducing overshoot. Think of it as "damping."

This is the most important term to tune after kP. Most VEX PID controllers are effectively PD controllers (integral = 0).

Integral term (kI)

Output is proportional to the accumulated error over time. It helps correct persistent small errors (like a robot that consistently stops 0.3" short), but can cause integral windupโ€”runaway oscillation if the robot is held in place. For VEX, it is usually set to 0.

LemLib's Two PIDs

LemLib uses two separate PID controllers per movement:

Both run simultaneously. The lateral PID says "go forward at this speed" and the angular PID says "also turn slightly to stay on course."

The Settings Struct

src/robot.cpplemlib::ControllerSettings lateral_controller(
    8,    // kP โ€“ how aggressively to respond to distance error
    0,    // kI โ€“ integral (disabled)
    51,   // kD โ€“ damping to prevent overshoot
    0,    // anti-windup (not needed with kI=0)
    .5,   // small error range (inches) โ€“ within this = "close enough"
    50,   // small error range timeout (ms) โ€“ stay inside for this long
    1.5, // large error range (inches)
    400, // large error range timeout (ms)
    0     // max acceleration (slew rate) โ€“ 0 = disabled
);

lemlib::ControllerSettings angular_controller(
    2.5,  // kP
    0,    // kI
    19,   // kD
    0,    // anti-windup
    2.4,  // small error range (degrees)
    50,   // small error range timeout (ms)
    4,    // large error range (degrees)
    205, // large error range timeout (ms)
    0     // slew rate
);

How the Exit Condition Works

LemLib's PID doesn't just stop when it first enters the error rangeโ€”it requires the robot to stay inside the range for a timeout. This prevents it from stopping prematurely if the robot passes through the target on an overshoot.

Error over time: 10 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 5 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ 2 โ”‚ โ† large error range (1.5") 1 โ•ฐโ”€โ”€โ•ฎ โ† small error range (0.5") 0 โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ด EXIT after 50 ms

Two error ranges let you exit early on long moves (large range) without sacrificing precision at the end (small range).

Tuning Guide

Step 1 โ€” Start with kP only (kI = kD = 0)

Increase kP until the robot reaches the target but overshoots noticeably. The right starting kP makes the robot fast without being erratic.

โ„น๏ธ
For a 600 RPM (blue) drivetrain, lateral kP usually starts around 5โ€“10. Angular kP usually starts around 2โ€“4. Heavier robots need lower kP.

Step 2 โ€” Increase kD to damp overshoot

Gradually raise kD until overshoot is gone. If the robot starts oscillating (bouncing back and forth), kD is too high. The goal: approach target smoothly, stop cleanly.

For this robot, the final values are kD = 51 (lateral) and kD = 19 (angular) โ€” lateral needs more damping because the robot has more momentum.

Step 3 โ€” Tune exit conditions

Tight tolerances (small error range) give precision but make moves slower. Loose tolerances are faster but the robot may not land exactly on target. Match tolerance to the precision required for each move type.

Step 4 โ€” Enable slew rate (optional)

Slew rate limits how fast the motor output can change, preventing jerky starts that could cause wheel slip. This robot leaves it at 0 (disabled) because the expo drive curve already handles start smoothness.

The pid_tuner Flag

Notice the pid_tuner boolean in robot.cpp. When set to true, it zeros out all exit conditions so the PID never exits earlyโ€”useful for measuring raw response without the timeout interfering.

const bool pid_tuner = false; // Set true to tune, false for competition

lemlib::ControllerSettings lateral_controller(
    8, 0, 51, 0,
    pid_tuner ? 0 : .5,   // small error range
    pid_tuner ? 0 : 50,   // small error range timeout
    pid_tuner ? 0 : 1.5,  // large error range
    pid_tuner ? 0 : 400,  // large error range timeout
    pid_tuner ? 0 : 0     // slew
);
โœ…
Tune lateral and angular PID separately. Call chassis.moveToPoint for lateral tests (go straight, check accuracy). Call chassis.turnToHeading for angular tests (spin in place, check angle). Don't try to tune both at once.