kassam 2a78f1b609 Record/replay studio, manual recorder kit, and new-robot install tooling
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.
2026-08-28 20:27:28 +04:00

121 lines
6.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Inspire RH56 dual-hand bring-up on Unitree G1 (PC2)
Setup notes specific to **this** robot. Upstream README assumes a clean
`unitree_sdk2` install in `/usr/local` and hard-codes `/dev/ttyUSB1`+`/dev/ttyUSB2`;
neither holds here, so we adjust as below.
Canonical (workstation): `…/yslootahtech/Project/G1/DFX_inspire_service`
Deploy to PC2 `unitree@192.168.123.164:~/DFX_inspire_service` via `rsync`, then `./build.sh`.
(wifi `10.255.254.86` is flaky; the eth0 IP `192.168.123.164` is the reliable path.)
## 0. Easiest way to drive everything — `manage.sh`
```bash
./manage.sh # status: service + devices + adapters
./manage.sh start|stop|restart
./manage.sh open|close|box|menu # fingers only
./manage.sh like|handshake|thumbup # arm + hand (ARM MOVES)
./manage.sh arm "shake hand" # built-in arm action; armlist to list
./manage.sh help
```
It keeps exactly **one** `inspire_g1` running (duplicates corrupt the RS-485 bus) and
auto-starts the service for gesture commands.
## Architecture (unchanged from upstream)
`hand_example`/your app → DDS topic `rt/inspire/cmd` (`MotorCmds_`, 12 motors:
05 right, 611 left) → **`inspire_g1`** → RS-485 (CH340 USB adapters, 115200 8N1,
Inspire slave id 1) → hands. State flows back on `rt/inspire/state`.
Finger order per hand: pinky, ring, middle, index, thumb-bend, thumb-rotation.
`q` ∈ [0,1]: **0 = closed, 1 = open**.
## 1. Build (no system changes)
`unitree_sdk2` in `/usr/local` is stale (no go2 IDL). `build.sh` links against the
newer `~/unitree_sdk2` source tree instead:
```bash
cd ~/DFX_inspire_service
./build.sh # -> build/inspire_g1, inspire_h1, hand_example
```
## 2. Serial access + telling the two hands apart (one-time)
Both adapters are CH340s reporting the **same** USB serial, so there is nothing
intrinsic to key on. Two separate problems, two separate answers:
**Access.** The udev rule grants `0666` to any CH340 tty — it does **not** create
`/dev/inspire_*` symlinks any more (per-port symlinks broke whenever the adapters
moved to a different hub socket). Only needed for the native path; the container
runs privileged with `/dev` mounted and does not care:
```bash
sudo cp ~/DFX_inspire_service/udev/99-inspire-hands.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules && sudo udevadm trigger
ls -l /dev/ttyUSB* # two nodes, world-writable
```
**Identity.** `inspire_g1` resolves which tty is which hand, best first:
1. **RS-485 id**`id 1 = right, id 2 = left`. Wiring-independent and permanent;
immune to replugs, hub changes and enumeration order. Set it once per robot:
```bash
docker exec inspire-hand ./build/hand_setid <left-hand-port> 1 2
```
2. `INSPIRE_RIGHT_PATH` / `INSPIRE_LEFT_PATH` — pin by physical USB socket
(`run.sh` ships `1-2.2.1.1`, which is **this** robot's topology).
3. USB path order — the fallback, and the one that can silently swap the hands
on unfamiliar hardware.
On a new robot, do (1). Then (2) and (3) stop mattering.
## 3. Run + test
```bash
cd ~/DFX_inspire_service/build
./inspire_g1 # Terminal 1 (no sudo needed after the udev rule)
./hand_example # Terminal 2 -> both hands open/close ~1 Hz
```
Stop with Ctrl+C in each. If a hand doesn't move: check 24 V power, RS-485 A/B
polarity, and that the hand is at Inspire slave id 1.
## Record / replay panel (dashboard)
The dashboard's **Record / replay** card runs `g1_record_replay.py` as a child process — the
same script as the CLI, so a take made in the browser and one made from a terminal are the
same file in `DataG1/`. It needs the recorder present on whatever host serves the dashboard:
```bash
# where g1_record_replay.py lives (searched: ../Manual_Recorder, ~/Manual_Recorder)
export RECORDER_DIR=~/Manual_Recorder
# an interpreter that can import unitree_sdk2py (on a workstation: the g1_env conda python)
export RECORDER_PY=python3
./manage.sh dashboard # prints the resolved recorder path, or NOT FOUND
```
The panel starts a take, relays the in-take keys (`o c [ ] ; '`, `f`, and the saved shapes as
keys 1-9), answers the recorder's preview/save prompts, and lists `DataG1/` for replay,
rename and delete. Two things it works around, both load-bearing:
- **Keys are paced ~150 ms apart.** The recorder reads them with `select()` + buffered
`sys.stdin.read(1)`; a burst lands in Python's buffer, `select()` then reports nothing
ready and every key after the first is stranded. Measured: 3 keys 10 ms apart → 2 arrived.
- **Stop sends SIGINT, and the dashboard repairs SIGINT first.** A process backgrounded by a
non-interactive shell inherits `SIGINT = SIG_IGN`, which survives `exec` — and `manage.sh`
starts the dashboard exactly that way, so without the repair Stop would silently do
nothing. The panel shows a warning if the repair did not hold. SIGINT (not SIGKILL) is
what lets the recorder walk the arm home and release it.
Replay and preview **move the arm** and both require an explicit confirmation. Starting a
take disarms force-follow, since the bridge and the recorder would otherwise both publish
`rt/inspire/cmd` every frame.
## If you move an adapter to a different USB socket
The udev `KERNELS` value changes. Re-discover and update the rule:
```bash
udevadm info -a -n /dev/ttyUSB0 | grep -m1 'KERNELS=="[0-9]'
```
## Troubleshooting
- **`/dev/inspire_*` missing / no CH340 in `lsusb`** → the hand USB-RS485 adapters are
unplugged (or robot rebooted with them out). Re-plug them; udev recreates the names.
- **Fingers don't move (thumb might)** → usually a latched actuator fault from running
two `inspire_g1` at once. `inspire_g1` now runs `ClearError` + sets speed/force on
startup; `./manage.sh restart` re-applies it. If still stuck, power-cycle the hands (24 V).
- **Two `inspire_g1` instances** → they collide on the serial bus; `./manage.sh status`
flags it, `restart` fixes it. Kill with `pkill -x inspire_g1` (never `pkill -f`).
- **`rt/inspire/state` reads all-zeros** → cosmetic; CH340 adapters echo their TX so
position read-back is unreliable. Does not affect commands sent to the hand.