← Back to all work

SLAM · Registration · Rust

mapr: a Rust library for 3D mapping and reconstruction

Reusable building blocks for turning depth measurements into a map: camera models, alignment, motion estimation, and surface reconstruction. Developed independently and used in the scanner reconstruction engine.

Geometry an application can use

Reconstruction work keeps returning to the same ideas: interpreting a depth image, aligning two observations, correcting a trajectory, and combining measurements into a surface. I built mapr so those operations could be used and tested independently.

The library is under active development. The scanner reconstruction engine provides a concrete application for its interfaces, while recorded datasets and controlled experiments let individual algorithms be examined in isolation.

Leaving scheduling with the application

Each tracking or reconstruction stage reads the model, computes without borrowing it, then writes its result back. A caller can run those phases directly or arrange them across threads.

The scanner's development engine uses that interface to run tracking and backend optimization separately, releasing the shared model lock during computation. The library owns no threads, locks, or channels; the application controls scheduling and buffering.

Making changes measurable

Benchmarks distinguish data preparation, a single registration iteration, and a complete solve. Recorded depth frames exercise missing pixels and surface boundaries; synthetic scenes provide controlled geometry. Resolution, search radius, and iteration budget are explicit inputs.

Surface evaluation reports distance distributions together with coverage, so a small error on a small surviving portion of a surface remains visible. Trajectory checks compare estimates with reference poses after a specified alignment. Regression tests and release-mode CI exercise these components as the APIs evolve.

A pose-graph example

Small motion errors accumulate as a camera moves. Recognizing a place it has visited before adds a constraint between distant parts of the path, allowing the solver to correct the drift.

In this controlled experiment, mapr reduces trajectory error from 217 mm to 14 mm. The camera path comes from a recorded benchmark; the odometry and loop-closure errors are deliberately injected to exercise the optimizer.

Pose-graph optimisation closing accumulated drift A 272-keyframe camera loop around a room, drawn as four layers that can each be switched off in the key below. Odometry alone accumulates 217 mm RMSE of absolute trajectory error and visibly peels away from ground truth. After Levenberg-Marquardt pose-graph optimisation ties the graph together across 664 loop closures, the error falls to 14 mm and the estimate lies on ground truth.
A controlled pose-graph experiment using the TUM RGB-D freiburg1_room trajectory. Noise is injected into both odometry and loop closures. Toggle the layers to compare the reference, drifted, and corrected paths.

How the example is measured

The saved run uses 272 sampled poses from TUM RGB-D's freiburg1_room sequence and 664 loop-closure constraints. The optimizer takes six Levenberg–Marquardt iterations.

Translation error · best-fit rigid alignmentBeforeAfter
Root mean square216.9 mm14.3 mm

The alignment removes a global rotation and translation before measuring position error. Both odometry and closure measurements contain Gaussian noise: translation standard deviations are 16 mm and 20 mm respectively; rotation standard deviations are 0.65° and 0.8°.

Closure selection spreads constraints across the path and adds links for keyframes left uncovered by thinning. In the recorded comparison, 664 distributed closures reached 14.3 mm error; a denser set of 1,482 reached 13.6 mm. Reducing the count further to 259 raised the error to 21.5 mm. The experiment makes that accuracy-versus-work tradeoff inspectable.

Numerical implementation

Pose-graph optimization carries measurement uncertainty between coordinate frames using the SE(3) adjoint. Bundle adjustment exploits sparse block structure through a Schur complement. Robust losses progressively tighten the treatment of outliers, and surface fusion weights measurements by their uncertainty.

CPU and GPU computation share Rust kernel source, with GPU kernels compiled to SPIR-V. These choices sit beneath the application-facing operations, where they can be benchmarked and changed without making the caller manage the solver's internals.