# Dockerized Inspire hand service + dashboard One self-contained image (aarch64) running `inspire_g1` + `hand_bridge` + the Flask dashboard. Build ON the robot (it pulls `unitree_sdk2` + CycloneDDS from the host). ## Build (once, on the robot) ```bash cd ~/DFX_inspire_service ./docker/build_image.sh # assembles context + docker build -> inspire-hand:latest ``` ## Run ```bash ./docker/run.sh # or: docker compose -f docker/docker-compose.yml up -d # dashboard -> http://:8088 ./docker/stop.sh ``` `run.sh` first stops any host-native `inspire_g1`/`hand_bridge` — running both the container and the host service would put **two writers on the RS-485 bus** and corrupt the hands. ## Why these flags - `--network host` — CycloneDDS (rt/inspire/cmd) and the arm actions (rt/arm_sdk, reaching the G1 motion controller on eth0) need the host network. - `--privileged -v /dev:/dev` — the CH340 `ttyUSB*` adapters re-enumerate on the hub; mounting `/dev` + privileged lets the in-container dynamic detection find them. - `IFACE=eth0` — interface for arm actions (must be eth0). Fingers work on either. ## Record / replay in the image The dashboard's Record/Replay card runs `g1_record_replay.py` as a child process, so the image needs the script **and** a python that can import `unitree_sdk2py`. `build_image.sh` pulls both into the context: ```bash RECORDER_SRC=~/Manual_Recorder \ UNITREE_SDK2PY_DIR=~/unitree_sdk2_python \ ./docker/build_image.sh ``` Neither is required — leave them out and the image still builds, with the panel reporting *"recorder not found"*. `start.sh` prints which case you got. - **That layer pip-installs (`cyclonedds`, `unitree_sdk2py`), so the robot needs network at BUILD time.** Offline alternative: copy the robot's own python site-packages into the image the same way `build_image.sh` copies the C libs. - **Takes are a host volume** — `run.sh` mounts `$HAND_DATA_DIR/DataG1` to `/opt/recorder/DataG1`, because `run.sh` starts with `docker rm -f` and would otherwise destroy every recording ever made. - **`arm_home.jsonl` is seeded on first start**, not baked into `DataG1/` — the bind-mount would hide a baked copy, and without it every take skips its return-to-home. It is hidden from the takes list and cannot be deleted from the panel: it is a home pose, not a take. ## Notes - Sudo/udev not needed inside the container (privileged). - Saved poses/combos persist via `-v $HAND_DATA_DIR:/opt/hand/data` + `HAND_DATA` (set by `run.sh`). They used to live in the image and were wiped by every rebuild.