Skip to content

Commit d2b6822

Browse files
feat: decode Q10 map geometry metadata (#881)
* feat: decode Q10 map geometry metadata * fix: preserve Q10 decoded packet API
1 parent 031b9e2 commit d2b6822

9 files changed

Lines changed: 779 additions & 63 deletions

File tree

roborock/map/b01_grid_layers.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,62 @@ def solve_calibration(
200200
return best[1]
201201

202202

203+
def solve_calibration_with_origin(
204+
layers: GridLayers,
205+
points: list[tuple[float, float]],
206+
origin: tuple[float, float],
207+
*,
208+
resolutions: Iterable[float],
209+
y_signs: Iterable[int] = (1, -1),
210+
min_on_floor: float = 0.5,
211+
) -> GridCalibration | None:
212+
"""Fit resolution + Y orientation around a *known* grid-pixel origin.
213+
214+
Unlike :func:`solve_calibration`, the pixel origin ``(ox, oy)`` is fixed --
215+
e.g. read straight from the Q10 grid-frame header -- so this only sweeps the
216+
candidate ``resolutions`` and ``y_signs`` and keeps the placement landing the
217+
most ``points`` on floor. With the expensive 2-D offset slide gone, far fewer
218+
points are needed to confirm the fit, so it works from a short path rather
219+
than a dense clean. Returns ``None`` if no candidate lands a ``min_on_floor``
220+
fraction of points on floor (e.g. the origin or points are bogus).
221+
"""
222+
if not points:
223+
return None
224+
w, h = layers.width, layers.height
225+
ox, oy = origin
226+
classify = layers.classifier
227+
# 1 = floor, 2 = wall/background (blocked), 0 = other. Index by cell.
228+
klass = bytes(
229+
1 if (c := classify(v)) == LAYER_FLOOR else 2 if c in (LAYER_WALL, LAYER_BACKGROUND) else 0 for v in layers.grid
230+
)
231+
232+
best: tuple[float, GridCalibration] | None = None
233+
for resolution in resolutions:
234+
if resolution <= 0:
235+
continue
236+
for y_sign in y_signs:
237+
on_floor = 0
238+
blocked = 0
239+
for x, y in points:
240+
px = int(x / resolution + ox)
241+
py = int(oy - y_sign * y / resolution)
242+
if not (0 <= px < w and 0 <= py < h):
243+
blocked += 1
244+
continue
245+
k = klass[py * w + px]
246+
if k == 1:
247+
on_floor += 1
248+
elif k == 2:
249+
blocked += 1
250+
score = on_floor - 1.5 * blocked
251+
if best is None or score > best[0]:
252+
best = (score, GridCalibration(float(resolution), float(ox), float(oy), y_sign))
253+
254+
if best is None or best[0] < len(points) * min_on_floor:
255+
return None
256+
return best[1]
257+
258+
203259
def decompose_grid(
204260
width: int,
205261
height: int,

0 commit comments

Comments
 (0)