Preserve the completed teach-and-repeat laboratory stage: reference preparation, cascaded acquisition, local tracking and recovery, recording lifecycle, replay qualification, and persistent Rerun scene controls. Document the open grid-picking regression and Rerun upgrade contract. No autonomous driving or loop-closure optimization is claimed.
207 lines
12 KiB
Markdown
207 lines
12 KiB
Markdown
# Route-wide stationary relocalisation — implementation boundary
|
||
|
||
## Intent
|
||
|
||
Replace the laboratory assumption that an independent scanner session starts at
|
||
the first pose of the selected route. A stationary prefix now retrieves
|
||
candidate places from the full selected route before local GICP refinement.
|
||
This supports recovery after an interrupted run at a known part of a route; it
|
||
does not grant any rover, scanner, or navigation authority.
|
||
|
||
## Implemented protocol
|
||
|
||
1. The existing ten-second stationary prefix and its identity, monotonic-clock,
|
||
gap and motion fences are unchanged.
|
||
2. `route_relocalization.py` indexes the full reference map in a 10-m spatial
|
||
grid and resamples the *entire selected path* at five-metre atlas anchors.
|
||
Each anchor receives a compact rotation-invariant radial/height descriptor
|
||
of its local reference submap. The full map is not subject to the 100,000
|
||
point cap for one GICP target; only each local target is. The query
|
||
descriptor is ranked against every usable anchor, not against
|
||
`reference_path[0]`.
|
||
3. The six best descriptor candidates receive a local target and three
|
||
yaw hypotheses chosen from a polar descriptor. Every generated hypothesis
|
||
is qualified by the existing small-gicp implementation, with a separately
|
||
recorded relocalisation policy. The broader initial correction envelope is
|
||
only for this isolated initial search; routine tracking retains its local
|
||
policy.
|
||
4. Candidate transforms are clustered by their transformed stationary query
|
||
position. Similar-quality clusters at distinct route progress are rejected
|
||
as `ambiguous-route-location`; no candidate yields `no-route-location`;
|
||
a worker or search deadline yields `incomplete-route-search`.
|
||
5. Only a complete, singular candidate becomes a **provisional** prior. The
|
||
pre-existing three disjoint fresh registration windows still decide whether
|
||
tracking is established. Candidate output retains
|
||
`localization_confirmed=false` and `vehicle_control=false`.
|
||
|
||
All route-search policy values, descriptor coverage, generated-attempt count,
|
||
clusters and chosen route progress are stored in the initialization artifact.
|
||
The 30-s computational deadline deliberately remains below the bootstrap's
|
||
40-s source-age fence; a timeout is an observable incomplete calculation, not
|
||
a geometric rejection or a stale provisional position.
|
||
|
||
## Operator-visible states
|
||
|
||
- During retrieval: **«Поиск положения на маршруте»**.
|
||
- No stable place: **«Синхронизация маршрута не выполнена»** with a request to
|
||
remain near an explored area and repeat stationary calibration.
|
||
- Repeated/similar geometry: an explicit multiple-similar-segments status; the
|
||
system does not select one arbitrarily.
|
||
- Incomplete computation: **«Синхронизация маршрута не завершилась»**, distinct
|
||
from a geometric no-match.
|
||
|
||
## Validation completed in code
|
||
|
||
- 90-m synthetic route with the query constructed at the middle: descriptor
|
||
retrieval chose 40–45 m and real GICP accepted the fit at 100% overlap and
|
||
0.0165 m RMSE.
|
||
- Separate synthetic duplicated-place case rejects as ambiguous and clears
|
||
correspondence colouring.
|
||
- The bootstrap rejects a route result whose declared generated-attempt count
|
||
does not match its recorded attempts.
|
||
- Existing causal stationary bootstrap, route-length and live ownership tests
|
||
remain green.
|
||
|
||
## Not yet demonstrated
|
||
|
||
This is an implementation and synthetic qualification, not proof on the K1
|
||
field data. It does not establish capture radius, recovery at 10 m offset,
|
||
recovery in the middle of the present 100-m route, kilometre-scale memory/time,
|
||
mount transferability, absolute localisation accuracy, obstacle handling or
|
||
autonomous vehicle motion. The next physical acceptance should start a fresh
|
||
stationary run away from the route start, first around the current known route
|
||
and then at a deliberately chosen mid-route point. Preserve each result,
|
||
including explicit no-match, ambiguity or incomplete-search outcomes.
|
||
|
||
## 2026-09-20 field finding and corrective decision
|
||
|
||
The latest independent field attempt, `ja-sun-010-100m-10left`, started about
|
||
10 m opposite the normal route entry and reported `no-route-location`. This
|
||
was not a route-entry-radius rejection: all 18 generated route hypotheses ran.
|
||
The descriptor ranked the known entry region first (then the 5 m and 10 m
|
||
anchors). Its best GICP result converged with 61.25% overlap, 0.811 m
|
||
correction and 5.605° rotation, but was rejected solely because its 0.27594 m
|
||
inlier RMSE exceeded the common 0.25 m tracking threshold.
|
||
|
||
The diagnosis also found a conflicting implementation detail: the stationary
|
||
query accumulator silently retained only a 20 m radius even though the route
|
||
search is intended to use the useful K1 scene around the operator. Therefore
|
||
the correct first change is **not** a global threshold reduction.
|
||
|
||
- `route-relocalization/v2` explicitly records a 40 m stationary query radius
|
||
for whole-route initialisation and its three fresh local confirmations. It
|
||
is a numerical input footprint only: it is not a cap on route length,
|
||
recording duration, scanner range, or the normal live presentation budget.
|
||
- `LiveCloudBuffer`, `StationaryPrefix` and the fresh-confirmation buffer now
|
||
receive that radius explicitly and persist it into each staged calculation.
|
||
Ordinary short-range tracking retains its 20 m default profile.
|
||
- The rejected-run screen now provides **«Переинициализировать»**. It is
|
||
admitted only for a running, unconfirmed `lost` state. The action clears the
|
||
old derived preview, prior and result; it does not send a K1 command, stop
|
||
recording, reuse an old cloud, or start a vehicle. The next pose/cloud pair
|
||
starts a new ten-second stationary prefix and is stamped as the next attempt
|
||
in the run evidence.
|
||
|
||
The 0.25 m tracking policy remains unchanged. Lowering it everywhere would
|
||
weaken routine motion tracking and incorrectly treat a one-shot recovery
|
||
near-miss as ongoing confirmation. If the repeat with the 40 m initial scene
|
||
again selects the correct local anchor but stays in the 0.27–0.30 m band, the
|
||
next bounded change is a separate provisional-initialisation policy, locked to
|
||
that selected local target and still requiring all three disjoint fresh windows
|
||
to agree before any tracking presentation. It must not alter vehicle
|
||
authority, which remains false in this laboratory profile.
|
||
|
||
Code validation covers the 40 m accumulator request, `lost → operator retry →
|
||
new prefix → second route search` without closing the source lease, the typed
|
||
HTTP endpoint, existing stationary/route cases and the Control Station build.
|
||
The required physical acceptance is a repeat of the same 10 m opposite-entry
|
||
placement: keep the scanner still for the full prefix, save the resulting run,
|
||
and compare candidate progress, overlap, RMSE, convergence and all three fresh
|
||
windows before considering a separate provisional band.
|
||
|
||
## 2026-09-20 `ja-sun-011` retry correction and 80-m footprint
|
||
|
||
`ja-sun-011-100m-10left` recorded the intended failure separately from K1
|
||
capture. Its first initialization generated all 18 route hypotheses in 2.94 s
|
||
and ended at `no-route-location`; the nearest entry hypothesis reached 57.82%
|
||
overlap but 0.2783 m RMSE, so it correctly did not bypass the 0.25 m gate.
|
||
|
||
The following retry began 0.16 s after the operator click, reached a new
|
||
`collecting` prefix, and then surfaced the generic
|
||
`Stationary prefix`/geometry failure as a terminal **«Совмещение остановлено»**.
|
||
The raw recording was still active, but the presentation made an operator
|
||
repositioning mistake look like a K1 or route-search stop. The old worker also
|
||
continued to ingest numerical receipts after an unconfirmed route loss, so
|
||
carrying the scanner could contaminate the next prefix.
|
||
|
||
`route-relocalization/v3` changes the boundary as follows:
|
||
|
||
- The stationary query, presentation head and candidate GICP target now use
|
||
K1's declared 80-m sensing footprint. This is not a maximum walk distance,
|
||
capture duration or route length. A dense target is deterministically
|
||
voxelised to the explicit per-GICP computational budget without reducing
|
||
its radial footprint.
|
||
- The 28-m radial/height descriptor remains only the compact retrieval key for
|
||
selecting places across the route. Expanding it to 80 m made neighbouring
|
||
anchors on the short synthetic route less distinctive. After retrieval, the
|
||
actual GICP comparison receives the complete 80-m query and local target.
|
||
- In unconfirmed `lost`, the raw K1 source continues but numerical receipts are
|
||
deliberately ignored. The operator may carry the scanner to another
|
||
observable point, stop there, then press **«Переинициализировать»**.
|
||
- If the button is pressed before the scanner has stopped, movement or a
|
||
receipt gap during the new prefix returns to actionable `lost` with a
|
||
reinitialization diagnostic; it does not terminate the research run or
|
||
issue any K1 stop command. The following retry starts a clean buffer and
|
||
prefix.
|
||
- The vertical **«Срез»** control remains a Z-height visualization filter. Its
|
||
7.5 m label is measured height in the currently rendered cloud, not an
|
||
80-m sensor or relocalization range. The Planning "Движок" panel now shows
|
||
the independent working radius explicitly.
|
||
|
||
Python validation covers 80-m capture, an 80-m dense target under the GICP
|
||
budget, full-route retrieval, and `lost → carry/retry interruption → retry →
|
||
second route search` while the exclusive source lease stays active. Control
|
||
Station typecheck, 872 unit tests and production build also passed. This remains
|
||
laboratory-only; the next field acceptance must preserve the retry result and
|
||
its artifacts before any threshold adjustment is considered.
|
||
|
||
## 2026-09-20 v4 hybrid restoration after `ja-sun-012` / `ja-sun-013`
|
||
|
||
The subsequent physical runs exposed that v3 had made normal startup depend on
|
||
the global target representation. It did not exhaust the route-search budget:
|
||
on `ja-sun-013`, it ranked the entry anchor first but the broad, voxel-reduced
|
||
target stopped at 70.69% overlap and 0.2577 m RMSE. That is not a valid reason
|
||
to relax the common 0.25 m acceptance limit.
|
||
|
||
`route-relocalization/v4` therefore uses a staged strategy,
|
||
`dense-start-first-then-route-recovery/v1`:
|
||
|
||
1. A normal start first runs the established 108-seed stationary acquisition
|
||
against a precise local reference window at the selected route start. Its
|
||
query footprint remains 80 m. A successful local result has to satisfy all
|
||
existing support, convergence and geometry checks; it remains provisional
|
||
until the three disjoint fresh windows confirm it.
|
||
2. Only a complete, honest local rejection enters full-route descriptor
|
||
retrieval. That fallback receives only the remaining search time under the
|
||
same 35-s / 40-s freshness fences. It cannot replace a valid dense start,
|
||
become vehicle authority, or silently reuse an old sample.
|
||
3. A retry whose prefix proves incomplete at `start_search()` now returns to
|
||
actionable `lost` with the reinitialisation diagnostic. It no longer
|
||
converts an operator repositioning attempt into terminal
|
||
**«Совмещение остановлено»**; raw recording remains under its existing owner.
|
||
|
||
Offline replay through the actual isolated worker, using unchanged saved
|
||
inputs, accepts both known-good runs in the dense-start stage:
|
||
|
||
- `ja-sun-013-100m`: 94.11% overlap, 0.1504 m RMSE, 108 attempts, 21.54 s
|
||
accumulated registration time.
|
||
- `ja-sun-012-100m-10left`, step 3: 90.09% overlap, 0.1808 m RMSE, 108
|
||
attempts, 17.13 s accumulated registration time.
|
||
|
||
The global recovery path is deliberately retained but not claimed as field
|
||
qualified yet. Its next bounded improvement is a coarse full-route retrieval
|
||
followed by a high-resolution local refinement *around the retrieved anchor*,
|
||
with separate field evidence at a deliberately chosen mid-route point. A
|
||
blind threshold reduction or an unbounded scan of the whole map remains out of
|
||
scope.
|