Simulation
Describe an experiment once in a small YAML file, then run it headless on Isaac Sim, Isaac Lab or plain Python — as a tracked run. Caasi resolves the right interpreter, wires the environment, detaches the process and hands you back a run id.
The experiment YAML
The single input format shared by sim run, train,
benchmark start and dataset generate. By convention these live in the
project's experiments/ directory.
# experiments/wave.yaml
name: wave # optional; default = file stem ("wave")
backend: sim # sim | lab | python (default: sim)
script: scripts/wave.py # REQUIRED; relative paths resolve against this YAML's directory
headless: true # default: true
args: ["--steps", "10000"] # optional; passed to the script
env: # optional; extra environment variables
MY_ASSET_ROOT: /data/assets
python: null # optional; explicit interpreter, overrides backend defaults
cwd: null # optional; working directory (default: the YAML's directory)
| Key | Type | Default | Notes |
|---|---|---|---|
script | path | — | Required. Missing → Error: 'script' is required in <path>. |
backend | sim | lab | python | sim | Anything else → error listing the valid backends. |
name | str | file stem | Used for the run name / run id. |
headless | bool | true | Recorded in the run manifest; train/benchmark append --headless to the script args when true. |
args | list | [] | Must be a YAML list. |
env | mapping | {} | Merged into the process environment (yours wins over Caasi's defaults). |
python | str | auto | Explicit interpreter; skips backend launcher resolution. |
cwd | path | YAML's directory | Working directory of the process. |
How the launch command is built
| Backend | Command | Requires |
|---|---|---|
python | <experiment.python or the CLI's python> script args… | nothing |
sim | <registered python> or <ISAACSIM_PATH>/python.sh script args… | registered tools.isaacsim (or env/`python:` fallback) |
lab | <registered python>, else <ISAACLAB_PATH>/isaaclab.sh -p script args…, else python3 | registered tools.isaaclab for the launcher path |
Caasi also sets CAASI_EXPERIMENT=<abs config path> and, for sim/lab backends,
ISAACSIM_PATH/ISAACLAB_PATH in the child environment — unless your
env: block already defines them.
If the backend needs a tool Caasi cannot resolve you get a clear error instead of a broken
launch: Error: Backend 'sim' needs a registered isaacsim install. Register one with
'caasi config set tools.isaacsim...' or use backend 'python'. (exit 1)
caasi sim
| Subcommand | What it does |
|---|---|
run | Run an experiment configuration. |
headless | Run an experiment with --headless --no-window forced. |
status | Simulation backend status. |
check | Check resources before launching. |
logs | Show logs of a simulation run. |
extensions | List extensions in the Isaac Sim install tree. |
stop / pause / resume | Stop, pause or resume a running simulation. |
Descriptions are the CLI's own — caasi sim --help prints this list. The launchers
take the experiment YAML above and produce ordinary
tracked runs; the rest operate on those runs or on the install itself.
caasi sim run
caasi sim run CONFIG_PATH [--name NAME] [--dry-run] [--json] [-- SCRIPT_ARGS…]
| Parameter | Kind | Type | Default | Description |
|---|---|---|---|---|
CONFIG_PATH | argument | path | required | The experiment YAML. |
--name | option | str | experiment name | Override the run name (and thus the run id). |
--dry-run | option | flag | off | Print command, cwd and env without starting anything. |
--json | option | flag | off | Print the run record instead of the two-line summary. |
| trailing args | pass-through | Anything Caasi doesn't recognize is appended verbatim to the script's argv (after the YAML's args). |
Example — dry run first
shellcaasi sim run experiments/wave.yaml --dry-run
Dry run — nothing was started:
command: /opt/isaac-sim-6.0/python.sh /home/you/demo/scripts/wave.py --steps 10000
cwd: /home/you/demo/experiments
env: ISAACSIM_PATH=/opt/isaac-sim-6.0
env: MY_ASSET_ROOT=/data/assets
Example — launch and follow
shellcaasi sim run experiments/wave.yaml -- --seed 7
Run 20260905-142301-wave started in the background.
Follow it with: caasi logs 20260905-142301-wave -f
caasi run status latest --json | jq '.status'
"running"
The run is created with backend: sim (or whatever the YAML says),
kind: experiment, and extra: {experiment, headless} in the manifest.
--json (or the global flag — caasi --json sim run exp.yaml) prints
the full run record instead of the summary; it has no effect together with --dry-run,
which always prints the human-readable command. And remember: the command's
exit code reflects the launch, not the simulation. Check the outcome later with
caasi run status.
caasi sim headless
caasi sim headless CONFIG_PATH [--name NAME] [--dry-run] [--json] [-- SCRIPT_ARGS…]
sim run with --headless --no-window forced onto the script's argv —
same argument, same options, same tracked run — whatever the YAML's headless: key
says. For configurations written for a windowed session that you now launch on a machine with no
display.
shellcaasi sim headless experiments/wave.yaml --dry-run
Dry run — nothing was started:
command: /opt/isaac-sim-6.0/python.sh /home/you/demo/scripts/wave.py --steps 10000 --headless --no-window
cwd: /home/you/demo/experiments
env: ISAACSIM_PATH=/opt/isaac-sim-6.0
env: MY_ASSET_ROOT=/data/assets
caasi sim headless experiments/wave.yaml -- --seed 7
Run 20260905-142301-wave started in the background.
Follow it with: caasi logs 20260905-142301-wave -f
The two flags land after the YAML's args and before your trailing pass-through
args. The run is recorded with kind: experiment, the YAML's backend and
extra: {experiment, headless: true}.
Status, checks and lifecycle
caasi sim status
caasi sim status [--json]
Two things at a glance: is an Isaac Sim install resolvable, and which sim-backend runs are
currently active (running/paused)?
shellcaasi sim status
✓ Isaac Sim 6.0 → /opt/isaac-sim-6.0
ID Name Status PID
20260905-142301-wave wave running 48213
caasi sim status --json | jq '.backend'
{ "tool": "isaacsim", "version": "6.0", "path": "/opt/isaac-sim-6.0", "python": null }
Always exits 0 — an unregistered backend is reported
(✗ Isaac Sim is not registered (caasi config set tools.isaacsim...)) but is not an
error. JSON: {"backend": {…}, "active_runs": [run records]}.
caasi sim check
caasi sim check [--verbose]
Pre-flight check before a big launch: runs the doctor engine for the four sections that matter
for simulation — isaac, nvidia, hardware,
storage — and prints each check with hints for problems (or all, with
--verbose).
shellcaasi sim check
✓ Isaac Sim — 6.0 at /opt/isaac-sim-6.0
✓ nvidia-smi — 550.107.02
✓ GPU 0 — NVIDIA GeForce RTX 4090 (24564 MiB)
✓ RAM — 62.7 GiB
✓ Disk (runs) — 412.9 GiB free
caasi sim check && caasi sim run experiments/wave.yaml
Exit 1 if any check fails (warnings don't fail), else 0 — made for
&& chains.
caasi sim logs
caasi sim logs QUERY [--lines|-n N] [--follow|-f] [--stream|-s stdout|stderr]
caasi run logs restricted to simulation runs — same
options and defaults (-n 50, -s stdout), same follow behaviour. A run
started by another backend is rejected instead of silently shown:
shellcaasi sim logs latest -n 2
[14:41:07] step 10000/10000 fps 244.1
[14:41:07] done.
caasi sim logs ant
Error: Run has backend 'lab', not 'sim'; use: caasi run logs <query>
Exit 1 for an unknown run, a non-sim backend, an unknown
--stream value or a missing log file. To watch both streams at once, use
caasi run attach.
caasi sim extensions
caasi sim extensions [--enabled] [--user] [--json]
An inventory of the Isaac Sim install tree — no simulator is started and nothing is imported.
Caasi walks exts, extscache, extsInternal,
extsUser, extsDeprecated and extsPhysics looking for
<extension>/config/extension.toml, and reports the extension name (the
[package] name key, else the directory name), the directory it came from, and whether
[core] preload is true.
| Option | Type | Default | Description |
|---|---|---|---|
--enabled | flag | off | Only list extensions preloaded (enabled) at startup. |
--user | flag | off | Only scan the extsUser directory. |
--json | flag | off | Array of {name, source, enabled} objects. |
shellcaasi sim extensions
Extension Source Enabled
omni.anim.graph.core exts —
omni.kit.viewport.window exts ✓
caasi.custom.recorder extsUser —
caasi sim extensions --enabled --json
[ { "name": "omni.kit.viewport.window", "source": "exts", "enabled": true } ]
This one does need a registered install: without one it fails with Error: Isaac Sim is not
registered (caasi config set tools.isaacsim...). (exit 1), where
sim status only reports it. A tree with no extension manifests prints
No extensions found. and exits 0.
caasi sim stop / pause / resume
caasi sim stop QUERY
caasi sim pause QUERY
caasi sim resume QUERY
Simulation-flavored shortcuts for the identical run lifecycle
commands (same QUERY semantics — latest, id prefix or name — same
signals, same messages, same exit codes). Convenience, nothing more:
shellcaasi sim pause latest
Run 20260905-142301-wave paused.
caasi sim resume latest && caasi sim stop latest
Run 20260905-142301-wave resumed.
Run 20260905-142301-wave stopped.
caasi lab
The same experiment YAML, launched through the Isaac Lab toolchain. Caasi delegates — it never imports Isaac Lab; it resolves the launcher, wires the environment and hands the process off as a detached run.
| Subcommand | What it does |
|---|---|
status | Isaac Lab environment status. |
run | Run an experiment configuration with the lab backend. |
train | Train a policy as a tracked background run. |
play | Play a trained policy (isaaclab.sh -p <play script>). |
evaluate | Evaluate a trained policy checkpoint. |
Descriptions are the CLI's own — caasi lab --help prints this list. The four
launchers share their plumbing:
- The command is built exactly as the backend table describes for
backend: lab: the registeredtools.isaaclabpython when it has one, else<ISAACLAB_PATH>/isaaclab.sh -p <script>, elsepython3. Without a registered install:Error: Backend 'lab' needs a registered isaaclab install. Register one with 'caasi config set tools.isaaclab...' or use backend 'python'.(exit 1) backend: labin the YAML is the normal case. Another value still launches, but prints a yellowNote: experiment backend is 'sim', not 'lab'.first — the run'sbackendalways comes from the YAML.- All four accept
--name,--dry-run,--jsonand trailing pass-through args;--dry-runprints the command and cwd (noenv:lines, unlikesim run) and--jsonprints the full run record. - The run's
kindcomes from the subcommand —experiment,train,play,evaluate— and its manifest carries the experiment path inextra, socaasi run listtells them apart.
caasi lab status
caasi lab status [--json]
Isaac Lab detection plus the two integration points Caasi cares about: the
isaaclab.sh launcher and whether a ros2 CLI is on PATH for the ROS
bridge.
shellcaasi lab status
✓ Isaac Lab 2.0 at /opt/IsaacLab
Launcher: /opt/IsaacLab/isaaclab.sh
Python: /opt/IsaacLab/_isaac_sim/python.sh
caasi lab status --json
{
"status": "ok",
"detail": "Isaac Lab 2.0 at /opt/IsaacLab",
"version": "2.0",
"path": "/opt/IsaacLab",
"python": "/opt/IsaacLab/_isaac_sim/python.sh",
"launcher": "/opt/IsaacLab/isaaclab.sh",
"ros2_bridge": true
}
Detection order: registry → ISAACLAB_PATH → ~/isaaclab,
~/IsaacLab, ~/workspace/* → pip metadata. When nothing is found the
status is fail with an install hint — but the exit code is still 0
(this is a report, not a gate; use caasi setup or doctor for gating).
caasi lab run
caasi lab run CONFIG_PATH [--name NAME] [--dry-run] [--json] [-- SCRIPT_ARGS…]
Starts the config's script as a tracked run with kind: experiment and
extra: {experiment, headless}:
experiments/ant.yamlname: ant-train
backend: lab
script: scripts/train_ant.py
headless: true
shellcaasi lab run experiments/ant.yaml --dry-run
Dry run — nothing was started:
command: /opt/IsaacLab/isaaclab.sh -p /home/you/demo/scripts/train_ant.py
cwd: /home/you/demo/experiments
caasi lab run experiments/ant.yaml
Run 20260905-151207-ant-train started in the background.
Follow it with: caasi logs 20260905-151207-ant-train -f
caasi sim run experiments/ant.yaml launches the very same file — it only adds a
yellow Note: experiment backend is 'lab', not 'sim'. to point out the mismatch. Either
way the run is tracked with backend: lab and appears in caasi run list
like everything else.
caasi lab train
caasi lab train CONFIG_PATH [--steps N] [--envs N] [--resume PATH] [--seed N]
[--device TEXT] [--name NAME] [--dry-run] [--json] [-- SCRIPT_ARGS…]
The Lab-side twin of caasi train, with the same
argv translation: --steps, --envs, --resume,
--seed, --device, then --headless when the YAML is headless,
then your trailing args. It adds --name and --json, and records
kind: train with extra: {experiment, steps, envs}:
shellcaasi lab train experiments/ant.yaml --steps 500000 --envs 4096 --device cuda:0 --dry-run
Dry run — nothing was started:
command: /opt/IsaacLab/isaaclab.sh -p /home/you/demo/scripts/train_ant.py --steps 500000 --envs 4096 --device cuda:0 --headless
cwd: /home/you/demo/experiments
caasi lab train experiments/ant.yaml --steps 500000 --envs 4096 --json | jq '.kind, .status'
"train"
"running"
caasi lab play / evaluate
caasi lab play CONFIG_PATH [--checkpoint PATH] [--name NAME] [--dry-run] [--json] [-- SCRIPT_ARGS…]
caasi lab evaluate CONFIG_PATH [--checkpoint PATH] [--name NAME] [--dry-run] [--json] [-- SCRIPT_ARGS…]
Both run a policy instead of training one, and both take their script from optional extra keys
in the same YAML: play uses play_script, evaluate uses
evaluate_script and falls back to play_script; without them the plain
script is used. --checkpoint PATH is handed to that script as
--checkpoint PATH, ahead of any trailing args. Runs are recorded with
kind: play / kind: evaluate and
extra: {experiment, checkpoint}.
experiments/ant.yaml (additions)play_script: scripts/play_ant.py
evaluate_script: scripts/eval_ant.py
shellcaasi lab play experiments/ant.yaml --checkpoint logs/ant/model.pt --dry-run
Dry run — nothing was started:
command: /opt/IsaacLab/isaaclab.sh -p /home/you/demo/scripts/play_ant.py --checkpoint logs/ant/model.pt
cwd: /home/you/demo/experiments
caasi lab evaluate experiments/ant.yaml --checkpoint logs/ant/model.pt
Run 20260905-172233-ant-train started in the background.
Follow it with: caasi logs 20260905-172233-ant-train -f
Neither appends --headless on its own — pass it as a trailing arg (or in the YAML's
args) when playing a policy back on a headless machine. Recorded artifacts can be
opened afterwards with caasi view run.