Back to articles

Watching PID Actually Work: A Night With EncoderMotor_PID_Demo

Electronics workbench with motor and terminal display

"set" said 1.000. "actual" never quite agreed with it. That disagreement is the whole lesson.


I've read the PID control equation more times than I can count, and it always stayed exactly that — an equation, three terms, a clean explanation in a textbook that made theoretical sense and never quite connected to anything I could actually watch happen. Tonight was about fixing that, with one real motor, one real encoder, and the EncoderMotor_PID_Demo sample program already sitting on the RRC Lite controller.

Wiring It Up

Electronics assembly on green cutting mat

Connected a small DC geared motor with its encoder cable into the board's M1 port — motor direction control on PE13/PE14, encoder feedback read on PA0/PA1. Flashed the demo program, pressed the dedicated RESET button on the board (the one wired specifically to the STM32, not the whole system — a detail I actually appreciated in the moment, since it let me reset just the controller without needing to restart anything else), and opened a serial assistant at 115200 baud to watch the log stream in.

Watching Two Numbers Refuse to Agree

Programming in the glow of a laptop

The log settled into two columns scrolling continuously: set, held steady at 1.000 r/s, and actual, the real measured speed coming back from the encoder. And here's the thing that actually made this exercise worth doing, rather than just confirming what I already assumed: actual never sat still at exactly 1.000. It hovered, drifting in a narrow band roughly between 0.960 and 1.116 r/s, never quite landing, never quite straying far, just continuously wobbling around the target the whole time the motor ran.

Watching those two columns side by side, in real time, was the first moment "error" stopped being a symbol in an equation and started being something I could actually point to on a screen — the small, constantly shifting gap between set and actual, on every single log line, is exactly the quantity the whole PID loop exists to fight against, moment to moment, forever, rather than something it solves once and walks away from.

What P, I, and D Are Actually Doing, Watching the Real Function

Looking at the actual pid_controller_update() function running this demo, the three terms map onto something genuinely intuitive once you're watching real numbers move:

P (proportional) reacts to whatever the current error is, right now, in this exact instant — if actual is sitting a good bit below set this very log line, P pushes harder proportionally to how far off it currently is. It's the term with no memory at all, purely "how wrong am I this instant, and how hard should that alone make me push."

I (integral) is where memory enters the picture — it accumulates error over time, specifically to catch the kind of small, persistent bias that P alone tends to leave behind. If actual were consistently sitting just a hair below set for many consecutive log lines — not oscillating, just quietly, persistently under target — P alone might never fully close that small residual gap, because a small steady error produces a proportionally small steady push. I keeps adding up that lingering error over time until the accumulated push is enough to finally close it.

D (derivative) looks at how fast the error itself is changing, not the error's current size — its job is specifically to keep the system from overshooting by sensing "the error is closing very quickly right now" and easing off preemptively, rather than waiting for the error to actually reach zero and only then reacting to the overshoot it's about to cause.

Watching the actual wobble in the actual column, this stopped being three abstract terms and became something like watching a conversation — P reacting to right now, I quietly building pressure against a stubborn lingering gap, D reaching in early to stop the whole system from swinging too far past target in its eagerness to correct.

The Moment That Actually Taught Me Something New

Focused on electronics in a workshop

The demo's speed target alternates — 1.0 r/s, then dropping to 0.5 r/s every ten seconds, back and forth. I hadn't expected this scheduled speed change to be the most interesting part of the evening, but it turned out to be exactly that. Every time set dropped from 1.000 to 0.500, actual didn't jump instantly — there was a genuinely visible, short window where actual was still reading somewhere well above the new 0.500 target, then chasing it downward over the next handful of log lines before settling back into its familiar narrow wobble around the new target value.

That chase — that specific, visible window of actual catching up to a newly-changed set — is transient response, a term I've read in a textbook probably a dozen times without it ever meaning anything more concrete than "the system's behavior right after a change." Watching it happen on a real motor, on a real screen, in real seconds I could actually count, made it something I now genuinely understand rather than something I could merely define if asked on a quiz. There's a real difference between those two, and it took one evening with a fifteen-dollar motor and a scrolling terminal window to close that gap.

avatar-hoang-huynh
Mr. Hoang Huynh
Automotive Cybersecurity Engineer