Skip to content

dotbot: add the wheel velocity command and set the v3 wheel to 43 mm on a 51:1 gearbox - #298

Merged
geonnave merged 8 commits into
DotBots:mainfrom
geonnave:wheel-control
Sep 25, 2026
Merged

geonnave merged 8 commits into
DotBots:mainfrom
geonnave:wheel-control

Conversation

@geonnave

@geonnave geonnave commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Adds the host side of the DotBot per-wheel speed loop: a WHEEL_VELOCITY payload and a REST route to send it, simulator support for the command, and corrects the v3 geometry record to the measured wheel and gearbox.

Problem

The firmware now closes a speed loop on each wheel (DotBot-libs DotBots/DotBot-libs#32, DotBot-firmware DotBots/DotBot-firmware#425), commanded in signed mm/s per wheel. The only direct drive command on the host was MOVE_RAW, which is motor duty, and the v3 geometry record carried a 44 mm wheel and a 50:1 gearbox that bench measurement has since corrected.

Approach

  • Protocol: PayloadType.CMD_WHEEL_VELOCITY = 0x0F with PayloadCommandWheelVelocity(left_mm_s, right_mm_s), two signed 16-bit fields, matching the C struct.
  • REST: PUT /controller/dotbots/{address}/{application}/wheel_velocity takes DotBotWheelVelocityCommandModel, validated to +/-700 mm/s per wheel (matching the firmware cap, which is being lowered from 800 to 700 in the firmware PRs to keep encoder headroom), and RestClient.send_wheel_velocity_command() calls it. mm/s like every other length the controller exposes. The controller treats the payload as a command, like MOVE_RAW, and does not parse it as an uplink.
  • Firmware behaviour callers must know, stated in the route docstring and the REST reference: only the dotbot-next app acts on the command, and every other app accepts the frame and ignores it. dotbot-next stops the wheels about 500 ms after the last command, so a caller must resend faster than 2 Hz.
  • Simulator: the simulated robot acts on the command the way dotbot-next does. It drives the simulated wheels at the commanded mm/s (the onboard loop closes on the encoders, so the simulated motor error does not apply), and stops them about 500 ms after the last command, like the firmware. A MOVE_RAW command or a set of waypoints takes control back. Payload types the simulator does not handle are now logged as a warning instead of being dropped silently.
  • Geometry: the v3 record moves to a 43 mm wheel (caliper, unloaded) and a 51:1 gear ratio, so mm_per_count becomes pi x 43 / (28 x 51) = 0.0946 mm, down from 0.0987. The gearbox is sold as 50:1; a hand recount found 51, and 28 x 51 = 1428 matches the 1430 +/- 1.5 counts per wheel turn measured by hand within 0.14 per cent, and 0.0948 +/- 0.001 mm per count from 24 lighthouse-measured straight legs within 0.3 per cent. The pins in test_robots.py move with it.

Validation

  • pytest: the new server (including the +/-700 bounds, out-of-range values on both wheels, a non-integer and a missing field), protocol (encode and decode), REST client and controller tests pass, the four simulator tests (wheels driven at the commanded speed, the stop after the timeout, MOVE_RAW taking over, the unhandled-payload warning) pass, and test_robots.py plus test_control_loop_geometry.py pass against the control-loop library built from the DotBot-libs branch. The full suite gives 850 passed, 5 failed; the 5 fail identically on main (four in test_cli_helpers, one in test_controller_dotbot_simulator).
  • The route was used as the command path for the untethered bench runs of the firmware loop on two v3 robots on 2026-09-23 (10 s speed holds within 0.1 per cent; 90 per cent of a step from rest in 20 to 55 ms at 100 to 300 mm/s; tracking within 1 per cent from 20 to 600 mm/s per wheel, 700 partial; a stop running on about 0.05 s times the speed, 3.4 mm from 100; no change in tracking from 2.95 down to 1.0 V). The full numbers are in the firmware and libs PRs.
  • The corrected geometry is what the wheel loop runs on: the firmware's gains were rescaled to 0.0946 mm per count for those runs.

Merge order

  1. DotBot-libs (drv/wheel_control: add a per-wheel speed loop, brake to zero, and correct the v3 distance per count DotBot-libs#32) first.
  2. DotBot-firmware (apps-sandbox/dotbot-next: drive the wheels through the speed loop and pace adverts on the min TX interval DotBot-firmware#425), whose submodule must point at the merged libs commit.
  3. This PR should merge right after drv/wheel_control: add a per-wheel speed loop, brake to zero, and correct the v3 distance per count DotBot-libs#32. The control_loop CI job builds the geometry check against DotBot-libs main, so it fails here until the libs PR merges (the pinned mm_per_count disagrees with the 0.0987 on current libs main).
File + -
dotbot/tests/test_server.py +100 -0
dotbot/dotbot_simulator.py +62 -5
dotbot/tests/test_dotbot_simulator.py +57 -0
11 small files: doc/reference/rest.md, dotbot/controller.py, dotbot/models.py, dotbot/protocol.py, dotbot/rest.py, dotbot/robots.py, dotbot/server.py, dotbot/tests/test_controller.py, dotbot/tests/test_protocol.py, dotbot/tests/test_rest.py, dotbot/tests/test_robots.py +119 -9
Total, 14 files +338 -14

Merge chain

One PR at a time, in this order: DotBots/DotBot-libs#32, this PR, DotBots/DotBot-firmware#425, DotBots/DotBot-libs#33, DotBots/DotBot-firmware#426, DotBots/DotBot-libs#34, DotBots/DotBot-firmware#427, DotBots/DotBot-libs#35, DotBots/DotBot-firmware#428, #301, #302. Next after this one: DotBots/DotBot-firmware#425. Rebase and squash merges both give the DotBot-libs commits new ids, so each firmware PR's dotbot-libs submodule has to be re-bumped to the merged libs main commit before it merges.

@codecov

codecov Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.12%. Comparing base (20af47d) to head (2ad1971).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #298      +/-   ##
==========================================
+ Coverage   84.98%   85.12%   +0.13%     
==========================================
  Files         206      206              
  Lines       26002    26190     +188     
  Branches     1836     1836              
==========================================
+ Hits        22098    22294     +196     
+ Misses       3897     3889       -8     
  Partials        7        7              
Flag Coverage Δ
console 75.57% <ø> (ø)
frontend 97.80% <ø> (ø)
python 86.84% <100.00%> (+0.22%) ⬆️

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

Files with missing lines Coverage Δ
dotbot/controller.py 85.71% <ø> (ø)
dotbot/dotbot_simulator.py 70.39% <100.00%> (+1.60%) ⬆️
dotbot/models.py 100.00% <100.00%> (ø)
dotbot/protocol.py 100.00% <100.00%> (ø)
dotbot/rest.py 77.63% <100.00%> (+0.60%) ⬆️
dotbot/robots.py 97.98% <ø> (ø)
dotbot/server.py 93.06% <100.00%> (+0.14%) ⬆️
dotbot/tests/test_controller.py 100.00% <100.00%> (ø)
dotbot/tests/test_dotbot_simulator.py 100.00% <100.00%> (ø)
dotbot/tests/test_protocol.py 100.00% <ø> (ø)
... and 3 more

... and 3 files 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.

Mirrors DotBot-libs drv/geometry.h on its wheel-control branch. The
geometry pin test builds against DotBot-libs main in CI, so it fails
until that header change lands there.

AI-assisted: Claude Opus 5.5
Mirrors the DotBot-libs drv/geometry.h change on its wheel-control
branch. The geometry pin test builds against DotBot-libs main in CI, so
it fails until that header change lands there.

AI-assisted: Claude Opus 5.5
The bound mirrors the dotbot-next wheel speed cap, which is lowered to
700 mm/s to keep encoder headroom; keep the two in step.

AI-assisted: Claude Opus 5.5
@geonnave
geonnave merged commit 1ed5f7b into DotBots:main Sep 25, 2026
25 of 27 checks passed
@geonnave
geonnave deleted the wheel-control branch September 25, 2026 06:59
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