From d0a1a365a22ee1d622131e97d99ddea34e4956c3 Mon Sep 17 00:00:00 2001 From: Timothy Lee Date: Tue, 15 Sep 2026 10:26:51 +0800 Subject: [PATCH 1/2] docs(fleet): document the camera panel where its controls live MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stream Settings, the stream selector and the audio controls are all the header of the dashboard's Camera Feeds panel — teleop has no stream selector at all and opens the same dialog on a different tab — so the teleop page owned the smaller half of what it was describing. The dashboard now carries both, and teleop keeps Take Voice and links out for the rest. Enabling a stream gets taught before the layout does, because a pane assigned to a stream that is not running stays empty and nothing on either page said so. --- solution/img/fleet-stream-selection.png | Bin 0 -> 21117 bytes solution/robot-management-toolbox/index.md | 4 +- .../robot-dashboard.md | 66 +++++++++++++++++- .../robot-teleoperation.md | 37 +++------- 4 files changed, 75 insertions(+), 32 deletions(-) create mode 100644 solution/img/fleet-stream-selection.png diff --git a/solution/img/fleet-stream-selection.png b/solution/img/fleet-stream-selection.png new file mode 100644 index 0000000000000000000000000000000000000000..9c2ca4082d0fc12ce60989bf6e284ee0b1c17d6b GIT binary patch literal 21117 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 -[Robot dashboard](/solution/robot-management-toolbox/robot-dashboard) covers the page in full: each panel, the telemetry readings and when a robot stops counting as reporting, the diagnostics view for a robot that is misbehaving, recovering localisation, taking control of a robot, and [what happens during a mission](/solution/robot-management-toolbox/robot-dashboard#what-happens-during-a-mission) as battery level and the link to the fleet change. +[Robot dashboard](/solution/robot-management-toolbox/robot-dashboard) covers the page in full: each panel, the telemetry readings and when a robot stops counting as reporting, the diagnostics view for a robot that is misbehaving, recovering localisation, [arranging the camera view](/solution/robot-management-toolbox/robot-dashboard#arranging-the-view) and the audio that goes with it, taking control of a robot, and [what happens during a mission](/solution/robot-management-toolbox/robot-dashboard#what-happens-during-a-mission) as battery level and the link to the fleet change. ## Robot teleoperation @@ -178,7 +178,7 @@ Beyond watching, an operator can intervene directly: drive the robot from the br framed caption="An assisted teleoperation view, on a robot fitted and configured for it: surround view, proximity zones, radar, and link quality." /> -[Robot teleoperation](/solution/robot-management-toolbox/robot-teleoperation) covers driving in full: keyboard and gamepad control, remapping and inverting the axes, speed and deadzone, arranging the camera views, audio, and the assisted view some robots present. The control panel itself — taking control, the E-Stop and who may release it — is on the [Robot dashboard](/solution/robot-management-toolbox/robot-dashboard#taking-control) page. +[Robot teleoperation](/solution/robot-management-toolbox/robot-teleoperation) covers driving in full: keyboard and gamepad control, remapping and inverting the axes, speed and deadzone, speaking through the robot while driving, and the assisted view some robots present. The control panel itself — taking control, the E-Stop and who may release it — is on the [Robot dashboard](/solution/robot-management-toolbox/robot-dashboard#taking-control) page. ## Mission planning diff --git a/solution/robot-management-toolbox/robot-dashboard.md b/solution/robot-management-toolbox/robot-dashboard.md index d7bd3e5..40f3bfa 100644 --- a/solution/robot-management-toolbox/robot-dashboard.md +++ b/solution/robot-management-toolbox/robot-dashboard.md @@ -1,6 +1,6 @@ --- sidebar_position: 1 -description: "The robot view in the Robot Management Toolbox: the navigation map, camera feeds, operations and telemetry panels, the control panel, and how battery and connection loss change a running mission." +description: "The robot view in the Robot Management Toolbox: the navigation map, camera feeds and how to arrange them, audio, operations and telemetry panels, the control panel, and how battery and connection loss change a running mission." --- # Robot dashboard @@ -35,6 +35,63 @@ One robot on one screen. The view is laid out so that judging a running mission Two things make it more likely to take. Put the pose where the robot physically is, as closely as you can judge — near enough in the right place is what it needs rather than an exact figure. And have the robot somewhere with clear, distinctive surroundings rather than a repetitive or bare one, because an aisle of identical bays gives it little to tell one position from another. +## Arranging the view + +What the camera panel shows takes two separate decisions: **which of the robot's streams are running**, and **how the panes are arranged**. Both are set from the controls along the top of the panel, which appear when the camera feeds hold the main view area — switch **Map / Cameras** to Cameras if they are currently in the small one. + +### Which streams are running + +
+ +The camera control counts **the streams running against the streams the robot offers** — `6 / 6` when every one of them is up. Opening it lists them by name, and ticking one starts it. The same set appears as **Enabled Streams** in Stream Settings, with each stream's type beside it. + +**Ticking a stream asks the robot to start sending that camera.** It is not a filter over feeds already arriving, which is why a pane can be assigned correctly and still show nothing: a pane whose stream is not running reads **Waiting for stream…** instead. When a layout you have just set looks empty, check this count before changing the layout: assigning a pane starts nothing. + +**Streams are not restored when you come back.** A session opens with none of them running, and the ones you started are stopped when you leave the robot, so the panes fill as you turn feeds on rather than arriving populated. The arrangement around them is remembered. + +### How the panes are arranged + +**Stream Settings**, from the same row of controls, holds the arrangement, and it is remembered per robot. + +
+ +**Layout is chosen in two steps** — first how many panes you want, from one to six, then which arrangement of that many. Four panes offers a 2×2 grid, one large pane with three small, or one across the top with three beneath; the thumbnails show each. **Stream assignment** then puts a named camera in each lettered slot. + +| Setting | Options | +| --- | --- | +| **Layout** | One to six panes, and from two panes up, several arrangements of each | +| **Stream assignment** | Which camera goes in each lettered slot | +| **Grid fit** | **Fill container**, which uses every pixel and may crop edges, or **fixed aspect ratio**, which keeps the whole frame | +| **Primary stream** | The feed that fills the screen while driving | +| **Picture-in-picture** | A stream pinned to a corner — top or bottom, left, centre or right — or switched off | +| **Visible in teleop** | Which cameras appear while driving, which can be a smaller set than the dashboard shows | + +**Dashboard and teleop keep separate layouts**, on their own tabs, so the arrangement you want while monitoring need not be the one you want while driving. The dashboard tab holds the pane layout and the grid fit; the teleop tab holds the primary stream and what is pinned around it. + +Grid fit is the choice worth deliberate thought: filling the pane uses every pixel but crops the edges, and fixed aspect keeps the whole frame at the cost of letterboxing. For judging clearance beside a robot, seeing the whole frame usually matters more than filling the screen. + +## Audio + +Audio has two halves, and they are separate controls: hearing the robot, and speaking through it. + +**Hearing it** is the audio button on the camera panel — **Robot audio on** while it is live, **Robot audio muted** while it is not. Where the browser has not yet been allowed to play audio the same button reads **Click to enable audio**, and pressing it both unlocks playback and unmutes. It is an on-or-off control rather than a level: how loud the robot is comes from the computer's own volume. + +**Speaking through it** is claimed the way the robot's controls are — one person at a time. **Take Voice** takes the channel, and while you hold it the control offers the microphone and a button to release it. Anyone else sees **Voice:** and the name of whoever holds it, and a Site Admin can **Revoke** a channel left held by someone who has gone. + +**The voice channel is independent of the robot's controls.** Taking it does not take the robot, so an operator can speak to whoever is near a machine without touching anything that moves it — and someone else driving does not stop you. Taking the channel is an Operator action; an Observer cannot. + +Speaking is **push-to-talk** by default: hold to speak, release to stop — **`T`** on the keyboard, or the gamepad button bound to it while driving. **Always on**, set from **Audio Mode** in Stream Settings, leaves the microphone live and gives you a mute button instead. That suits an operator who needs both hands and does not suit a shared control room, where it carries whatever else is being said in the room to whoever is standing near the robot. + ## Telemetry Telemetry answers "is this robot all right?" and is presented three ways: **realtime** for what is true now, **history** for how a reading has moved, and **activity** for what the robot has been doing. @@ -127,7 +184,7 @@ Every other control on this panel is held under a **lease** — an exclusive cla **only one person commands it at a time**, and a second operator cannot take the controls until the lease is released. There is no ambiguity about who is responsible for a moving robot. -Pressing **Teleop** starts a driving session in the main view area. Driving itself — keyboard and gamepad control, key mapping and axis inversion, speed and deadzone, arranging the camera views, and audio — is covered on [Robot teleoperation](/solution/robot-management-toolbox/robot-teleoperation). +Pressing **Teleop** starts a driving session in the main view area. Driving itself — keyboard and gamepad control, key mapping and axis inversion, speed and deadzone — is covered on [Robot teleoperation](/solution/robot-management-toolbox/robot-teleoperation). Drive commands depend on the robot being on the map the fleet has activated. A robot that is behind has its dispatch and Go Home controls withdrawn until it catches up — see [Catching a robot up to the map](/solution/robot-management-toolbox/tenant-management#catching-a-robot-up-to-the-map). @@ -188,7 +245,10 @@ after the restart, [contact Weston Robot support](/support/before-you-contact-us No. Control is held under a lease, and only one person holds it at a time. **The camera panel is empty** -The robot is not connected. Feeds are live, so there is nothing to show for a robot that is not reporting in. Check the heartbeat reading in telemetry. +Check the stream count first, since a session starts with nothing running and an empty panel may simply be a panel with no feeds turned on. If streams are running and the panes are still empty, the robot is not connected — feeds are live, so there is nothing to show for a robot that is not reporting in, and the heartbeat reading in telemetry will say so. + +**I set a layout and the panes are still empty** +A pane shows its camera only while that stream is running, and setting the layout does not start anything. Open the stream count beside **Settings** and tick the cameras you want — see [Which streams are running](#which-streams-are-running). **Why did teleoperation stop on its own?** Teleoperation stops the robot when the connection to the fleet degrades. That is deliberate — driving a robot you cannot see is worse than stopping it. diff --git a/solution/robot-management-toolbox/robot-teleoperation.md b/solution/robot-management-toolbox/robot-teleoperation.md index 7d445e4..c9171e3 100644 --- a/solution/robot-management-toolbox/robot-teleoperation.md +++ b/solution/robot-management-toolbox/robot-teleoperation.md @@ -1,6 +1,6 @@ --- sidebar_position: 2 -description: "Driving a robot from the browser: keyboard and gamepad control, key mapping and axis inversion, speed and deadzone, arranging the camera views, and audio." +description: "Driving a robot from the browser: keyboard and gamepad control, key mapping and axis inversion, speed and deadzone, the driving view, and speaking through the robot." --- # Robot teleoperation @@ -47,39 +47,22 @@ These are the values a full deflection asks for, so lowering them makes the whol **Settings apply globally, and can be overridden per robot.** Editing while a robot is open changes that robot's override; the global value is shown beside it so you can see what you have departed from, and **Reset to global** puts it back. A heavy outdoor machine and a small indoor one can therefore sit at different speeds without either being wrong. -## Arranging the view +## What you see while driving -What you see while driving is yours to arrange. **Stream Settings**, from the camera panel, holds it, and the arrangement is remembered per robot. +The driving view has its own arrangement — a primary feed filling the screen, others pinned around it, and a choice of which cameras appear at all — kept separately from the dashboard's on the **Teleop** tab of **Stream Settings**. Both tabs, and turning streams on in the first place, are covered under [Arranging the view](/solution/robot-management-toolbox/robot-dashboard#arranging-the-view). -
- -**Layout is chosen in two steps** — first how many panes you want, from one to six, then which arrangement of that many. Four panes offers a 2×2 grid, one large pane with three small, or one across the top with three beneath; the thumbnails show each. **Stream assignment** then puts a named camera in each lettered slot. - -| Setting | Options | -| --- | --- | -| **Layout** | One to six panes, with several arrangements at each count | -| **Stream assignment** | Which camera goes in each lettered slot | -| **Grid fit** | **Fill container**, which uses every pixel and may crop edges, or **fixed aspect ratio**, which keeps the whole frame | -| **Picture-in-picture** | A stream pinned to a corner — top or bottom, left, centre or right — or switched off | -| **Visible in teleop** | Which cameras appear while driving, which can be a smaller set than the dashboard shows | - -**Dashboard and teleop keep separate layouts**, on their own tabs, so the arrangement you want while driving need not be the one you want while monitoring. - -Grid fit is the choice worth deliberate thought: filling the pane uses every pixel but crops the edges, and fixed aspect keeps the whole frame at the cost of letterboxing. For judging clearance beside a robot, seeing the whole frame usually matters more than filling the screen. - -Layout preferences are restored when you return to a robot. Which camera streams are running is not — streams are started fresh each session, so a view opens empty until its feeds connect. +**Which streams are running is not remembered between sessions**, so a driving view opens with nothing in it until you turn feeds on. A pane whose stream is not running reads **Waiting for stream…**, which is the layout working and the feed absent rather than the layout being wrong. ## Audio -Audio is **push-to-talk** by default: hold to speak, release to stop — **`T`** on the keyboard, or the bound gamepad button. It can be set to always-on where an operator needs both hands, from the same Stream Settings dialog. Incoming volume is adjustable separately. +**Take Voice**, on the teleop bar, claims the robot's voice channel — one person at a time, and separately from the robot's controls, so the person driving is not necessarily the person speaking. + +Speaking is **push-to-talk** by default: hold to speak, release to stop — **`T`** on the keyboard, or the gamepad button bound to it. It can be set to always-on where an operator needs both hands, from Stream Settings. Push-to-talk is worth keeping as the default in a shared control room, since always-on carries whatever else is being said in the room to whoever is near the robot. +Hearing the robot, and who may take or revoke the channel, are covered under [Audio](/solution/robot-management-toolbox/robot-dashboard#audio). + ## The assisted view Some robots present an **assisted** driving view: camera feeds stitched into a surround view, proximity zones drawn from the robot's own sensing, and a bird's-eye radar panel, alongside speed, link latency and bandwidth. @@ -98,7 +81,7 @@ Invert the axis rather than remapping it. Each of the three axes has its own inv Expected. Editing with a robot open sets that robot's override; the global value is shown beside it, and Reset to global clears the override. **The view opened with no cameras** -Streams start fresh each session rather than being restored, so the panes fill as the feeds connect. The layout itself is remembered. +Streams start fresh each session rather than being restored, so nothing is running until you turn a feed on — see [Which streams are running](/solution/robot-management-toolbox/robot-dashboard#which-streams-are-running). The layout itself is remembered. **Can two people drive at once?** No. Control is held under a lease, and only one person holds it at a time. From 383ffa2ce364b4ae784bcddf077455d6da1f9530 Mon Sep 17 00:00:00 2001 From: Timothy Lee Date: Tue, 15 Sep 2026 10:26:58 +0800 Subject: [PATCH 2/2] docs(lessons): record the two figure traps from the camera panel pass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A supplied screenshot carried a red highlight box marking the control to write about; publishing it would have put the only annotated figure on the site, so it was reconstructed clean and the recovery method is worth keeping. The second is the reverse case — fleet-stream-settings.png was captured against a robot with no streams, which only became a defect when the figure moved under prose about enabling them. The recapture goes in TODO. --- TODO.md | 1 + docs/LESSONS.md | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/TODO.md b/TODO.md index 9062c72..ea26395 100644 --- a/TODO.md +++ b/TODO.md @@ -8,6 +8,7 @@ Open items for the docs site. WHAT, not HOW. Status: `[ ]` open, `[~]` in progre - [ ] Capture the route-solve screenshot for `solution/robot-deployment-toolbox/map-inspector.md` — "Testing that a route solves" has no figure because the running backend cannot find the pathfinding binary. It works standalone; the backend needs `TMG_PATHFINDING_BINARY=/wra_topometric_graph/build/bin/pathfinding_query`. Verified by running the binary directly, which returned `{"found":true,…}`. - [ ] `solution/img/toolbox-load.png` is unreferenced. Left in place deliberately — an earlier unused-image sweep deleted `toolbox-finished-map.png` while `solution/intro.md` still used it and the build failed. Re-run the reference check immediately before deleting, not before the edit that changes references. - [ ] Open questions in `docs/design/release-pages-plan.md` §7: Toolbox/SDK delivery routes, R2026.08 labelling, component version visibility, WR SDK reference home. +- [ ] Recapture `solution/img/fleet-stream-settings.png` against a robot that is reporting streams. The published capture was taken with none, so its **Enabled Streams** list is an empty heading and every Stream Assignment slot reads *None* — on `robot-dashboard.md` that figure now sits under prose about enabling streams, and the caption has to explain the gap. A capture with six streams listed and slots filled would carry the section on its own. - [ ] **Two Robot Management Toolbox capability claims need a product ruling before they go back.** The index claimed missions are **portable across sites** and that **duplication and revision comparison** let the next mission start from an existing one. Both existed only on `solution/robot-management-toolbox/index.md` (from `9587dae`), were absent from `mission-editing.md` which owns the feature, and were never seen by the `6abe51d`/`2e4b8ea` exposure passes. Removed in the index de-duplication pass rather than relocated, because relocating would have published them as verified. If the editor does offer either, they belong on `mission-editing.md` with the control named; if it does not, they stay out. - [ ] **`waypoint` vs `checkpoint` across the two toolboxes.** The index said a mission's checkpoint *is* a map waypoint plus a heading; `mission-editing.md` §2 gives three ways to place one (click the map, a saved location, the robot's pose) and never mentions waypoints, while the Deployment Toolbox defines waypoint as a kind of map node. The index no longer asserts the relationship, but the two vocabularies still need reconciling in one place. DEFER TO #38. diff --git a/docs/LESSONS.md b/docs/LESSONS.md index bdd9423..5b40a71 100644 --- a/docs/LESSONS.md +++ b/docs/LESSONS.md @@ -140,3 +140,15 @@ Operational lessons for this repository, in Pattern / Correction / Context form. - **Pattern:** `9587dae` expanded the Robot Management Toolbox index's feature teasers "into real capability documentation" — 3-6 paragraphs each, against the sibling Deployment Toolbox index's one. Every later audit then went to the sub-pages: `6abe51d` and `2e4b8ea` (exposure passes) and `cfb043e` (authority corrections) rewrote `mission-editing.md`, `robot-dashboard.md` and `tenant-management.md` and **never opened the index**. Three defects followed from that one asymmetry. The E-Stop authority rule ended up stated **three times on the index and zero times on `robot-dashboard.md`**, the page that documents the button — so a reader on the owning page could not learn who may press it. `stop_safe`/`continue_mission` and a published default survived on `robot-dashboard.md` even though `cfb043e`'s own commit body ruled the behaviour "set on the robot rather than configured here", because that correction landed on the two *prose* sentences and not on the table-like bullet below them. And two capability claims — missions "portable across sites", "duplication and revision comparison" — existed **only** on the index, absent from `mission-editing.md`, so no exposure pass ever tested them. The pattern then demonstrated itself mid-repair: `83d98e3` landed on `mission-editing.md` correcting "a single correction applies everywhere it is used" (false for a mission a robot already holds), while the index was still publishing the same over-claim two files away. - **Correction:** An overview teaser gets purpose, the reason to open the page, an optional figure, and the link — nothing a sub-page owns. Give every load-bearing fact exactly one owning page and let the index point at it; where safety or orientation justifies a second mention, keep it to a clause and link the canonical one. When auditing a claim, grep the index **and** the sub-pages in the same pass: a correction applied to one is not a correction to the other, and the index is the copy that gets skipped because it reads like a summary rather than like documentation. The tell that a teaser has drifted is a specific number, an enum value or a role boundary appearing in it. `onBrokenAnchors` will not catch any of this — the pages stay perfectly linked while they disagree. - **Context:** Any docs section with an index plus owning sub-pages. This is the structural sibling of "A framing correction invalidates more than the sentence it lands on" — that one is about a correction missing a diagram, this one about a correction missing a whole page. + +### A highlight box drawn on a supplied screenshot is a pointer, not content to publish + +- **Pattern:** A screenshot arrived with a thick red rectangle around the control the request was about — *"include this portion"*. Publishing it as-is would have put the only annotated figure on a site whose 30-odd screenshots carry none, and forced the `alt` to describe a marker that is not part of the product. The red was the sender saying *which control*, and the figure is already about that control, so the annotation carried no information the caption did not. +- **Correction:** Reconstruct the clean UI rather than crop around the marker or publish it. These captures are flat dark panels, so the geometry is recoverable: read the exact mask (`r>110 && r-g>50 && r-b>50` — the marker is hard-edged, the only pinkish pixels elsewhere are subpixel text fringes), then fill each band from the equivalent unmarked geometry — a sibling button's edge column at the same `y` for a button outline, a mirrored corner arc for a panel corner, the flat background shifted by the band width, and one redrawn 1px line where the marker sat over a border. Then assert **changed pixels == masked pixels** and look at the result at 4× before shipping. Ask for an unmarked capture only if the marker crosses text or artwork, where none of this recovers anything. +- **Context:** `solution/img/fleet-stream-selection.png`, rebuilt from a marked-up capture. Sits beside "Verify the geometry after editing an image" and "Alt text must describe the image you publish" — same family, one step earlier: decide what the published file should contain before describing it. + +### A figure captures one moment of state, and prose written later can outrun it + +- **Pattern:** `fleet-stream-settings.png` was captured against a robot reporting no streams, so its **Enabled Streams** section is a heading with nothing under it and all four Stream Assignment slots read *None*. Nothing was wrong while the page only discussed layout. Moving the figure onto a section that teaches *enabling streams* made the empty region load-bearing, and a first draft of the caption said the dialog shows "which streams are enabled" — pointing the reader at blank space. +- **Correction:** Before writing a caption or a sentence beside an existing figure, open the image file and check that the state it captured still supports what the new prose claims. Where it does not and a recapture is not available, say so in the caption in one clause and log the recapture in `TODO.md` — an acknowledged gap costs a reader nothing, an unacknowledged one costs them their confidence in the page. A figure that moves between sections is the moment to run this check; the image did not change, its job did. +- **Context:** `solution/robot-management-toolbox/robot-dashboard.md`. The converse of the alt-text lesson: there the image changed under the text, here the text changed under the image.