Bring every doc up to date, and give the repo a Wi-Fi template that exists

CONTEXT.md had grown by appending and contradicted itself (all four nodes vs six, a retracted CPU
figure, "no calibration", "nobody enrolled", "audio untested as a service", the Wi-Fi-off boot as
not done, a class the tracker replaced). Rewritten as one current-state document organised by
subsystem, with fresh measurements, the owner's decisions, the gotchas, the neck's arrival-day plan
and what is genuinely still open.

README: status, speakers not fitted, new-session note for Hermes, the calibration section (shipped
calibration, coverage hints, outlier dropping, --from-views), running the tests, the docs row.
HANDOFF.md gets a status banner listing where the original brief has drifted. CLAUDE.md starts at
CONTEXT.md. The echo-cancel config no longer talks about a Pi Zero, and the enclosure guide no longer
tells you to centre a servo with a tool that does not exist yet.

pi/network-config.example is a tracked, credentials-free first-boot template: the README pointed at
pi/50-cloud-init.yaml, which is gitignored and so missing from a fresh clone. The dead IdentityCache
(replaced by the tracker) is removed. Docstrings corrected: audio scope publishing, the MCP health
topics, and what the calibration tool does.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AFN8nRRCiDiMN7ChBpHJhs
This commit is contained in:
2026-10-05 08:38:32 -07:00
co-authored by Claude Sonnet 5.5
parent ce7063f647
commit 720a7cfc34
14 changed files with 390 additions and 468 deletions
+29 -18
View File
@@ -9,7 +9,7 @@ through MCP.
connected), idle, listening, thinking, looking, speaking. The mouth draws the actual voice, and the
eyes glance toward whoever the camera sees.
- **Ears and voice:** the HAT's two microphones and speakers, shared with your computer as ordinary
audio devices.
audio devices (the speakers are not fitted yet; the microphones work).
- **Neck:** pan and tilt servos (`turn_head`), once they are fitted.
- **One cable:** USB-C carries power and a private network link to your computer. Wi-Fi is a fallback.
@@ -21,8 +21,8 @@ only speaks MCP.
| Works and is tested | Waiting on hardware |
|---|---|
| Face on the display, always on at power-up, with a sleeping pose | Servos: `turn_head`, `head_position`, the pan/tilt URDF |
| Camera, face detection, opt-in recognition of people you enrol | Speakers: playback, echo cancel, Hermes talking out loud |
| Eyes follow faces; mouth draws your live voice | Fan (the Pi runs at about 62 °C open-air without one) |
| Camera (calibrated), face detection, opt-in recognition of people you enrol | Speakers: playback, echo cancel, Hermes talking out loud |
| Eyes follow faces; mouth draws your live voice | Fan (the Pi runs at about 60 °C open-air without one) |
| MCP bridge: `hermes mcp test robot` passes, real Hermes turns call `look()` and `faces()` | USB-C splitter (the Pi is powered from your computer's port for now) |
| Thermal guard: slows the camera, face and detector before the Pi throttles | |
| USB network link, firewall, boots and works with Wi-Fi off | Printed enclosure (`enclosure/`) |
@@ -71,8 +71,8 @@ door.
1. **Flash** Ubuntu Server 24.04 for the Pi (arm64, preinstalled image) and check its SHA256.
2. **Configure first boot** on the card's `system-boot` partition. In `user-data`, create the user
`hermes` with key-only login and the hostname `hermes-bot`. For Wi-Fi, put the access point in
`network-config` (a template is `pi/50-cloud-init.yaml`; it holds your password, so it is
gitignored). cloud-init applies these on the *first* boot only: to change Wi-Fi later, edit
`network-config` (copy `pi/network-config.example` and fill it in; keep a filled copy out of the repo: `pi/50-cloud-init.yaml`
is gitignored for that). cloud-init applies these on the *first* boot only: to change Wi-Fi later, edit
`/etc/netplan/50-cloud-init.yaml` on the Pi.
3. **Boot, then copy this repo to the Pi and run the installer:**
@@ -90,9 +90,10 @@ door.
1. **Network link:** `sudo host/omarchy/robot-usb-link.sh` gives your end of the link `10.55.0.1/30`,
matched by the gadget's fixed MAC, so any port and any machine works. The robot is `10.55.0.2`.
2. **Hermes:** the token is in `/etc/robot-mcp.env` on the Pi. Put it in `~/.hermes/.env` as
`ROBOT_MCP_TOKEN=`, and merge `host/omarchy/hermes-config-snippet.yaml` into
`~/.hermes/config.yaml`.
2. **Hermes:** the token is in `/etc/robot-mcp.env` on the Pi (`ssh hermes@10.55.0.2 sudo cat
/etc/robot-mcp.env`). Put it in `~/.hermes/.env` as `ROBOT_MCP_TOKEN=`, and merge
`host/omarchy/hermes-config-snippet.yaml` into `~/.hermes/config.yaml`. Start a new Hermes session
afterwards: a session keeps the tool list it started with.
3. **Audio (optional, needs the speakers):** `host/omarchy/50-robot-tunnel.conf` makes the robot's
microphone and speaker appear as devices; `host/omarchy/hermes-robot` starts Hermes with them as
the defaults.
@@ -137,27 +138,37 @@ give MCP images to the main model: it saves the photo to its image cache and the
restart robot`.
- **Enrol yourself** so the eyes prefer you (numbers only are stored, never photos, in
`~/.local/share/hermes/faces.json`): on the Pi, with the robot running and after `source
~/robot-env.sh`, `python3 -m hermes_vision.people enroll YOURNAME`. `list` shows who is enrolled, `forget YOURNAME` removes you.
~/robot-env.sh`, `python3 -m hermes_vision.people enroll YOURNAME`. `list` shows who is enrolled;
`forget YOURNAME` removes you. Enrolling is deliberately not an MCP tool: a prompt must not be able to
teach the robot a face.
- **Update the code:** `rsync -a --delete --exclude __pycache__ ros/ hermes@<pi>:~/ros_ws/src/`, then
`colcon build --symlink-install` in `~/ros_ws`, then `sudo systemctl restart robot`.
- **Run a node by hand:** `source ~/robot-env.sh` first: it puts the libcamera fork and the right
OpenCV on the path, and keeps ROS local.
- **Run the tests:** on the Pi, after `source ~/robot-env.sh`, `python3 -m pytest test` in each package
under `~/ros_ws/src/` (the face suite takes about a minute). A shell that sourced the environment
before a package was built cannot import it: source again.
## Calibrating the camera
Needed once, and again if you change the lens focus or the image size. Set the focus first (the lens
turns if it is not glued: focus at the distance you will use), then show `docs/checkerboard-9x6.png`
on a screen (or print it) and measure one square with a ruler. With the robot running, on the Pi, after
`source ~/robot-env.sh`:
This camera's calibration ships in `pi/calibration/` and the installer restores it, so a fresh install is
already calibrated. Redo it if you change the lens focus, the image size or the camera: a changed focus
invalidates it. Set the focus first (the lens turns if it is not glued: focus at the distance you will
use), then show `docs/checkerboard-9x6.png` on a screen (or print it) and measure one square with a
ruler. With the robot running, on the Pi, after `source ~/robot-env.sh`:
```
python3 -m hermes_vision.calibrate --cols 9 --rows 6 --square-mm <measured>
```
Hold the camera 25 to 60 cm from the board and vary distance, tilt and position, keeping all of it in
view. It collects 20 different views, solves with OpenCV and writes
`~/.ros/camera_info/ov5647__base_soc_i2c0mux_i2c_1_ov5647_36_1296x972.yaml`. A reprojection error under
about 0.5 px is good. `sudo systemctl restart robot` to load it.
Hold the camera 25 to 60 cm from the board and vary distance, tilt and position, **including the edges
and corners of the camera's view** (the tool says which regions are still uncovered), and hold still:
blurred views are dropped as outliers. It collects 24 different views, solves with OpenCV (holding the
`k3` distortion term at zero, which a lens this mild does not need) and writes
`~/.ros/camera_info/ov5647__base_soc_i2c0mux_i2c_1_ov5647_36_1296x972.yaml`, plus the collected views so
`--from-views FILE` can re-solve them without recapturing. A reprojection error under about 0.5 px is good.
The square size only scales distances, not the lens model, so an approximate measurement is fine.
`sudo systemctl restart robot` to load it, and copy the YAML into `pi/calibration/` to keep it.
## Tuning and troubleshooting
@@ -205,5 +216,5 @@ about 0.5 px is good. `sudo systemctl restart robot` to load it.
| `pi/` | Everything installed on the Pi: `install.sh` and its parts, services, firewall, overlays. |
| `host/` | Your computer's side: the USB link script, Hermes and PipeWire config, a platform check. |
| `enclosure/` | OpenSCAD model, STLs and the print guide. Built from estimates: measure before printing. |
| `docs/face-prototype.html` | The browser prototype the face was ported from. |
| `docs/` | `face-prototype.html` (the browser prototype the face was ported from) and `checkerboard-9x6.png` (for calibration). |
| `HANDOFF.md` | The original implementation brief. `CONTEXT.md` tracks where things really stand. |