Advanced Metal Research
GitHub Contact AMR

Run everything in simulation

On this page
  1. Services
  2. The simulated machine
  3. Where files go
  4. Useful overrides
  5. Run the smoke test
  6. Without the dev stack

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 stop

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

ServiceWhat runsEndpointNeedsIf missing
catalogExternal program-catalog scripthttp://127.0.0.1:8787CATALOG_DAEMON_SCRIPTSkipped. The script is not in this repository.
natsnats-server -a 127.0.0.1 -p 14222 -m 18222nats://127.0.0.1:14222nats-server 2.14+ at DEV_STACK_NATS_BINSkipped
rt-simrosie-rt-core-sim with a compiled simulation machinenative ipc.sockmake -C rt-core control sim (built automatically if missing)—
rt-controlrt-control --backend simulation --pair-id local-dev --pair-revision 1control.sock, jog.sockrt-sim healthy—
daemonjoint_trajectory_daemon for cell dev-cellTCP 127.0.0.1:8797make -C motion-server/joint-trajectory/v1 allSkipped
pendantVirtual pendant bridge, robot-v4-sim adapterhttp://127.0.0.1:51712Go; built from steamdeck/virtual on start—
motionWeld planner, pixi run -e motion motion-servehttp://127.0.0.1:8796weld_planner/v1 motion env, NVIDIA GPUFails. Its log says why.
olpoffline-programming/v1/start-offline-programming.shhttp://127.0.0.1:8794Go, the Tesseract env, a C++ compiler—
uiVite for the OLP UIhttp://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's default env) 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.json

The 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#

PathDefaultContents
Stack directory, ROSIE_DEV_STACK_DIR${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/rosie-stack-$UIDpid files, <service>.log, readiness files
Runtime directory, ROSIE_LOCAL_RT_DIR${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/rosie-dev-rt-$UIDipc.sock, control.sock, jog.sock, the simulator's metrics.json
Build directory, ROSIE_LOCAL_RT_BUILDrt-core/build/dev-stackmachine.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; echo

To 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#

VariableDefaultEffect
DEV_STACK_UI_HOST127.0.0.1; 0.0.0.0 when a Tailscale address is foundWhere the UI binds
DEV_STACK_UI_MODEdev; built when a Tailscale address is founddev hot-reloads. built serves a compressed bundle and needs restart ui after edits.
DEV_STACK_FIREWALL10 skips the helper that adds a ufw rule for remote UI access
DEV_STACK_MACHINE_CONFIGrt-core/config/machines/simulation/simulation-program.jsonThe simulated machine, described above
DEV_STACK_PENDANT_PORT51712Virtual pendant bridge port
DEV_STACK_DENSE_INGEST127.0.0.1:8797Dense daemon TCP ingest
STACK_WAIT_SECS300How 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.sh

It 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.sh

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