struct mola::LidarOdometry::Parameters::IMUGravityCorrection::MapGravity
Overview
Estimate the map-frame gravity direction online, instead of freezing it from one accelerometer average at the first keyframe.
The frozen capture is only as good as averaging_samples worth of accelerometer while the platform is not actually static: measured on a handheld sequence, independent 50 ms averages disagree by ~4 deg RMS, and whatever value happens to be captured then biases the verticality reference for the whole run.
mola::imu::MapGravityEstimator instead solves for gravity in the map frame (plus IMU biases) from preintegrated IMU and this odometry’s own relative attitudes and velocities, so platform acceleration cancels and no quasi-static window is needed. Its up_map and its earned sigma then replace the frozen ones.
#include <LidarOdometry.h> struct MapGravity { // fields bool enabled = false; uint32_t solve_every_n = 5; double min_interval_seconds = 1.0; bool log_only = false; bool relevel_map_frame = false; uint32_t relevel_min_intervals = 5; double relevel_min_tilt_deg = 3.0; mrpt::containers::yaml estimator_params; // methods void initialize(const Yaml& c); };
Fields
uint32_t solve_every_n = 5
Run a solve() every N closed intervals (a solve is a small Gauss-Newton over the window, but not free).
double min_interval_seconds = 1.0
Minimum wall-clock span [s] of one interval. Gravity is recovered as (v_to - v_from - R_from*dV)/dt, so a velocity error eps shows up as a gravity error eps/dt: closing an interval every scan (dt ~ 0.1 s) turns a 0.1 m/s velocity error into ~6 deg of apparent tilt.
bool log_only = false
Run the estimator and log its result, but do NOT let it influence the verticality constraint. The trajectory then comes out identical to a run with enabled: false, which is what makes the estimate scoreable against ground truth: with the feedback loop closed, the map frame it is estimating is partly its own doing.
Use this to validate the estimator on a new dataset before trusting it, and keep it OFF in production.
bool relevel_map_frame = false
Rotate the MAP FRAME itself, once, so that it becomes gravity-aligned, instead of only feeding the per-scan verticality prior. The map frame is the initial body frame, so on a platform that starts tilted the whole map leans by that tilt for the rest of the run, and no later mechanism removes it.
This is a GAUGE change, not a state update: the local map, the simplemap, the trajectory, the state estimator and the published odom frame are all rotated together about the map origin, so every relative quantity is preserved exactly. It happens at most once per session, and never after a map has been loaded or geo-referenced.
Off by default: it changes the frame every product of the run is expressed in.
uint32_t relevel_min_intervals = 5
Number of intervals the estimator must hold before its estimate is used to re-level the map frame. Deliberately NOT min_intervals_for_convergence (which gates the per-scan prior, a different consumer with different needs): for leveling the map once, the estimate is at its best at the FIRST solve and slowly degrades afterwards, so waiting is harmful. The default corresponds to that first solve under the shipped solve_every_n.
double relevel_min_tilt_deg = 3.0
Minimum estimated tilt [deg] for the re-level to be worth doing. Mandatory, and not a formality: the estimate carries an error of its own, so correcting a map frame that is already level replaces a small error with a larger one.
The gate is applied to the ESTIMATE, but the quantity that has to be large is the TRUE tilt, and the two differ by that same error. So the threshold is set at about twice the measured p90 error of the estimate at its firing point, not at one times it. Below the threshold the correction is permanently stood down (and logged), rather than retried later.
mrpt::containers::yaml estimator_params
Options forwarded verbatim to mola::imu::MapGravityEstimator, so its parameters do not have to be mirrored here. Note that its own defaults are tuned for a different use: window_size in particular wants to be much larger here (100+ rather than 20), since the whole point is that verticality information accumulates.