Dashboard (web/hand_web.py) - Record/replay panel driving g1_record_replay.py as a pty child: take library (replay/download/duplicate/rename/delete/upload/delete-all), pause & resume, and in-take key buttons that grey out in the --fingers modes the recorder ignores (measured: in touch mode the keys change nothing at all). - /api/restart is container-aware: it kills inspire_g1 and lets the supervisor relaunch it. It used to run manage.sh, which started a SECOND inspire_g1 beside the supervised one - two writers on one RS-485 bus - and never returned. - Shape/combo libraries take a .bak on every write, with an undo button. Both files are rewritten in full, so deleting the last entry was unrecoverable. recorder/ - The recorder lives in this project now: one source of truth for the CLI and the dashboard, with pause/resume added to replay. - record.sh picks a runtime by itself (a python with the SDK, the vendored SDK, or the inspire-hand container). bundle.sh packs a ~340KB portable kit. tools/ - preflight.sh: read-only readiness report for a new robot (hardware, docker, build prerequisites, per-robot settings) ending in an install-path verdict. - fetch_deps.sh: stage build dependencies, verifying the libs are aarch64. - export_ui.py: regenerate an embedding app's vendored copy of the UI. docker/ - build_image.sh resolves its dependencies from several layouts: deps/ inside the project, /usr/local, a source install prefix, or a ROS2 colcon workspace (where the idl headers live when /usr/local has none). - web/ is copied in the last layer, so dashboard edits skip the C++ rebuild. - restart=always, and start.sh always builds so an edit cannot silently run a stale image. deps/unitree_sdk2 is vendored so a robot that has never seen the SDK can build. Docs: README quickstart + embedding notes, SETUP_G1 corrected (that udev rule stopped creating /dev/inspire_* symlinks a while ago), ROBOT_README describing a live install.
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)
cd ~/DFX_inspire_service
./docker/build_image.sh # assembles context + docker build -> inspire-hand:latest
Run
./docker/run.sh # or: docker compose -f docker/docker-compose.yml up -d
# dashboard -> http://<robot-ip>: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 CH340ttyUSB*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:
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 waybuild_image.shcopies the C libs. - Takes are a host volume —
run.shmounts$HAND_DATA_DIR/DataG1to/opt/recorder/DataG1, becauserun.shstarts withdocker rm -fand would otherwise destroy every recording ever made. arm_home.jsonlis seeded on first start, not baked intoDataG1/— 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 byrun.sh). They used to live in the image and were wiped by every rebuild.