Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
9a15141
dotbot: make the robot geometry record complete for v3
geonnave Sep 22, 2026
9639e70
dotbot/detection: read the robot geometry from the record
geonnave Sep 22, 2026
b67b5ae
dotbot/dotbot_simulator: read the drivetrain from the geometry record
geonnave Sep 22, 2026
b5142ba
dotbot/tests: pin the geometry record to the compiled control loop
geonnave Sep 22, 2026
9786bc3
dotbot/controller: skip the -1000 no-heading sentinel, not 0xFFFF
geonnave Sep 22, 2026
16526ac
dotbot/controller: log the advertised y as Y
geonnave Sep 22, 2026
5fefaa7
dotbot: expand a photodiode fix and a heading into a body pose
geonnave Sep 22, 2026
c38cbc6
dotbot/models: add the body pose and robot model beside the LH2 position
geonnave Sep 22, 2026
264362e
dotbot/controller: expand each LH2 fix into a body pose
geonnave Sep 22, 2026
969a55d
dotbot/console-web: draw each robot from the body pose the controller…
geonnave Sep 22, 2026
4edeae4
dotbot/console-web: name the heading's source in the robot inspector
geonnave Sep 22, 2026
0a53c9e
dotbot: publish each wheel rectangle with the body pose
geonnave Sep 22, 2026
1fe5351
dotbot/console-web: draw the tyres as a layer of the map glyph
geonnave Sep 22, 2026
b25eee0
dotbot/detection: take the tyres and connectors from the record
geonnave Sep 22, 2026
c5aa580
dotbot: publish each tyre rectangle with the camera pose
geonnave Sep 22, 2026
bd9d87b
dotbot/console-web: draw the tyres in the camera overlay
geonnave Sep 22, 2026
c05335b
dotbot/dotbot_simulator: hold the heading back until 50 mm of travel
geonnave Sep 22, 2026
ec8c08b
dotbot/dotbot_simulator: add a robot with no heading to the worlds
geonnave Sep 22, 2026
cdf1fb7
dotbot/dotbot_simulator: steer the waypoint loop on the true heading
geonnave Sep 23, 2026
f55599d
dotbot: carry the photodiode in the body pose
geonnave Sep 23, 2026
69fa38f
dotbot/console-web: hang the map glyph off the pose's photodiode
geonnave Sep 23, 2026
19914c5
dotbot/console-web: widen the panel toggles and bind them to [ and ]
geonnave Sep 23, 2026
ca40d60
dotbot: carry the reach, core and envelope radii in the body pose
geonnave Sep 23, 2026
2b84f3c
dotbot/console-web: draw robots as Body or Sensor with a possible foo…
geonnave Sep 23, 2026
95c001a
dotbot/console-web: frame the site view with a modest margin
geonnave Sep 23, 2026
dc027ee
dotbot/console-web: centre the map on the minimap point that was pressed
geonnave Sep 23, 2026
b6b93e3
dotbot/console-web: let the map pan 2 m past the site view
geonnave Sep 23, 2026
346813e
dotbot/console-web: case the footprint ring so it reads over the camera
geonnave Sep 23, 2026
e822d3c
dotbot/console-web: size waypoints from the robot's body, capped at 1…
geonnave Sep 23, 2026
2a0cc5d
dotbot/console-web: draw the small fallback as a disc with a heading bar
geonnave Sep 23, 2026
b062140
dotbot/console-web: bar a sensor point with its heading when one is k…
geonnave Sep 23, 2026
18e5303
dotbot/console-web: size a robot placed from swarmit from its last pose
geonnave Sep 23, 2026
66e3969
dotbot/console-web: know a robot's LED colour only while its app runs
geonnave Sep 23, 2026
026cd61
dotbot/console-web: keep the map still when a side panel toggles
geonnave Sep 23, 2026
37b59cf
dotbot: serve a headingless pose for each swarmit device type
geonnave Sep 23, 2026
69104cb
dotbot/console-web: size a robot out of its app from its device type
geonnave Sep 23, 2026
2a5f66e
dotbot/console-web: draw the LED as one sensor mark, drop the drive dot
geonnave Sep 23, 2026
16fffcb
dotbot/console-web: remember each side panel's collapsed state per br…
geonnave Sep 23, 2026
89a2c4b
dotbot/console-web: save the map view as its centre and scale
geonnave Sep 23, 2026
ce96298
dotbot/controller: clear the direction when an advertisement has none
geonnave Sep 23, 2026
681714a
dotbot: drop the pose from the overwritten notification update
geonnave Sep 23, 2026
0f1bdcc
dotbot: name J5 and J6 as the connector pair, not M1 and M2
geonnave Sep 23, 2026
8ee452b
dotbot: cache the heading-invariant robot geometry
geonnave Sep 23, 2026
a728697
dotbot/tests: pin the swarmit device models to swarmit's device types
geonnave Sep 23, 2026
2f3c27e
dotbot/controller: seed the twin's heading and heading origin from it…
geonnave Sep 23, 2026
9b97881
dotbot/tests: drop the headingless steering test that cannot fail
geonnave Sep 23, 2026
434a63a
dotbot/tests: drop the tyre test that restates the camera pose code
geonnave Sep 23, 2026
df2b2d3
dotbot/tests: pin the robot detector's answer on a bench raster
geonnave Sep 23, 2026
b977119
dotbot/console-web: hold every floor point on screen when a side pane…
geonnave Sep 23, 2026
4249a95
dotbot/console-web: place the calibration camera once its pane toggle…
geonnave Sep 23, 2026
ba7e5ea
dotbot/console-web: hold a restored view under the zoom ceiling
geonnave Sep 23, 2026
fcd9616
dotbot/console-web: point the joystick knob along the pose's heading
geonnave Sep 23, 2026
2fe3480
dotbot/console-web: ignore key auto-repeat on the panel keys
geonnave Sep 23, 2026
81b1118
dotbot/console-web: describe the saved view as a centre and scale
geonnave Sep 23, 2026
33aa41b
dotbot/console-web: require the pose's wheels and radii the host alwa…
geonnave Sep 23, 2026
56e2637
dotbot: apply black, isort and pyupgrade to the pose geometry files
geonnave Sep 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 25 additions & 59 deletions dotbot/camera/detection/pose.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,37 +36,30 @@
from dotbot.robots import robot_geometry

_GEOMETRY = robot_geometry()
_CENTRE = _GEOMETRY.outline_centre


def _robot_frame(point):
"""A board-frame point in this module's robot frame (y flipped to forward)."""
return (point.x - _CENTRE.x, _CENTRE.y - point.y)


def _robot_frame_path(path):
"""One board-frame path as an array in this module's robot frame."""
return np.array([_robot_frame(p) for p in path], float)


# Outline centre to the axle, backwards.
AXLE_BEHIND_CENTRE_MM = 24.5
AXLE_BEHIND_CENTRE_MM = -_robot_frame(_GEOMETRY.axle_midpoint)[1]

# Outline centre to the LH2 photodiode, forwards. The lighthouse reports the
# photodiode's position, so this is the offset that makes the camera's point
# and the lighthouse's point the same point.
PHOTODIODE_AHEAD_MM = _GEOMETRY.board_length_mm / 2 - _GEOMETRY.diode_to_front_mm

# Board outline in the robot frame, transcribed from the Edge.Cuts layer of
# the v3 main board: an 84 mm nose and the step down to the 57 mm tail at
# y = +1.5.
OUTLINE_MM = np.array(
[
(-42.0, 47.5),
(42.0, 47.5),
(42.0, 40.5),
(43.0, 39.5),
(47.0, 39.5),
(47.0, 1.5),
(28.5, 1.5),
(28.5, -47.5),
(-28.5, -47.5),
(-28.5, 1.5),
(-47.0, 1.5),
(-47.0, 39.5),
(-43.0, 39.5),
(-42.0, 40.5),
],
float,
)
PHOTODIODE_AHEAD_MM = _GEOMETRY.diode_ahead_of_centre_mm

# Board outline in the robot frame: an 84 mm nose and the step down to the
# 57 mm tail at y = +1.5.
OUTLINE_MM = _robot_frame_path(_GEOMETRY.outline_path)

# Outline centre to the tip of the nose, forwards: the outline's own extent.
NOSE_AHEAD_MM = float(OUTLINE_MM[:, 1].max())
Expand All @@ -78,17 +71,9 @@
TAIL_HALF_MM = float(np.abs(OUTLINE_MM[OUTLINE_MM[:, 1] < 1.5][:, 0]).max())
NOSE_BAND_MM = TAIL_HALF_MM + 4.5

# The two motor connectors, in the same robot frame.
CONN_MM = [
np.array([(-19.5, -29.5), (-7.5, -29.5), (-7.5, -19.5), (-19.5, -19.5)], float),
np.array([(7.5, -29.5), (19.5, -29.5), (19.5, -19.5), (7.5, -19.5)], float),
]

# The tyres, as the template draws them: the track between their centres,
# and one tyre's width and depth.
TRACK_MM = 85.0
TYRE_W_MM = 18.0
TYRE_D_MM = 40.0
# The two motor connectors and the two tyres, in the same robot frame.
CONN_MM = [_robot_frame_path(c) for c in _GEOMETRY.connector_paths]
WHEELS_MM = [_robot_frame_path(w) for w in _GEOMETRY.wheel_paths]

# The template's canvas, as a half-width in millimetres: the robot at any
# heading, with room for the search to slide it.
Expand Down Expand Up @@ -385,29 +370,10 @@ def poly(canvas, pts):
wheel = np.zeros_like(board)
conn = np.zeros_like(board)
poly(board, OUTLINE_MM)
axle = -AXLE_BEHIND_CENTRE_MM
for side in (-1, 1):
x0 = side * TRACK_MM / 2 - TYRE_W_MM / 2
x1 = side * TRACK_MM / 2 + TYRE_W_MM / 2
poly(
wheel,
[
(x0, axle - TYRE_D_MM / 2),
(x1, axle - TYRE_D_MM / 2),
(x1, axle + TYRE_D_MM / 2),
(x0, axle + TYRE_D_MM / 2),
],
)
for side in (-13.5, 13.5):
poly(
conn,
[
(side - 6, axle - 5),
(side + 6, axle - 5),
(side + 6, axle + 5),
(side - 6, axle + 5),
],
)
for tyre in WHEELS_MM:
poly(wheel, tyre)
for connector in CONN_MM:
poly(conn, connector)
self.maps = dict(
green=np.clip(board - wheel - conn, 0, 1), dark=wheel, red=conn
)
Expand Down
4 changes: 3 additions & 1 deletion dotbot/camera/detection/propose.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@

import numpy as np

ROBOT_MM = 95.0 # DotBot footprint, a physical fact
from dotbot.robots import robot_geometry

ROBOT_MM = robot_geometry().envelope_mm # the footprint a robot occupies

# Design constants, all in units of the robot or of the floor's own noise.
SAMPLES_PER_ROBOT = 12.0 # working grid: a robot spans this many px
Expand Down
16 changes: 11 additions & 5 deletions dotbot/camera/detection/robot.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
NOSE_AHEAD_MM,
OUTLINE_MM,
PHOTODIODE_AHEAD_MM,
WHEELS_MM,
OutlineFit,
Template,
axes,
Expand Down Expand Up @@ -197,7 +198,8 @@ def frame_pose(pose: Pose, area: Area, mm_per_px: float) -> dict:
"""One pose in frame millimetres and both heading conventions.

Every `*_mm` is frame millimetres, x right and y down, the same frame as
the area and as an LH2 position.
the area and as an LH2 position. `outline_mm` is the board path and
`wheels_mm` the two tyre rectangles, both already turned to the heading.

`heading_atan2_deg` is the detector's own convention: `atan2(dy, dx)` in
the y-down frame, so 0 = +x and +90 = +y. `heading_deg` is the robot
Expand All @@ -208,14 +210,18 @@ def frame_pose(pose: Pose, area: Area, mm_per_px: float) -> dict:
origin = np.array([float(area.x), float(area.y)])
centre = origin + np.asarray(pose.centre_px, float) * mm_per_px
right, forward = axes(pose.heading_atan2_deg)
outline = [
centre + p[0] * right + p[1] * forward for p in np.asarray(OUTLINE_MM, float)
]

def place(path):
return [
_mm(centre + p[0] * right + p[1] * forward) for p in np.asarray(path, float)
]

return {
"centre_mm": _mm(centre),
"photodiode_mm": _mm(centre + forward * PHOTODIODE_AHEAD_MM),
"nose_mm": _mm(centre + forward * NOSE_AHEAD_MM),
"outline_mm": [_mm(p) for p in outline],
"outline_mm": place(OUTLINE_MM),
"wheels_mm": [place(wheel) for wheel in WHEELS_MM],
"heading_deg": round(wrap180(pose.heading_atan2_deg - 90.0), 1),
"heading_atan2_deg": round(wrap180(pose.heading_atan2_deg), 1),
"green_flare": round(pose.green_flare, 3),
Expand Down
8 changes: 8 additions & 0 deletions dotbot/console-web/dev/simulator_init_state.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,11 @@ calibrated = 0xff
pos_x = 250
pos_y = 900
direction = 135

# No `direction`: advertises no heading, so the console's no-heading rendering
# has a live producer.
[[dotbots]]
address = "F00DFACE77777777"
calibrated = 0xff
pos_x = 900
pos_y = 1400
Loading
Loading