Skip to content

dotbot: send waypoint poses, batch ids and max speed, and read the waypoint report - #301

Merged
geonnave merged 9 commits into
DotBots:mainfrom
geonnave:waypoints
Sep 25, 2026
Merged

geonnave merged 9 commits into
DotBots:mainfrom
geonnave:waypoints

Conversation

@geonnave

@geonnave geonnave commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Sits directly on main now that #298 has merged, so the diff below is this PR alone. The robot side is DotBots/DotBot-libs#35 and DotBots/DotBot-firmware#428, both merged; the console gesture for poses is stacked on this one as #302.

The host side of onboard waypoint batches: waypoints can carry a heading, the controller sends the new wire trailer and a max speed, reads the robot's waypoint report, and resends a batch until the robot shows it has it.

What a waypoint means

Every waypoint is a position for the robot's centre, the axle midpoint, with or without a heading. A point with heading_deg is a pose: the robot stops there, turns in place to face it, then goes on. The dotbot-next firmware app drives the axle there; the older dotbot apps still steer their LH2 photodiode onto the point, since they have no estimator. The waypoint echo in the controller now starts at the robot's axle, its own estimate when it advertises one, else the body pose expanded from its fix when that has a heading, else the fix.

Approach

  • Protocol (dotbot/protocol.py, mirroring drv/protocol.h):
    • LH2_WAYPOINTS carries a trailer after the points: batch_id u8, heading_tol_deg u8, pass_mm u16, then one heading_cdeg i16 per point (0.01 degree, 0 facing +y, clockwise positive like the advertised direction; 0x7FFF = none). 7 + 10n bytes, 167 for 16 points. A trailer, rather than headings interleaved with points, so the older apps still read threshold, count, points and ignore the rest.
    • The advertisement's 8-byte waypoint report after waypoint_idx: status, reason, batch id, max speed in 10 mm/s, axle x and y. The shorter legacy shapes still parse.
    • CMD_MAX_SPEED (0x11), max_speed_mm_s u16.
  • Models and REST:
    • DotBotLH2Waypoint adds heading_deg, normalised to [0, 360). The waypoints request gains intermediate_threshold (the pass radius, mm) and heading_tolerance (degrees); None leaves the firmware default (20 mm, 3 degrees).
    • DotBotModel gains waypoints_status (NONE, IN_PROGRESS, ARRIVED, FAILED, ABORTED), waypoints_reason (the reason's name), waypoint_index, max_speed (mm/s) and axle_position, all null for apps that send no report. They reach the console through the usual update notification.
    • The waypoints request is refused with 422 for more than 16 points (the firmware's DB_MAX_WAYPOINTS, beyond which it drops them), a threshold outside 0 to 65535, or a heading that is not a finite number. A point whose heading fails validation is refused rather than taken as a point without one.
    • PUT /controller/dotbots/{address}/{application}/max_speed takes max_speed_mm_s (0 to 700; 0 restores the default). Only dotbot-next acts on it, clamped to 20 to 700.
  • Lost packets: batch id plus resend, chosen over an acknowledgement because the advertisement is already periodic.
    • Each batch gets a new id. While nothing of this controller's is pending, the id follows the one the robot advertises, so two controllers on one robot (the console's and a script's) never pick the robot's current id, which the robot would ignore as a repeat.
    • The batch is resent while advertisements do not show its id: first 1.2 s after sending (adverts come every 0.1 to 1 s), at most 5 tries, then a warning.
    • Robots whose advertisement carries no report (the older apps) get the batch once, since a resend would restart their path.
    • The empty batch (stop) goes the same way, so a lost stop is resent too. send_max_speed is confirmed by the advertised max speed, rounded half up as the firmware rounds it.
    • A resend never brings an older command back over a newer one. A pending batch is dropped when this controller sends the robot a move raw, wheel velocity or control mode command, which end a batch on the robot, and when the advertisement shows a batch id this controller did not send, which is another controller's newer batch. Restoring the default speed cancels a pending max speed.

Validation

  • pytest: the protocol round-trips (trailer, report, legacy shapes, a 16-point batch at 167 bytes); REST headings reaching the wire as centidegrees; intermediate_threshold and heading_tolerance passing through; the max speed route (valid, default, out of range, unknown robot); the echo starting at the axle; the controller's report handling and resend logic (confirmed, lost and resent, giving up after 5 tries, sending once to apps without a report, max speed confirmation and its half-way rounding, not repeating another controller's id, not resending over another controller's batch or a direct command, a second lost batch still resent); the request bounds (17 points, threshold, NaN heading, pass radius and tolerance out of range). The suite gives 933 passed and 5 failed; the 5 fail the same way without this PR, on dotbot: add the wheel velocity command and set the v3 wheel to 43 mm on a 51:1 gearbox #298's branch as it was before merging (a local config file and a bound port in the simulator test).
  • CI: all checks pass, check custom control loop included.
  • This controller drove all the floor tests of drv/steering: sequence waypoint batches with poses, a completion report and a speed limit DotBot-libs#35 on v3 robots 2426 and E21A. The ones that exercise this PR directly:
    • Lost batch, the first transmission dropped on purpose by a wrapper: 3 of 3 recovered by the resend 1.4 s later (the move started 1.6 s after the send, 0.2 s normally), then ARRIVED.
    • Duplicate, the same batch sent again at index 1: 3 of 3 without a restart.
    • Max speed 150 / 450 / default: fastest measured 170 / 485 / 319 mm/s, reported back as 150 / 450 / 300.
    • The report's status, index and axle matched on every path run (square, zigzag, U-turn, pose path), and the precise-arrival runs at 5 / 3 / 2 / 1 mm arrived 22 of 22 with mean axle misses of 5.6 / 2.6 / 2.1 / 1.5 mm.
    • Found on the way: a script sending raw batch ids can collide with the controller's, which follow the robot's; the robot then ignores it, as designed. That is why the ids follow the advertised one.

Known limits and follow-ups

  • Docs pending: the REST reference and guides for the new fields and route come with the docs pass after review.
  • Simulator: the DotBot simulator behaves as an older app. It sends no report, so it gets each batch once, drives the points without their headings, and logs the max speed command as unhandled.
  • The older dotbot apps are kept only for the old-against-new comparison; they get each batch once and report nothing.
  • The console on main sends a 60 mm terminal radius, and the robot stops at the edge of it; dotbot/console-web: place pose waypoints and show the waypoint report in the dock #302 makes it 10 mm by default.

Merge chain

Merged on 2026-09-25: DotBots/DotBot-libs#32, #298, DotBots/DotBot-firmware#425, DotBots/DotBot-libs#33, DotBots/DotBot-firmware#426, DotBots/DotBot-libs#34, DotBots/DotBot-firmware#427, DotBots/DotBot-libs#35 and DotBots/DotBot-firmware#428. The rest go one PR at a time, in this order: this PR, #302, DotBots/DotBot-libs#36, DotBots/DotBot-firmware#429, #303. Next to merge overall: #301, then #302, which complete the waypoint batches; then the cut-over, DotBots/DotBot-libs#36, DotBots/DotBot-firmware#429 and #303. Next after this one: #302. Each firmware PR gets a final dotbot-libs: bump to ... merge on main commit, moving its submodule to its libs PR's merge commit on DotBot-libs main, before it merges, as DotBots/DotBot-firmware#425 to DotBots/DotBot-firmware#428 did. The next firmware PR then conflicts on that submodule line, so it is rebased onto main when its turn comes.

File + -
dotbot/tests/test_controller.py +263 -0
dotbot/controller.py +181 -2
dotbot/tests/test_server.py +168 -5
dotbot/protocol.py +147 -1
dotbot/tests/test_protocol.py +106 -6
dotbot/models.py +68 -9
dotbot/server.py +55 -5
1 small file: doc/conf.py +3 -0
Total, 8 files +991 -28

@codecov

codecov Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.94737% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.36%. Comparing base (20af47d) to head (9d2e148).
⚠️ Report is 11 commits behind head on main.

Files with missing lines Patch % Lines
dotbot/controller.py 95.45% 4 Missing ⚠️
dotbot/models.py 96.87% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #301      +/-   ##
==========================================
+ Coverage   84.98%   85.36%   +0.37%     
==========================================
  Files         206      206              
  Lines       26002    26650     +648     
  Branches     1836     1836              
==========================================
+ Hits        22098    22749     +651     
+ Misses       3897     3894       -3     
  Partials        7        7              
Flag Coverage Δ
console 75.57% <ø> (ø)
frontend 97.80% <ø> (ø)
python 87.20% <98.94%> (+0.58%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
dotbot/protocol.py 100.00% <100.00%> (ø)
dotbot/server.py 93.58% <100.00%> (+0.66%) ⬆️
dotbot/tests/test_controller.py 100.00% <100.00%> (ø)
dotbot/tests/test_protocol.py 100.00% <100.00%> (ø)
dotbot/tests/test_server.py 100.00% <100.00%> (ø)
dotbot/models.py 99.65% <96.87%> (-0.35%) ⬇️
dotbot/controller.py 87.11% <95.45%> (+1.40%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

The waypoints trailer, the advertisement's report and the max speed
command mirror DotBot-libs drv/protocol.h and dotbot-next's parser; keep
the layouts in step.

AI-assisted: Claude Opus 5.5
…eeds up

A resend fired after a direct command, a stop to the default speed, or
another controller's newer batch would bring the old command back, a
stop included. The max speed confirmation now rounds half up as the
firmware's lroundf does, so 445 mm/s is confirmed by 45.

AI-assisted: Claude Opus 5.5
Breaking: a waypoints request with more than 16 points, a threshold
outside 0 to 65535, or a heading that is not a finite number is now
refused with 422, where it used to be truncated by the robot, fail
with 500, or lose its heading.

AI-assisted: Claude Opus 5.5
@geonnave
geonnave marked this pull request as ready for review September 25, 2026 08:23
@geonnave
geonnave merged commit 6668874 into DotBots:main Sep 25, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant