Skip to content

feat: decode Q7 (B01) map geometry — dock, robot pose, path and rooms - #911

Open
andig wants to merge 5 commits into
Python-roborock:mainfrom
andig:feat/q7-map-geometry
Open

feat: decode Q7 (B01) map geometry — dock, robot pose, path and rooms#911
andig wants to merge 5 commits into
Python-roborock:mainfrom
andig:feat/q7-map-geometry

Conversation

@andig

@andig andig commented Aug 4, 2026

Copy link
Copy Markdown

Summary

Decodes the previously unmapped SCMap RobotMap protobuf fields for Q7 (B01) devices and renders dock, robot position and cleaning path with the shared V1 glyphs — the top open item ("Map") of #739, and the Q7 side of #827.

Field semantics were established empirically by diffing live MQTT captures from a Q7 Series (roborock.vacuum.sc05, fw 03.01.80) while docked, cleaning and returning to dock:

field name content
5 mapInfo (repeated) saved-map list (id + name)
6 historyPose cleaning path points (meters); grows during a clean
7 chargeStation dock pose (x, y, phi)
8 currentPose live robot pose + path index + activity flag
9 areaInfo (repeated) zone polygons
13 roomMatrix room matrix bytes
14 roomOutline (repeated) per-room boundary pixel chains + room-to-room border chains

Changes

  • Extend b01_scmap.proto with the messages above and regenerate b01_scmap_pb2.py (protoc gencode 6.31.1, matching the checked-in file).
  • B01MapParser projects into MapData:
    • charger from chargeStation;
    • vacuum_position from currentPose — saved maps carry a (1100.0, 1100.0) placeholder pose, so out-of-map-bounds poses are rejected and the robot falls back to its dock;
    • path from historyPose;
    • rooms (bounding box + label position + name) from roomOutline + roomDataInfo.
  • Render charger/vacuum/path with the shared V1 ImageGenerator (same approach as the Q10 renderer); raster converted to RGBA for glyph compositing.
  • Correct the occupancy value comment: 127 is floor, 128 is wall (verified against the rendered floor plan and pixel histogram: ~28k floor px ≈ 70 m² home vs ~2.6k wall px).

Validation

  • 4 new unit tests: world→pixel projection, placeholder-pose rejection, room-outline extraction, existing fixture unchanged.
  • Full test suite passes locally (one pre-existing unrelated failure in test_web_api::test_url_cycling fails on a clean checkout too); ruff check/format clean.
  • Live-tested against a real sc05: saved map renders dock + docked robot in the correct rooms; during a clean the robot marker and path track the actual movement.

Companion PR (independent): live map pushes — the same capture showed Q7 devices stream these frames unsolicited during cleaning.

🤖 Generated with Claude Code

Copilot AI lite review requested due to automatic review settings August 4, 2026 10:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for decoding and rendering additional Q7 (B01) SCMap geometry by extending the protobuf schema and projecting decoded poses/path/rooms into the shared V1 MapData + renderer pipeline.

Changes:

  • Extended b01_scmap.proto with additional Q7/B01 map fields (poses, path history, areas, room matrix/outline) and regenerated the checked-in *_pb2.py.
  • Updated B01MapParser to project charger pose, robot pose, and cleaning path into MapData, and to derive room bounding boxes from roomOutline.
  • Added unit tests covering world→pixel projection, placeholder-pose rejection, and room-outline extraction.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 2 comments.

File Description
tests/map/test_b01_map_parser.py Adds unit tests for pose/path projection, placeholder pose handling, and room-outline extraction.
roborock/map/proto/b01_scmap.proto Extends the B01/Q7 SCMap proto2 schema with newly decoded messages/fields.
roborock/map/proto/b01_scmap_pb2.py Updates checked-in protobuf gencode to match the extended schema.
roborock/map/b01_map_parser.py Projects decoded charger/robot/path/rooms into MapData and renders overlays using the shared V1 image generator.
Files not reviewed (1)
  • roborock/map/proto/b01_scmap_pb2.py: Generated file
Suppressed comments (2)

roborock/map/proto/b01_scmap_pb2.py:32

  • The generated module name passed to BuildTopDescriptorsAndMessages is now 'b01_scmap_pb2', but this file is imported as roborock.map.proto.b01_scmap_pb2. This mismatch changes the generated message classes’ module and can break pickling / dynamic imports and some protobuf reflection use cases. Regenerate with the correct module path (or update this line) so it matches the actual import path.
    roborock/map/proto/b01_scmap_pb2.py:19
  • The runtime version validation call embeds the proto filename ('b01_scmap.proto'), which no longer matches the proto path used elsewhere in the repo/regeneration instructions. Keeping it consistent with the checked-in proto path makes it easier to trace where this descriptor came from when debugging descriptor collisions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread roborock/map/proto/b01_scmap_pb2.py
Comment thread roborock/map/b01_map_parser.py Outdated
Decode the previously unmapped SCMap RobotMap fields, established
empirically from live MQTT captures of a Q7 Series (roborock.vacuum.sc05):

- 5 mapInfo: saved-map list (id + name)
- 6 historyPose: cleaning path points (meters)
- 7 chargeStation: dock pose
- 8 currentPose: live robot pose with path index and activity flag
- 9 areaInfo: zone polygons
- 13 roomMatrix, 14 roomOutline: room boundary pixel chains and
  room-to-room border chains

The parser now projects dock, robot position (falling back to the dock on
saved maps, which carry a (1100, 1100) placeholder pose), cleaning path and
room bounding boxes + label positions into MapData, and renders the shared
V1 glyphs (charger, vacuum, path) through the common image generator, same
as the Q10 renderer.

Also corrects the occupancy value comment: 127 is floor and 128 is wall
(verified against the rendered floor plan).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@andig
andig force-pushed the feat/q7-map-geometry branch from 379a4ed to e16bd49 Compare August 4, 2026 10:40
andig and others added 4 commits August 4, 2026 12:49
The occupancy grid carries no room ids, so each room is flood-filled from
its label position, bounded by walls and the roomOutline boundary chains.
Room colors come from the shared adjacency-aware V1 palette and room names
are drawn through the standard ROOM_NAMES drawable.

A fill that escapes a gapped outline would flood the whole floor, so fills
larger than half the floor area are discarded and those pixels keep the
plain floor color.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Use the shared V1 palette roles, matching the Q10 renderer and the app
look: transparent outside, GREY_WALL for walls and interior obstacles,
MAP_INSIDE for floor not assigned to any room.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Project areaInfo zone polygons into pixel space and draw them through the
shared no-go drawable. Per-kind type mapping (no-go vs no-mop) is left for
when more zone samples are available.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Carpets arrive as RobotMap field 20 (id, type, four vertices in meters,
enabled flag), confirmed live by adding a carpet in the app and diffing
map frames. Enabled carpet rectangles are stippled into the raster with a
checkerboard texture like the V1 carpet look, and exposed as the same
flat top-down carpet_map contract the Q10 parser uses.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

2 participants