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. |
check | Check resources before launching. |
extensions | List extensions in the Isaac Sim install tree. |
Descriptions are the CLI's own — caasi sim --help prints this list, and since 0.3.0
it prints exactly these four. The two launchers take the experiment YAML
above and produce ordinary tracked runs; check preflights the
machine and extensions inventories the install.
runsim status, sim stop, sim pause, sim resume
and sim logs are gone in 0.3.0. Each was a thin wrapper around one semantic
operation that the run group already owns, so drive simulation runs through it with
--backend sim: caasi run status|stop|pause|resume|logs <query>
--backend sim. The toolchain-availability half of the old sim status now
lives in sim check; the machine-wide live view is
caasi monitor. See
caasi run and below.
caasi sim run
caasi sim run CONFIG_PATH [--name NAME] [--dry-run] [--json] [-- SCRIPT_ARGS…]
An entry point, not a separate operation: sim run delegates to
caasi run start CONFIG_PATH --backend sim. Many
entry points, one semantic op — everything below is that command with the sim backend implied, and
caasi run status/logs/stop apply to the result exactly as
they do to any other run.
| 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 —
the same delegation to run start --backend sim,
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}.
Checks and lifecycle
Two things live under sim: the pre-flight check and the
install inventory. Everything else about a simulation run — is it
alive, what did it print, pause it, stop it — belongs to the
run group, targeted with --backend sim.
| Up to 0.2.0 | Since 0.3.0 |
|---|---|
caasi sim status | caasi run list --backend sim and caasi run status latest --backend sim for the runs; caasi sim check for the toolchain |
caasi sim logs QUERY | caasi run logs QUERY --backend sim |
caasi sim stop QUERY | caasi run stop QUERY --backend sim |
caasi sim pause QUERY | caasi run pause QUERY --backend sim |
caasi sim resume QUERY | caasi run resume QUERY --backend sim |
caasi sim check
caasi sim check [--verbose] [--json]
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). It also took over the toolchain-availability half of the old
sim status: whether an Isaac Sim install resolves at all is the first item of the
report.
sim check is one of the check entry points, so it emits the shared
Check Contract — the same scope /
items / result report the root caasi check produces, scoped
to sim:
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 --json | jq '{scope, result}'
{ "scope": "sim", "result": "ready" }
caasi sim check && caasi sim run experiments/wave.yaml
Exit 0 when the result is ready or warning (warnings
don't gate), 3 when it is incompatible — the exit rule every
check entry point shares, and what makes it usable in && chains
and in CI.
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 check reports the same absence as an item in its report.
A tree with no extension manifests prints
No extensions found. and exits 0.
Status, logs and lifecycle: caasi run … --backend sim
caasi run status QUERY [--backend sim|lab|python] [--json]
caasi run logs QUERY [--backend sim|lab|python] [--lines|-n N] [--follow|-f] [--stream|-s stdout|stderr]
caasi run stop QUERY [--backend sim|lab|python]
caasi run pause QUERY [--backend sim|lab|python]
caasi run resume QUERY [--backend sim|lab|python]
One underlying operation per verb, reached through the
run group: same QUERY semantics (latest, id
prefix or name), same signals, same messages, same exit codes, and the same log options and
defaults (-n 50, -s stdout). --backend filters or targets
runs by backend — pass sim to keep the group to simulation runs, or drop it to act on
any run:
shellcaasi run status latest --backend sim
wave (20260905-142301-wave)
Status running
Backend sim
caasi run logs latest --backend sim -n 2
[14:41:07] step 10000/10000 fps 244.1
[14:41:07] done.
caasi run pause latest --backend sim
Run 20260905-142301-wave paused.
caasi run resume latest --backend sim && caasi run stop latest --backend sim
Run 20260905-142301-wave resumed.
Run 20260905-142301-wave stopped.
To watch both streams at once, use
caasi run attach; for the live resource panel —
CPU, RAM, GPU, VRAM, throughput, runtime — use
caasi monitor
(--once for a single snapshot).
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. |
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, and since 0.3.0
it prints exactly these four. The three 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 three 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,play,evaluate— and its manifest carries the experiment path inextra, socaasi run listtells them apart. Training runs (kind: train) come from the rootcaasi traininstead.
lab train moved to the root trainThere is no caasi lab train in 0.3.0. Training is the generic root launcher
caasi train CONFIG — one command for every
backend, delegating to caasi run start and
resolving the interpreter through the same backend table, so a
backend: lab experiment still launches through isaaclab.sh -p. See
below.
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.
Training: the root caasi train
caasi train CONFIG_PATH [--steps N] [--envs N] [--resume PATH] [--seed N]
[--device cuda:0] [--dry-run] [-- SCRIPT_ARGS…]
lab train is gone in 0.3.0: training is the root
caasi train, the generic training launcher. It
delegates to caasi run start and resolves the
interpreter through the same backend table, so a backend: lab
experiment is still launched with isaaclab.sh -p — one command, every backend. The
argv translation is the old one: --steps, --envs, --resume,
--seed, --device, then --headless when the YAML is headless,
then your trailing args; the run is recorded with kind: train and
extra: {experiment, steps, envs}. train has no --name and no
JSON mode of its own — use --dry-run to see the command, then
caasi run status for everything after launch:
shellcaasi 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 train experiments/ant.yaml --steps 500000 --envs 4096
Run 20260905-151207-ant-train started in the background.
Follow it with: caasi logs 20260905-151207-ant-train -f
caasi run status latest --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.