Verification level
Visible native Isaac. The native window shows the first motion change direction after the old chunk has applied 20 of its 40 frames.
Prerequisites
- FastSim
0.1.0a6, UniRoboSim Core0.10.0, andunirobosim-isaaclab==0.10.1in one Python 3.12 environment. - Isaac Lab 3.0 / Isaac Sim 6.0, an NVIDIA GPU, and a working display.
Why the runtime is paused
Simulation speed is not guaranteed to match wall clock. Sleeping for half a second
would therefore not mean "20 control frames". The program pauses continuous physics
and uses single_step() so the example is deterministic on slow and fast machines.
Physics and control are both 60 Hz, so each step crosses one control boundary.
Code flow
FRAME_DT = 1.0 / 60.0matches the configured control clock.linear_path(...)returns exactlyframestwo-axis joint targets, including both endpoints. It has no simulator dependency.- The application starts, then pauses while keeping the world alive.
old_chunk = submit_joints(..., preempt=True)returns immediately with an operation handle and marks this incumbent chunk as replaceable.- The loop reads
status().progress.applied_framesand single-steps until at least 20 old frames were really applied. new_chunk = submit_joints(...)arrives from the same session and replaces the preemptible incumbent at the next safe control boundary.- Single-step continues until the new operation is terminal.
result()proves the old status ispreemptedand the new status issucceeded.release()explicitly removes both completed operation records.
The first row of the new path is close to the expected 20-frame old position. In a real model plugin, use the latest observation as this new starting point to avoid an unwanted discontinuity.
The preempt argument describes the chunk being submitted: True means a later
chunk may preempt it. It is therefore required on the old/incumbent chunk, not on the
new/challenger chunk. Set it on every model-servo chunk that should remain replaceable.
Run it
cd demo/fundamentals/05_servo_preemption
fastsim config validate run.yaml --json
python main.py
Expected result: the native Isaac window opens, begins the old motion, and changes to the new motion at the safe boundary after 20 old frames. The terminal resembles:
Old chunk: preempted, applied=20/40
New chunk: succeeded, applied=40/40
The old applied count can be slightly above 20 if provider bookkeeping crosses the
same safe boundary, but it must remain below 40 and the status must be preempted.
Common errors
- New chunk waits instead of replacing: the old incumbent was submitted without
preempt=True, so its policy correctly denied replacement. - Old chunk succeeds: the runtime was allowed to run continuously and consumed all 40 frames before the new submission.
- Infinite wait in custom code: always set a timeout or a bounded maximum-step policy around external model/device workflows.
Next
Continue with 06 — RGB scene-camera screenshot.