Files
NODEDC_MISSION_CORE/docs/audits/2026-09-21-smooth-map-correction.md
T

8.7 KiB
Raw Blame History

Offline smooth correction of a recorded ring

Decision and boundary

Implemented and exercised an experimental map derivative, not a live SLAM replacement, planner change or automatic reference promotion. The real input and all detailed geometry remain under ignored private runtime storage. No scanner command, capture mutation, viewer publication or vehicle command was made.

The result supports proceeding with review of a corrected reference. It does not establish absolute positioning accuracy or independent repeat-pass acceptance.

Implementation

  • src/k1link/reconstruction/smooth_correction.py: vendor-neutral smooth field C(s), where s is cumulative distance in the original recorded trajectory.
  • experiments/reconstruct_recorded_ring.py: K1-specific immutable-source adapter, source hashing, frame split, surface measurements, candidate solver, local registration checks and full-resolution derivative materialization.
  • experiments/review_recorded_ring.py: fixed-population cross-visit holdout and fresh-frame checks of sensitive matching windows; no re-fit of the correction.
  • tests/test_smooth_map_correction.py: synthetic invariants and negative cases.
  • pyproject.toml, uv.lock: optional map-correction extra with SciPy 1.16.2 and the existing small-gicp 1.0.1. No new mandatory runtime import.

Capture remains plugin-owned. Reconstruction has no plugin, UI, planner or hardware imports. Planning may later consume a separately admitted derivative; there is deliberately no runtime path that changes the current reference.

Model, not recovered ground truth

The field has natural cubic splines for translation and rotation-vector parameters, 20 m knot spacing, and an explicitly recorded first/second derivative penalty. Rotations act about a geometry-bound pivot so changing world coordinates does not change the physical answer. Three-point Gauss quadrature integrates the squared spline derivatives; the first knot fixes the gauge.

A surface link measures C(query) = C(reference) × T(reference, query), evaluated at the observed patch center and in orientation. No endpoint-position equality, flat-ground constraint or endpoint-derived initial guess is introduced. The weights are engineering regularization scales, not sensor covariance.

Each whole source cloud frame receives one rigid transform, and corresponding pose positions/orientations receive the same field. Thus within-frame distances are preserved, apart from float32 serialization. Different frames may move relative to one another; that is precisely the deformation being tested.

This field is parameterized by traversal, not a single XYZ warp that would move two different visits identically. Small-rotation chart admission is explicit; large corrections, arbitrary loop discovery and scale to multi-kilometre graphs remain unqualified. Current CPU least-squares uses a dense numerical Jacobian.

Input and separation

The private ring source has 5,178 cloud frames, 5,182 poses and 17,669,672 points, with a 579.797 m recorded trajectory. Full decode and source SHA-256 checks precede and follow the operation. Source sequences are continuous. The export preserves every point; the solver/validation sample every eighth source point and use 0.25 m voxels. Spatial windows are declared computation profiles, not capture-distance or vertical-clipping limits.

Every ten seconds, the two-second interval at phase 46 seconds is withheld: 1,061 frames withheld, 4,117 retained. No withheld frame enters the reference map or the surface-link fits. Upstream K1 mapping still makes these observations correlated; this is neither an independent recording nor ground truth.

Cloud/pose binding uses the existing host-monotonic chronology. Nearest pose receipt distance is 25 ms p95, 143 ms maximum. This is not proven hardware synchronization and does not recreate per-point firing times or native sweeps.

Actual constraints admitted

The start-area revisit qualifies on retained frames with the unchanged production GICP policy: 98.70% overlap within 0.5 m and 0.120 m inlier RMSE. Forward/reverse fits differ by approximately 5 mm at the patch center and 0.191 degrees.

All 29 attempted additional neighboring-window registrations were rejected under the unchanged overlap/RMSE gates. They were excluded, not assigned low quality but positive identity. Therefore this result uses one measured closure and a smoothness prior, not a densely measured multi-edge reconstruction of the drift history. Rejected measurements are retained in the private audit.

Results

Check Original Corrected v2
Trajectory length 579.797 m 579.983 m
Final minus initial Z 1.4646 m +0.00265 m
Local held-out registration candidates 51 / 52 52 / 52
Median local overlap within 0.5 m 98.259% 98.285%
Median local inlier RMSE 0.16246 m 0.16192 m
Cross-visit held-out overlap within 0.5 m 25.54% 89.43%
Cross-visit all-point distance median 0.891 m 0.119 m
Cross-visit all-point distance p95 1.825 m 0.906 m

The cross-visit comparison uses the same 16,109 source points from 41 withheld frames, compared to only the first retained window, with the correction frozen and no re-fit. The remaining outlier tail is real and is not explained away as vegetation without classification. Millimetre endpoint-Z agreement is not millimetre map or localization accuracy. Physical endpoint locations were not measured and may differ; no exact physical endpoint offset was imposed.

Maximum displacement of the sampled trajectory is 1.548 m. The maximum change of displacement along it is 8.61 mm per travelled metre (p95 7.99 mm/m). This is a correction-field gradient, not a measured strain bound for every 3D surface. The maximum rotation-parameter gradient is 0.000786 degrees/m. Halving/doubling the smoothness weight changes trajectory positions by less than 0.008 mm in this one-closure case; that only shows this prior's numerical stability, not truth.

Registration sensitivity retained

The first candidate had 51/52 accepted windows, with a non-convergence at 437 m; the original had a non-convergence at 327 m. No quality threshold was lowered. Fresh disjoint one-second halves in both regions passed for both maps.

After changing the field's coordinate pivot and exact quadrature (v2), its geometry is nearly unchanged but all 52 two-second windows pass. This is evidence of numerical/window-composition sensitivity of GICP convergence, not proof that its failure mode has been fixed. Both revisions and their less-favorable results are preserved. Production matching/loss/recovery code is unchanged.

The local test starts at identity, then uses the last accepted transform; it does not seed each query with the correction field. The test is local matching, not global place recognition or the entire live state machine. Agreement with the generating field must not be called independent pose accuracy.

Reproduction and storage

Install the optional extra through uv in a project environment. Run the experiment with --raw, --sha256, --session-id and a fresh --output directory. An optional --cache reuses a hash-sealed decoded source. Subsequent review takes the result directory and can include additional explicitly named window groups. Outputs are exclusive-created, not overwritten.

corrected-points.f32 is little-endian Nx3 float32 map coordinates. Frame offsets, counts and receipt times are retained in corrected-trajectory.npz; distance_m and frame_distance_m there are original source traversal parameters, not recomputed corrected path lengths. Intensities are unchanged and remain in the hash-sealed source cache. correction.json records the field including its pivot. The review seal binds code files and measured artifacts by SHA-256.

Acceptance and next step

  • 17 focused tests pass, including existing registration regression tests.
  • Ruff and diff whitespace checks pass.
  • One sequential CPU job at a time; no duplicate server, Docker workload or temporary worker remains. The canonical service stays on port 8000.
  • Candidate only; neither stored session nor operational reference replaced.
  • Next: versioned reference admission and independent repeat-pass verification, retaining recoverable localization state for numerical matching failures.
  • Ops documentation write is pending: instruction/project MCP reads timed out; no card was changed and no raw API fallback was used.

Subsequent same-day progress: the separate bundle and offline planning adapter are verified in 2026-09-21-map-reference-version-admission.md. Source/default live selection remains unchanged. Ops metadata reads recovered, but card searches still time out, so the engineering card update remains pending.