Run everything in simulation
On this page
dev-stack.sh, at the repository root, runs a whole simulated cell on one Linux or WSL2 machine. Each service runs in its own session, with a pid file and a log. stop kills exactly what start began, and nothing else.
export DEV_STACK_UI_HOST=127.0.0.1 DEV_STACK_FIREWALL=0
./dev-stack.sh start # every service
./dev-stack.sh start rt-sim rt-control olp ui # or only the ones you name
./dev-stack.sh status
./dev-stack.sh logs olp # tail one or more logs
./dev-stack.sh restart ui
./dev-stack.sh stopWith no names, a command applies to every service. If you are new to the stack, start with the Quickstart.
Services#
start launches the services in this order. It waits for rt-sim, rt-control and pendant to become healthy before it moves on.
| Service | What runs | Endpoint | Needs | If missing |
|---|---|---|---|---|
catalog | External program-catalog script | http://127.0.0.1:8787 | CATALOG_DAEMON_SCRIPT | Skipped. The script is not in this repository. |
nats | nats-server -a 127.0.0.1 -p 14222 -m 18222 | nats://127.0.0.1:14222 | nats-server 2.14+ at DEV_STACK_NATS_BIN | Skipped |
rt-sim | rosie-rt-core-sim with a compiled simulation machine | native ipc.sock | make -C rt-core control sim (built automatically if missing) | — |
rt-control | rt-control --backend simulation --pair-id local-dev --pair-revision 1 | control.sock, jog.sock | rt-sim healthy | — |
daemon | joint_trajectory_daemon for cell dev-cell | TCP 127.0.0.1:8797 | make -C motion-server/joint-trajectory/v1 all | Skipped |
pendant | Virtual pendant bridge, robot-v4-sim adapter | http://127.0.0.1:51712 | Go; built from steamdeck/virtual on start | — |
motion | Weld planner, pixi run -e motion motion-serve | http://127.0.0.1:8796 | weld_planner/v1 motion env, NVIDIA GPU | Fails. Its log says why. |
olp | offline-programming/v1/start-offline-programming.sh | http://127.0.0.1:8794 | Go, the Tesseract env, a C++ compiler | — |
ui | Vite for the OLP UI | http://127.0.0.1:5189/offline-programming/v1/ui/ | npm ci in offline-programming/v1/ui | — |
daemon, pendant and olp all refuse to start until rt-control is healthy. Before each one starts, the stack runs motion-server/v1/local-rt-core.sh bind. That command writes a consumer binding for the running simulator, so every client targets the same pair and configuration digest.
The olp log is the one to read when something is off. It reports its state in lines prefixed olp-start::
rails:says whether the seam worker (the weld planner'sdefaultenv) is available.motion:names the weld planner origin used for planning and dense playback.
olp takes the longest to start, because the launcher builds whatever is missing and waits up to 270 s for its local simulator.
The simulated machine#
By default, local-rt-core.sh prepare starts from rt-core/config/machines/simulation/simulation-program.json, nine flat axes named J1 to J9. It rewrites each axis's travel and velocity limits from the Rosie 1400 URDF, switches the axes to a simulation drive profile that supports Home, and requires Home on every axis. It then compiles the result with rtctl compile.
The simulated robot therefore behaves like a real cell, in order: Home, Arm, then move.
To bind the actual Rosie 1400 description instead, set:
export DEV_STACK_MACHINE_CONFIG=rt-core/config/machines/simulation/simulation-rosie1400.jsonThe cell then serves the same robot description that OLP plans against. But that machine uses the real drive profile, and the simulated bus does not answer its Home objects. Home is refused, so nothing arms or jogs on it. Leave it unset unless you are working on that gap.
Note
The simulator runs rt-core's state machine over a simulated bus. It does not model dynamics, contact or the drives' own control loops, and it does not qualify anything for powered motion. The separate mujoco-sim/v1 project is an experimental physics model, not a stand-in for the controller.
Where files go#
| Path | Default | Contents |
|---|---|---|
Stack directory, ROSIE_DEV_STACK_DIR | ${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/rosie-stack-$UID | pid files, <service>.log, readiness files |
Runtime directory, ROSIE_LOCAL_RT_DIR | ${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/rosie-dev-rt-$UID | ipc.sock, control.sock, jog.sock, the simulator's metrics.json |
Build directory, ROSIE_LOCAL_RT_BUILD | rt-core/build/dev-stack | machine.json, config/<sha256>/, binding.env, olp-cells.json, olp-rt-core.json, daemon-rt-core.json, dense-plans/ |
binding.env exports the variables a client needs to reach the simulator: ROSIE_RT_CONTROL_SOCKET, ROSIE_RT_JOG_SOCKET, the pair, the configuration digest and the URDF path and hash. Source it to point your own tools at the stack:
source rt-core/build/dev-stack/binding.env
rt-core/build/rtctl describe --socket "$ROSIE_RT_CONTROL_SOCKET" --json | head -c 300; echoTo change the simulation paths, stop the whole stack first. start refuses to change ROSIE_LOCAL_RT_DIR or ROSIE_LOCAL_RT_BUILD under a running stack.
Useful overrides#
| Variable | Default | Effect |
|---|---|---|
DEV_STACK_UI_HOST | 127.0.0.1; 0.0.0.0 when a Tailscale address is found | Where the UI binds |
DEV_STACK_UI_MODE | dev; built when a Tailscale address is found | dev hot-reloads. built serves a compressed bundle and needs restart ui after edits. |
DEV_STACK_FIREWALL | 1 | 0 skips the helper that adds a ufw rule for remote UI access |
DEV_STACK_MACHINE_CONFIG | rt-core/config/machines/simulation/simulation-program.json | The simulated machine, described above |
DEV_STACK_PENDANT_PORT | 51712 | Virtual pendant bridge port |
DEV_STACK_DENSE_INGEST | 127.0.0.1:8797 | Dense daemon TCP ingest |
STACK_WAIT_SECS | 300 | How long start waits for health |
The complete list is in Ports, sockets and environment.
Warning
DEV_STACK_OLP_CELLS and DEV_STACK_OLP_REMOTE add real cells to the OLP machine list, alongside the local simulation. Once they are set, the same UI can arm and move hardware. Read Connect to a cell and the safety model first.
Run the smoke test#
The composed smoke test checks that the stack works end to end. It builds the core and both motion servers, and starts rt-sim, rt-control and pendant in a private directory. It then runs two Go tests against them: one dense program that must complete, and one OLP jog that must move the simulated axes.
export TMPDIR=/dev/shm/rt-core/smoke
mkdir -p "$TMPDIR"
bash motion-server/v1/tests/dev-stack-smoke.shIt ends with dev-stack smoke: rt_core PASS (public jog motion and dense completion), and stops what it started.
Without the dev stack#
The OLP launcher also works on its own:
bash offline-programming/v1/start-offline-programming.shIf no dev-stack binding is live, it builds rt-core and starts its own simulator and rt-control in a temporary directory. It stops them again when you press Ctrl+C.