fleet/agents/r1/README.md

83 lines
3.3 KiB
Markdown
Raw 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.

# sanad_api_r1 — R1 fleet **telemetry** agent
Pushes the Unitree **R1 EDU**'s live status to the YS Lootah fleet server. One
robot type = one folder = its own Docker image (sibling of `sanad_api_g1`).
This build covers the **"Main statuses"** endpoint — the R1 **map is skipped by
request**:
```
POST {SERVER_URL}/api/v1/fleet/ingest/telemetry Authorization: Bearer <device_token>
{ "sn": "r1_0000", "mac": "…", "battery": 74, "charging": false,
"status": "idle", "position": {"x": …, "y": …}|null, "faults": [] }
```
Sent every ~2 s. If DDS state can't be read it still sends a **heartbeat**
(`battery: null`, `status: "offline"`) so the robot shows as online.
## Where each field comes from (R1 EDU, unitree_hg DDS)
| field | source |
|---|---|
| `battery`, `charging` | `rt/lf/bmsstate` (`BmsState_`): `soc` 0100; `charging = current > +0.05 A` — same as SanadR1 `arm_controller.get_battery` |
| `faults[]` | `rt/lowstate` (`LowState_`): low battery, motor over-temp, comms-stale |
| `status` | derived (charging / moving / idle / offline); optional loco **FSM** read (ids `0`/`1`/`4`/`811`, GET-only) with `R1_READ_FSM=1` |
| `position` | R1 localizes with stereo **VSLAM** (ROS side). This agent is ROS-free, so position is `null` unless `R1_POSITION_SOURCE=rosbridge` (reads `/odom`) |
| `mac` | primary NIC hardware address |
> **Safety:** never commands motion — only the read-only `GET_FSM_ID` RPC is ever
> issued to the R1.
## Install (full Docker) — on the R1 backpack
One command — builds the DDS image, creates `.env`, starts the container (auto-restarts on boot):
```bash
./install.sh # creates .env if missing, then build + up -d
# (edit .env when prompted: SERVER_URL, DEVICE_TOKEN, SN, DDS_INTERFACE=eth10)
./install.sh --logs # follow logs
./install.sh --simulate # one synthetic post (server smoke test, no robot)
./install.sh --down # stop + remove
```
Or the raw compose flow:
```bash
cp .env.example .env # SERVER_URL, DEVICE_TOKEN, SN, DDS_INTERFACE=eth10
docker compose up -d --build
docker compose logs -f
```
Test without a robot (synthetic state, exercises the full upload path):
```bash
docker compose run --rm sanad-api-r1 --simulate --once # one synthetic post
docker compose run --rm sanad-api-r1 --dry-run # print telemetry, no POST
# or bare:
SERVER_URL=DEVICE_TOKEN=SN=r1_0000 python3 sanad_api_r1.py --simulate -v
```
### Key env (full list in `.env.example`)
| var | meaning |
|---|---|
| `SERVER_URL`, `DEVICE_TOKEN` | given by YS Lootah — **required** |
| `SN` | fleet id, e.g. `r1_0000` |
| `DDS_INTERFACE` | robot link — **`eth10`** on the R1 backpack |
| `R1_READ_FSM` | `1` = read loco FSM for status (GET-only) |
| `R1_POSITION_SOURCE` | `none` (default) · `rosbridge` |
| `POLL_INTERVAL` | seconds between posts (default 2) |
## Build note
Build **on the R1 backpack** (arm64). The image bundles the DDS stack
(CycloneDDS + the vendored `unitree_sdk2py` wheel under `vendor/`), mirroring
SanadR1's recipe. `network_mode: host` is required for DDS visibility. If
`unitree_sdk2py` can't load, the agent logs a warning and sends heartbeats.
## Porting
`sanad_api_go2` is the same pattern; the Go2 uses `unitree_go` (not `unitree_hg`)
DDS, so swap the message imports (`BmsState_`/`LowState_` idl path) and topic
names for the Go2 SDK.