Skip to main content

Crate rigidity_graph

Crate rigidity_graph 

Source
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.
EdgeReport
What one edge is doing after a solve.
NodeReport
How well the survey determines one station.
OptimiseParams
How hard to try.
PoseGraph
Nodes, edges, and the optimisation over them.
Report
What an optimisation did.
Shape
What the edges add up to.

Enums§

GraphError
What can be wrong with a graph.

Functions§

calibrated_information
JᵀWJ in calibrated units, with the directions the geometry cannot see at all left out.