PID tuning by trial-and-error is slow and inconsistent. I now tune from measured step responses and keep every run logged.

First, I characterize the motor and load using open-loop PWM sweeps. That tells me dead zones and saturation points, so I can clamp controller output intelligently.

Then I tune in order: proportional for responsiveness, derivative for damping, and integral last for steady-state error. Integral windup is controlled with output limits and reset conditions when target changes sharply.

The key is plotting target, measured speed, and controller output together. Once you can see overshoot, settling time, and noise sensitivity on one graph, parameter changes become deliberate instead of random.

Logging for the Serial Plotter

The Arduino IDE's Serial Plotter graphs tab- or space-separated values, one sample per line. Printing setpoint, measured, and output on the same line lets me watch all three curves react to a change in real time.

// One line per control-loop iteration. Labels render as a legend in newer IDEs.
void logForPlotter(float setpoint, float measured, float output) {
  Serial.print("setpoint:");
  Serial.print(setpoint);
  Serial.print('\t');
  Serial.print("measured:");
  Serial.print(measured);
  Serial.print('\t');
  Serial.print("output:");
  Serial.println(output);
}

Call it once per loop at a fixed rate. Keep the loop interval constant — inconsistent timing looks like a tuning problem when it is really a scheduling problem.

Starting gains and a mental model

I don't pull starting values out of thin air; I start deliberately small and scale up. A reasonable opening point for a small DC motor speed loop is something like Kp = 1.0, Ki = 0.0, Kd = 0.0, then raise Kp until the response is brisk but starts to ring, before adding a little Kd for damping and finally a small Ki to remove steady-state error.

The mental model I use is the relay / Ziegler–Nichols idea: increase proportional gain until the loop sits at a steady, sustained oscillation, note that gain and the oscillation period, and use them as a reference for where the useful range of Kp, Ki, and Kd lives. I treat classic Ziegler–Nichols numbers as a starting neighborhood, not a final answer — they tend to be aggressive, so I back off from there.

What to watch on the plot rather than any promised numbers: with too much Kp the measured curve overshoots and oscillates around the setpoint; with enough Kd those oscillations damp out faster; with Ki too high you see a slow drift past the setpoint and back (windup) after a large step. Adjust one gain at a time and let the plot tell you what changed.

Sources

  • Imported from the previous version of the site (gaborl.hu). Original publication around 2025-01-20.
  • Åström & Hägglund, PID Controllers: Theory, Design, and Tuning — Ziegler–Nichols reference tuning and anti-windup background.
  • Brett Beauregard, Arduino PID Library (br3ttb/Arduino-PID-Library) — a practical reference implementation with output clamping and integral windup handling.