Files
dora-teleop/README.md
T
cristhian e0f5e332ca
validate / validate (pull_request) Successful in 58m41s
validate / test (pull_request) Successful in 58m43s
validate / markdownlint (pull_request) Successful in 58m35s
docs: rewrite the README as a technical reference
2026-09-17 17:03:25 -03:00

121 lines
6.3 KiB
Markdown

# dora-teleop
dora-rs teleoperation node: normalises two control sources — a gamepad read from
`dora-joy`, and a web console served by this node itself — into one owner's lease, and
emits velocity set-points and robot-agnostic commands. It carries no hardware driver: pad
I/O lives in `dora-joy`, arm I/O in a robot node.
**Requirements:** dora 1.0+ (verified with `dora-cli 1.0.1`), Python 3.12+, `uv`.
## System
```mermaid
flowchart LR
pad([gamepad]) --> joy
browser([browser]) -- "ws: held directions, verbs" --> web
web -- "ui_state snapshot" --> browser
joy["dora-joy — sibling repo"]
subgraph teleop["dora-teleop"]
web["web console — built in, HTTP + ws on one port"]
core["command arbitration"]
end
joy -- "joy: axes, buttons, present" --> core
web -- "held directions, ±1" --> core
core -- "twist SI · command verbs (standard)" --> arm
core -- "jog · request (deprecated)" --> arm
arm -. "state · poses · response" .-> core
arm["robot node"]
```
## Quick start
```bash
uv sync
dora run dataflow.yml --uv # teleop + bench stand-in: no pad, no arm
```
Open `http://localhost:7000` and drive the robot from the console. The bench answers
services and publishes telemetry so the loop closes.
### Watching the wire
Every dataflow sets `debug: enable_debug_inspection: true`, so dora can inspect the wire
without any node printing. This needs a coordinator, so it works for the arm-free dataflow:
```bash
dora up
dora start dataflow.yml --uv --name teleop-bench
dora topic list -d teleop-bench # every edge in the graph
dora topic echo -d teleop-bench teleop/jog # every velocity, live
dora topic hz -d teleop-bench teleop/jog --duration 10 # the re-send cadence
dora stop --name teleop-bench && dora down
```
## Running with hardware
| Dataflow | Nodes | Needs |
|------------------------|--------------------------------------|--------------------|
| `dataflow.yml` | teleop + bench stand-in | nothing |
| `dataflow-robot.yml` | joy + teleop + ulite6 | a pad and an arm |
| `dataflow-gamepad.yml` | the same, plus the probe, no console | a pad and an arm |
## Gamepad
| Control | Does (base frame) | Index | Units |
|----------------------|----------------------------------------------------------------|--------------|-------|
| Left stick X / Y | Translate in the horizontal plane: `+X` stick up, `+Y` stick right | axes 0, 1 | mm/s |
| Left / right trigger | Translate along Z: right trigger lifts (`+Z`), left lowers | axes 4, 5 | mm/s |
| Right stick X / Y | Rotate: `+roll` stick right, `+pitch` stick up | axes 2, 3 | deg/s |
| LB / RB | Yaw: RB is `+yaw` | buttons 6, 7 | deg/s |
| A | Close the tool | button 0 | — |
| B | Open the tool | button 1 | — |
| X | Release the E-STOP and clear the arm's error | button 3 | — |
| Y | Toggle freedrive | button 4 | — |
| Back | **E-STOP** — latches until X | button 11 | — |
The sticks move the arm in its horizontal X/Y plane only; the triggers are the one path
to Z. Up on the stick sends the arm away from the operator, the same `+X` convention the
console draws.
## Inputs and outputs
| Direction | Name | Description |
|-----------|----------------|--------------------------------------------------------|
| in | `tick` | 50 ms — change detection, deadman, telemetry |
| in | `joy` | One pad reading from `dora-joy` |
| in | `response` | An answer from the arm |
| in | `motion_state` | How a motion ended |
| in | `robot_state` | The arm's own state |
| in | `tcp_pose` | Tool-tip pose, six float32, mm and degrees |
| in | `joint_pose` | Joint angles, six float32, degrees |
| out | `twist` | **Standard.** `{linear, angular, frame}`, SI |
| out | `command` | **Standard.** `{action, value}`, robot-agnostic verbs |
| out | `jog` | *Deprecated.* mm/s + deg/s, for `dora-ulite6` |
| out | `request` | *Deprecated.* A service call, `request_id` in metadata |
| out | `ui_state` | The console's telemetry snapshot, as JSON |
## Environment variables
| Variable | Default | Description |
|--------------------------|-----------|--------------------------------------------------------|
| `SPEED_MM_S` | `60.0` | Translation speed at full deflection |
| `SPEED_DEG_S` | `30.0` | Rotation speed at full deflection |
| `DEADZONE` | `0.12` | Stick travel near centre to ignore |
| `EXPO` | `0.4` | How much of the curve is cubic; 0 is linear |
| `JOG_TTL_S` | `0.4` | Owner silence before the arm is stopped |
| `JOG_KEEPALIVE_S` | `0.2` | How often an unchanged velocity is repeated |
| `JOY_TTL_S` | `0.2` | `joy` silence before the pad reads as gone |
| `GAMEPAD_IDLE_RELEASE_S` | `2.0` | Untouched pad before it hands the arm back |
| `STATE_POLL_S` | `0.5` | How often to ask the arm what telemetry does not carry |
| `WEB_HOST` | `0.0.0.0` | Address the console binds to |
| `WEB_PORT` | `7000` | Console port; `0` opens no port at all |
## Development
```bash
uv sync --group dev
uv run pytest # 149 tests, no gamepad, arm, browser or daemon needed
uv run ruff check .
uv run ruff format --check .
```