Unit 07 · lesson

Timing, Loops, and Nonblocking Code

A robot cannot freeze the rest of its brain every time one mechanism waits.

Suppose you write:

motor_on()
sleep(5)
motor_off()

For a desktop script, that may be acceptable. In a robot control loop, a blocking delay can prevent other work from happening at the expected rate.

What still needs attention?

During those five seconds, the robot may still need to:

  • read a stop input;
  • update wheel control;
  • watch current or temperature;
  • receive messages;
  • publish telemetry;
  • detect a collision.

A behavior that monopolizes execution can make unrelated subsystems unreliable.

Time as state

Instead of "wait here," store when the action began.

if state == EJECTING:
    motor = reverse
    if now - state_start >= 0.5 s:
        state = IDLE

The loop can keep running. Each pass checks whether the transition condition has become true.

That is the core idea behind nonblocking behavior.

A timing trace

TimeStateSensorMotorTransition?
0.00 sINTAKINGclear+0.6no
0.10 sINTAKINGclear+0.6no
0.20 sINTAKINGobject+0.6yes
0.22 sHOLDINGobject+0.15no

A trace exposes behavior that prose can hide.

Failure mode: timer without state

A single global timer reused by several behaviors can create confusing interactions. The value may be correct but belong to the wrong operation.

Tie timing to the state or command that owns it.

Make a trace

Take the behavior specification from Lesson 1.

Create a table for at least six time steps. Show:

  • current state;
  • one important input;
  • output;
  • transition decision.

Then introduce one delayed sensor event and rerun the trace.

If the entire behavior falls apart because one event occurs 200 ms later, you have found a design weakness before touching hardware.

Blocking code steals time from other responsibilities

Imagine a robot program that checks sensors every 20 ms. Inside the loop, someone adds a two-second delay after turning on an actuator.

During those two seconds the program may stop:

  • reading new sensor data;
  • refreshing safety logic;
  • updating other actuators;
  • logging state;
  • responding to operator input.

The actuator command itself may be fine. The timing architecture is not.

A nonblocking design records when an action started and keeps cycling:

if state == "ACTUATE":
    motor_on()

    if now - started_at >= 2.0:
        motor_off()
        state = "NEXT"

The exact code depends on the platform. The important idea is that elapsed time becomes data instead of a reason to stop the entire program.

Measure loop timing

For a controller expected to run every 20 ms, record several observed loop durations:

CycleDuration
118 ms
219 ms
321 ms
487 ms
519 ms

The average looks acceptable, but cycle 4 is a serious clue. Real-time-ish systems care about worst cases and jitter, not only averages.

If robot behavior occasionally feels late, inspect timing before changing gains or motor power.