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 Core 0.10.0, and unirobosim-isaaclab==0.10.1 in 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

  1. FRAME_DT = 1.0 / 60.0 matches the configured control clock.
  2. linear_path(...) returns exactly frames two-axis joint targets, including both endpoints. It has no simulator dependency.
  3. The application starts, then pauses while keeping the world alive.
  4. old_chunk = submit_joints(..., preempt=True) returns immediately with an operation handle and marks this incumbent chunk as replaceable.
  5. The loop reads status().progress.applied_frames and single-steps until at least 20 old frames were really applied.
  6. new_chunk = submit_joints(...) arrives from the same session and replaces the preemptible incumbent at the next safe control boundary.
  7. Single-step continues until the new operation is terminal.
  8. result() proves the old status is preempted and the new status is succeeded.
  9. 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

bash
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:

text
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.