Loop Overruns and Watchdog Warnings
Why 'Loop time of 0.02s overrun' appears, how the scheduler watchdog points at the culprit, and how to keep the 20 ms budget.
Sign in to track progress, earn XP, and save lessons.
TimedRobot runs your periodic code 50 times a second -- a hard 20 ms budget per loop shared by every subsystem periodic() and every command. Blow that budget and the DS prints a watchdog message (Watchdog not fed within 0.020000s and/or Loop time of 0.02s overrun), control gets choppy, and odometry drifts because updates arrive late.
The watchdog tells you who
WPILib's watchdog and epoch reporting in the command scheduler print which command or subsystem consumed the time when a loop overruns. Read that output first -- it usually names the offender directly, so you don't have to guess.
Cause 1: Print statements
System.out.println is surprisingly expensive and should be removed for competition -- it is a classic overrun source. Replace debugging prints with structured Epilogue logging.
Cause 2: Over-eager logging and repeated CAN queries
The CPU cost of logging is rarely the logging itself -- it's calling expensive methods to fetch the data, most often querying devices on the CAN bus. WPILib's guidance: if logging causes overruns, reduce the number of logged fields, or restructure to read and cache device data once per loop and log the cached value instead of re-querying inside every log call. On Phoenix 6 devices you can batch this with BaseStatusSignal.refreshAll(...):
// Bad: three separate CAN reads per loop just for logging
log(motor.getPosition().getValueAsDouble());
log(motor.getVelocity().getValueAsDouble());
log(motor.getMotorVoltage().getValueAsDouble());
// Better: refresh a status-signal batch once, then read cached values
BaseStatusSignal.refreshAll(posSig, velSig, voltSig);
Cause 3: Blocking calls in the loop
Never Thread.sleep(), busy-wait, or run an unbounded while loop in periodic or command code -- everything shares the one 50 Hz thread. Persisting SPARK parameters (PersistMode.kPersistParameters) blocks CAN, so do it only at setup, never per loop.
Cause 4: Heavy vision or math on the RIO
Run AprilTag detection on a coprocessor (Limelight, an Orange Pi with PhotonVision), not the RIO. Offload motion profiling to the motor controller (Motion Magic) when you can.
Confirm the fix
Check roboRIO CPU% in the DS or the DS log viewer before and after your change. A healthy robot sits comfortably under budget with headroom; if CPU is pinned near 100%, you'll keep overrunning under load. Reproduce the overrun reliably (it often only appears when many mechanisms move at once), apply one change, and confirm the watchdog warnings stop in the same scenario.
Key takeaways
- TimedRobot gives all periodic/command code a shared 20 ms (50 Hz) budget; exceeding it triggers watchdog/overrun warnings.
- The scheduler watchdog reports which command or subsystem caused the overrun -- read it before guessing.
- Remove print statements and avoid re-querying CAN devices for logging; cache device reads once per loop (e.g. BaseStatusSignal.refreshAll on Phoenix 6).
- Never block (sleep, busy-wait, persist flash) in the loop; offload vision to a coprocessor and profiling to the motor controller.
Go deeper
Lesson quiz
RequiredAnswer all 3 questions correctly to complete this lesson.
1.What is the default loop period of WPILib's TimedRobot, which the watchdog uses to detect overruns?
2.What does a 'Loop time of 0.02s overrun' / watchdog warning actually do to the robot?
3.What is the recommended way to handle a long-running task like OpenCV vision processing that won't fit in a 20 ms loop?
Answer every question to submit.