Expand description
Pose-graph optimisation with each edge’s information in calibrated units.
Nodes, edges, Gauss–Newton on SE(3), an anchor — and
calibrated_information, which is where this crate differs from the
ordinary machinery, though by much less than it once claimed.
§What this crate set out to do, and what measurement left of it
Every pairwise registration produces JᵀWJ at its solution, and every
pose-graph package in this space takes that matrix at face value. Until
0.1.1 this crate said that was wrong — that an edge down a corridor
should carry no weight along the axis the geometry never determined,
not a small one — and thresholded the spectrum accordingly. On scenes
generated here that wins by 120×.
It does not survive real data. Against theodolite ground truth on the
ETH ASL surveys the threshold never once beat plain JᵀWJ and lost by
as much as 3.4×, and four further ways of reshaping the matrix lost
too. The reason is that an edge’s error is a bias rather than
scatter, and that the bias lies away from the best-determined
direction — which is what the anisotropy of JᵀWJ already says. The
shape was right; only the scale was wrong, and a scale common to every
edge does not move a survey.
So calibrated_information no longer thresholds. It puts the
project’s calibration into the matrix and leaves out a direction that
is genuinely blind, and is otherwise JᵀWJ/σ². Its documentation
carries the numbers; degenerate_leg in this crate’s tests carries the
assertions, including the old comparison kept as an equality so that
reintroducing a threshold quietly would move a number somebody has to
argue for.
What survived is the diagnosis rather than the weight: which
directions are weak is worth reporting, and
Conditioning::classify
still reports it for a single edge while PoseGraph::diagnose reports
it for the survey. What conditioning cannot report at all is whether a
registration landed in the right minimum; for that, see
rigidity_pipeline::median_absolute_residual.
§Frames, and the one thing that will go wrong if they are misread
A node’s pose is world-from-scan: it carries that scan’s own
coordinates into the survey. An edge from i to j measures
Z ≈ T_i⁻¹·T_j — scan j’s coordinates expressed in scan i’s — which
is exactly what rigidity_core::icp returns when j is the source and
i is the target.
The edge’s information matrix lives in the tangent at Z, under a
left perturbation, in scan i’s frame. That is not a choice made
here: the ICP updates its pose as T ← exp(Δξ)·T and builds its
Jacobian rows from points in the target’s frame, so IcpResult:: information is already in those coordinates and
calibrated_information restates it in the same ones. An
information matrix in the wrong frame does not fail loudly — it
converges to a slightly wrong answer, which is the failure mode this
paragraph is here to prevent.
Nodes are perturbed on the right, T ← T·exp(δ), because that keeps
each increment in the body frame of its own scan, where the
measurements were taken.
§Determinism
Single-threaded, and the dense solve is a fixed sequence of operations on a fixed matrix, so the result does not depend on a thread count that does not exist. A few hundred poses is a 1200×1200 Cholesky, which is milliseconds; sparse storage waits until a survey asks for it.
Structs§
- Diagnosis
- The survey, seen whole.
- Edge
- One measured relative pose, and how much of it to believe.
- Edge
Report - What one edge is doing after a solve.
- Node
Report - How well the survey determines one station.
- Optimise
Params - How hard to try.
- Pose
Graph - Nodes, edges, and the optimisation over them.
- Report
- What an optimisation did.
- Shape
- What the edges add up to.
Enums§
- Graph
Error - What can be wrong with a graph.
Functions§
- calibrated_
information JᵀWJin calibrated units, with the directions the geometry cannot see at all left out.