Prerequisites
Install FastSim and the Isaac Lab provider in one Python environment. The Run uses Isaac Lab 3.0 / Isaac Sim 6.0 with a visible window.
Configuration, key by key
schema: fastsim/2 # FastSim vNext Run schema.
name: demo-control-generic-articulation
backend: isaaclab # Resolve the door's Isaac Lab USD variant.
runtime:
launch_profile: visible # Show the real simulator window.
physics_hz: 60 # Physics integration frequency.
control_hz: 60 # Control-frame admission frequency.
seed: 37 # Reproducible reset seed.
scenario:
scene:
articulations: # Jointed assets that are not robots.
door: # Alias passed to the control API.
use: articulation://fastsim/demo-door
pose:
xyz_m: [0.0, 0.0, 0.0] # Root position in world metres.
quat_xyzw: [0.0, 0.0, 0.0, 1.0]
initial_state:
joints:
door_hinge: 0.0 # Initial angle in radians.
The door component declares resource group door and maps it to physical axis
door_hinge. Application code selects the group; it does not depend on USD prim
paths.
Python, line by line
- The imports provide an async event loop, finite-number checks, a reliable path to
run.yaml, and the publicfastsimpackage. - The constants locate the Run, keep the final pose visible, and define the exact
three-frame result plus the
0.20 +/- 0.08 radacceptance band. main()is asynchronous because control waits must not block FastSim's event loop.fastsim.app(..., planning_reads=True)creates the direct-development facade and enables the scene-state read performed at the end.async withgives this block exclusive application ownership and guarantees cleanup.start()starts the real backend; the first one-second sleep makes the initial door pose visible before motion begins.control.targets()asks FastSim which physical entities and resource groups are actually controllable in the compiled Run.next(...)selects the descriptor whose canonical ID isarticulations.door; printing it makes the resolved binding explicit.control.joints(...)submits one ordered chunk and asynchronously waits for its terminal result.actor="door"uses the Scenario alias. FastSim resolves it toarticulations.doorwithout exposing the backend path.group="door"selects the one-axis resource group declared by the component.path=[[0.0], [0.75], [0.20]]starts closed, opens to 0.75 rad, and returns to 0.20 rad. Each row must match the group's declared axis order.dt=0.75places adjacent path frames 0.75 simulated seconds apart.timeout=20.0bounds the caller's wait; it is not a sleep or a physics duration.pause()preserves the final world while establishing a stable read boundary.scene.state()obtains portable live state;next(...)selects the door's articulation state.- The validation block requires
succeeded, exactly3/3frames, one radian joint, a finite angle, and error no greater than0.08 rad; failure raises and makes the process exit non-zero. - The three prints report terminal status, applied/planned frames, and measured angle, error, and tolerance.
- The final sleep holds the paused door on screen for three seconds.
asyncio.run(main())starts the program when the file is run directly.
Acceptance gates
- Terminal status must be
succeededand frame counts must be exactly3/3. - Final state must contain exactly one finite radian joint.
- The measured angle must be within
0.20 +/- 0.08 rad. The explicit band covers the controller's terminal tolerance without accepting a visibly wrong pose. - The visible Isaac Lab acceptance run on 2026-08-25 measured
0.236 rad, an error of0.036 rad, and exited with code 0.
Run it
From this case directory:
cd demo/fundamentals/08_control_generic_articulation
fastsim config validate run.yaml --json
python main.py
The visible door should open and return partway. The program itself enforces the gates above; printed output alone is not treated as a pass.
Verification level
REAL VISIBLE ISAAC PASS. The native run completed all 3/3 frames, measured
0.236 rad against the 0.20 +/- 0.08 rad gate, exited normally, and left no Isaac
worker process behind.
Why there is no open_door() API
“Open” is task semantics. FastSim core only needs to know that an ordered physical
joint chunk targets one declared resource group. A rule-based plugin, model,
teleoperation source, or direct application may decide what 0.75 rad means.
Common errors
NO_RESOURCE_GROUP: usecontrol.targets()to inspect valid groups instead of guessing from an asset filename.PATH_WIDTH_MISMATCH: every path row must contain one value for this one-axis door.- A window that closes immediately after an exception: read the terminal error; the
async withblock intentionally performs cleanup on failure.