Files
dora-teleop/dataflow-gamepad.yml
cristhian da1b079b7b
validate / validate (pull_request) Successful in 58m41s
validate / test (pull_request) Successful in 58m44s
validate / markdownlint (pull_request) Successful in 58m40s
serve the web console from the node and drop the websocket bridge
The browser reached this node through `dora-websocket-transceiver`: a second
node, a second port, and a round trip through the dataflow for every button
press, to carry a UI only this node can interpret. The node now serves the
page and holds the browsers' sockets itself on one port, so the console is
one URL and a consumer wires nothing for it.

`dora_teleop/server.py` runs aiohttp on a thread of its own and meets the
control loop at two non-blocking points: a bounded queue the tick drains, and
a snapshot handed back. Both queues are bounded deliberately — a flood is
dropped rather than grown, and a slow browser loses stale snapshots rather
than the newest — because nothing a browser does may stall the thread that
stops the arm. A port already in use stops the node rather than leaving it
announcing a console nobody can open.

The web deadman moved but did not change: it was a `clients` message from the
bridge and is now the open-socket count read each tick. One hop fewer, same
meaning.

Contract, so 0.2.0 -> 0.3.0: `ui_command` and `clients` are gone. `ui_state`
stays published for `dora topic echo` and the bench, with no browser reading
it. New WEB_HOST and WEB_PORT (7000; 0 opens no port).

The console itself is laid out like the arm's own pendant — two rings
quartered into arc sectors you hold, each with its out-of-plane axis as pills
above. Directions and signs match xArm Studio on the controller, `Y+` on the
left included: two consoles driving one arm that disagree about which way is
+Y is a trap, not a preference.

Also sends a velocity only when it changes, repeating an unchanged one on
JOG_KEEPALIVE_S (0.2 s) purely to beat the consumer's own expiry. Repeating
every tick made `dora-ulite6` re-plan the same motion twenty times a second.
That turned out not to be the whole cause of the stepping an operator felt —
the rest was fixed in dora-ulite6 v0.3.1, pinned here — but sending 18
messages where 83 went before is worth keeping on its own.

`dora-joy` moves to v0.1.1: v0.1 is an annotated tag, and dora 1.0.1 records
the tag object's id as the commit hash and then rejects its own checkout for
sitting on the peeled commit. Neither dataflow could be built before this.
2026-09-16 14:50:21 -03:00

86 lines
2.6 KiB
YAML

# The same bench, with a real gamepad and a probe.
#
# `dataflow.yml` is what CI validates, and it deliberately has no pad in it: the
# pad is `dora-joy`, another repo, and pulling it in by `git:` would cost that
# file its one property — that it resolves and runs from this checkout alone.
# This file is the other half. It needs the network on first build, and a pad.
#
# The bench answers services and publishes telemetry but never moves anything.
# What this is for is watching the node decide: the lease changing hands, the
# deadman firing when you let go, and the arm being stopped when the pad — or
# the node reading it — goes away.
#
# Run it under a coordinator rather than `dora run`, so `dora topic` can read the
# wire and so the probe has something to attach to:
#
# dora up
# dora start dataflow-gamepad.yml --uv --name teleop-bench
# dora-teleop-probe # in another terminal
# dora topic echo -d teleop-bench teleop/jog
# dora stop --name teleop-bench && dora down
strict_types: true
debug:
enable_debug_inspection: true
nodes:
- id: joy
git: https://gitea.liria.cl/liria/dora-joy.git
tag: v0.1.1
build: pip install .
path: dora-joy
inputs:
tick: dora/timer/millis/20
outputs:
- joy
- id: teleop
path: dora-teleop
build: pip install .
inputs:
tick: dora/timer/millis/50
# queue_size 1: joy ticks at 20 ms and this node at 50, so without it the
# loop would work through a backlog of readings that have already been
# overtaken. An arm should be driven by the newest one or by none.
joy:
source: joy/joy
queue_size: 1
queue_policy: drop_oldest
response: bench/response
robot_state: bench/robot_state
tcp_pose: bench/tcp_pose
joint_pose: bench/joint_pose
outputs:
- twist
- command
- jog
- request
- ui_state
env:
SPEED_MM_S: "60"
SPEED_DEG_S: "30"
# This one is about the pad, so the console stays shut. Set a port to
# bring it up alongside and watch the lease move between the two.
WEB_PORT: "0"
# Declared, not spawned: `dora-teleop-probe` attaches to this id when you run
# it, and the dataflow runs perfectly well when nobody does.
- id: teleop-probe
path: dynamic
inputs:
joy: joy/joy
- id: bench
path: examples/bench.py
inputs:
tick: dora/timer/millis/200
jog: teleop/jog
request: teleop/request
ui_state: teleop/ui_state
outputs:
- response
- robot_state
- tcp_pose
- joint_pose