Bench note
Loop timing that matches your sensor's real bandwidth
The loop you write versus the loop you get
Your control loop's delay(10) suggests a hundred-hertz update, but your MPU-6050 is still clearing its FIFO from three cycles ago, your moving average spans forty milliseconds, and the PID output lands sometime after the motor's inertia makes the setpoint irrelevant. Firmware that ignores sensor bandwidth writes checks the hardware can't cash.
I learned this tuning a line-following robot that twitched at every edge. The code claimed 50 Hz; the IMU's low-pass filter added 22 ms of its own lag, the I²C read took another 8 ms when the bus stretched, and by the time the PWM updated, we were reacting to data from two frames ago. Closed-loop tuning isn't magic—it's accounting.
Measure the round trip, not the wishlist
Toggle a GPIO at the top of your loop and again after the sensor read completes. Measure the pulse width with a logic analyzer or a second micro running a timer capture. That number—not your millis() delta—is your true sample period. Add the sensor's own conversion time from its datasheet, any bus overhead, and the filter you added because the raw signal was noisy.
If your accelerometer specs 100 Hz output but you're polling it at 200 Hz, you're spending half your cycles reading stale data. If your filter's group delay is 15 ms and your loop period is 10 ms, you're feeding the controller information that lags reality by more than a full cycle. The math will converge in simulation; the robot will hunt.
Pick rates you can actually hit
A 10 ms loop sounds faster than 20 ms, but if your sensor pipeline takes 12 ms, you're just adding jitter. Choose a period that fits your slowest real constraint—sensor conversion, bus transaction, or filter settling—then double it for margin. Log your worst-case timing during a full run (not just a bench test) and size your watchdog timer to catch when you blow past it.
Half-speed loops with consistent timing outperform race-condition sprints. Your PID gains care about the delta-t between samples; if that's a random walk, integral windup becomes a feature, not a bug.
Document the timing you shipped
Your README should list the actual sample rate, the sensor's native bandwidth, and any filter delays. When you come back in three months to tune gains or swap the IMU for a faster model, you'll need to know whether the 50 Hz you wrote was aspiration or measurement. If you're logging loop overruns to EEPROM or a file, note the counter in the repo so future-you knows where to look.
The documentation you write now is the README that saves your weekend later. Timing specs aren't polish—they're the contract between your controller and the physics it's trying to match.