2.13.1 (2026-08-25)
Merge pull request #94 from MOLAorg/feat/mm-filter-generators-create-layer mm-filter: create new metric map layers via generators:
Fix stale doc URLs missing the mp2p_icp_core path segment These sm2mm/mm-filter demo comments and the sm2mm.h docstring still pointed at pre-split paths (apps/sm2mm, apps/mm-filter, top-level demos/), left over from the mp2p_icp_core/mp2p_icp_viz package split. Same class of issue as flagged in review for the new demo added in this branch; fixing the other pre-existing occurrences found by a repo-wide search.
Address review: fix demo URL, doc precedence, and unresolved formula check - Fix stale doc link in the new demo (missing mp2p_icp_core path segment). - CreateMetricMapFromDefinition(): docs said “exactly one” of the two definition sources must be given, but the implementation actually prefers metricMapDefinitionIniFile if both are non-empty; reword to match. - Generator::createTargetLayerIfNeeded(): call Parameterizable::checkAllParametersAreRealized() before creating the map, matching the guard Generator::process() already has. Without it, a metric_map_definition formula (e.g. “$f{…}”) referencing a variable that’s never bound in this code path (createTargetLayerIfNeeded has no attached ParameterSource) would silently serialize as its unresolved 0.0 placeholder instead of raising a clear error.
Let mm-filter pipelines create new metric map layers via generators: mm-filter operates on an already-built .mm file, so it had no way to instantiate a brand-new layer of an arbitrary mrpt::maps::CMetricMap subclass (e.g. mola::KeyframePointCloudMap): that logic lived inside Generator, and only ran as a side effect of processing an observation, which mm-filter does not have. Factor the map-creation logic (TSetOfMetricMapInitializers + CMultiMetricMap, driven by a metric_map_definition YAML block) out of Generator::implProcessCustomMap() into a standalone mp2p_icp_filters::CreateMetricMapFromDefinition(), and expose it via a new Generator::createTargetLayerIfNeeded(), callable without any observation at hand. mm-filter now optionally parses a generators: section (same schema as sm2mm) and uses it only to pre-create each generator’s target_layer if missing, before running filters:. A plain FilterMerge step can then insert an existing point cloud layer into it, closing the gap reported in MOLAorg/mola#147.
Add feature-detection macro for nn_visit_pt2pl_candidates() Downstream code (e.g. mola) needs to build against both this and older mp2p_icp releases still distributed via ROS binary repos, which predate NearestPlaneCapable::nn_visit_pt2pl_candidates() and the PlaneCandidate/plane_candidate_visitor_t types.
Contributors: Jose Luis Blanco Claraco, Jose Luis Blanco-Claraco
2.13.0 (2026-08-21)
Merge pull request #91 from MOLAorg/feat/ndt-blend-matcher Add Matcher_NDT_Blend: a smooth alternative to the nearest-plane argmin
Restore include ordering in the matcher registry
Add Matcher_Points_Blend and Matcher_Points_KnnPlane Two more correspondence rules, at opposite ends of the same axis, so that the question “does the discreteness of the correspondence rule drive the sensitivity” can be measured rather than argued. Matcher_Points_Blend is the point-based counterpart of Matcher_NDT_Blend: the target is the distance-weighted mean of the map points in a radius instead of the nearest one, so it moves continuously as the query crosses the perpendicular bisector between two map points. temperature 0 runs the very same nn_single_search() query as Matcher_Points_DistanceThreshold with pairingsPerPoint 1, so it reproduces it exactly. The neighborhood is a radius query and never a fixed-k one: a point entering or leaving a top-k list is a harder flip than the one being removed. Its intrinsic cost is that a weighted mean of surface points is pulled toward the interior of the neighborhood, so the temperature has to be chosen against the map’s point spacing. Matcher_Points_KnnPlane goes the other way: k nearest map points, a least-squares plane through them, and three hard gates. Every gate is a constant or a function of a static property of the measurement, so nothing in it reads back the registration’s own quality. The plane is fitted by solving A x = -1 with a column-pivoting Householder QR in single precision rather than by an eigen fit, because the two are not numerically the same on near-degenerate neighborhoods and this matcher exists to reproduce a protocol faithfully. One consequence worth knowing: that form cannot represent a plane through the origin. Two hazards found while writing them, both silent: - nn_radius_search() applies a small hard cap on the number of results whenever maxPoints is nonzero, ranking candidates and keeping the best few. Only maxPoints 0 keeps a radius query a radius query, so that is what the blend passes, and the parameter that could change it is documented as such. - A k-NN query asked for more neighbors than the map holds can still return exactly k, padding the tail by repeating an earlier point under a fabricated zero distance. The “reject if fewer than k were found” gate therefore cannot be implemented as a size check; each neighbor is validated against its own coordinates instead. Matcher_NDT_Blend gains optional weight-distribution statistics, gated on MP2P_ICP_BLEND_STATS, reporting how many candidates a query really combines, and now rejects negative temperature and searchRadius rather than silently falling back to the unblended path.
Formatting, and keep the zero-temperature bounding-box test identical too At temperature 0 the search radius now reverts to distanceThreshold, so the bounding-box early-return matches Matcher_Point2Plane as well, not just the per-point query. Behavior is unchanged wherever the boxes overlap, which is every case exercised so far, but the control is now exact by construction.
Add Matcher_NDT_Blend: a smooth alternative to the nearest-plane argmin Matcher_Point2Plane resolves its candidate planes with a hard argmin, so an arbitrarily small pose change can swap the winner and move the residual by the full disagreement between the two candidates. Matcher_NDT_Blend keeps the same inputs and emits the same one point-to-plane pairing per local point, but takes a likelihood-weighted combination of the candidates instead of the best one, so the residual varies continuously with the pose. Solvers are unchanged. Candidates are read through a new NearestPlaneCapable::nn_visit_pt2pl_candidates(), whose default implementation reports only the best match, so maps that expose just an argmin keep working. Three details carry the behavior: - temperature 0 takes the plain nn_search_pt2pl() path and therefore reproduces Matcher_Point2Plane exactly, which makes it a usable control. - The weight fades to zero with zero derivative at searchRadius, so a candidate entering or leaving the window does so continuously; a hard cutoff would reintroduce the discontinuity at the window edge instead. - Normals are combined through their outer products rather than as vectors. The point-to-plane cost is invariant to a plane’s normal sign, and this keeps the blend invariant too; a vector average would instead depend on each map cell’s own sign bookkeeping. The cost is that two exactly perpendicular candidates carrying exactly equal weight switch rather than interpolate, which no single plane could represent anyway. Defaults reproduce the previous behavior, so no shipped pipeline changes.
Merge pull request #90 from MOLAorg/feat/decimate-adaptive-dynamic-budget FilterDecimateAdaptive: dynamic point budgets, and a bound on the voxel stride
Clamp the stride-derived budget at the input point count An arbitrarily small maximum_voxel_stride made ceil(nVoxels/stride) exceed uint32_t, and the narrowing conversion is undefined there. The input is the real ceiling anyway, since no decimation method can emit more points than it was given. Strides below 1 stay legal: the methods that take several points out of one voxel can honor them.
FilterDecimateAdaptive: dynamic point budgets, and a bound on the voxel stride The point budget and the voxel size were loaded with MCP_LOAD_*, which parses a YAML scalar up to its first non-numeric character and keeps the truncated value without complaint. A budget written as an expression therefore became something else entirely, silently. Both are now declared parameters, so they accept formulas over the pipeline’s variables like the range and map-size parameters already do. The sampling walk uses a stride of nVoxels/desired_output_point_count and stops as soon as the count is reached, so a stride above 1 means only one in every stride occupied cells is ever visited. An absolute point count thus expresses very different sampling densities depending on how many occupied cells the input happens to have, which is a property of the scene, the sensor and the voxel size rather than of the configuration. The new optional maximum_voxel_stride bounds that ratio directly. It only ever raises the effective count, so the absolute budget keeps acting as a floor and leaving it unset preserves today’s behavior exactly. Also: size the outputs vector up front. Declaring a dynamic parameter stores a pointer to its target field, and growing the vector afterwards would dangle the ones already declared. A DEBUG line now reports nVoxels, the effective budget, the stride and the emitted count per output, so a configuration can be checked against its actual input instead of assumed.
Merge pull request #89 from MOLAorg/feat/cov2cov-ambiguity-gating Matcher_Cov2Cov: optional range-adaptive matching distance and ambiguity gate
Merge branch ‘develop’ into feat/cov2cov-ambiguity-gating
Remove the ambiguity gate from MatchingDistanceProfile firstToSecondDistanceMin/firstToSecondMinRange were not yet justified by results (regressed translation error ~62% on a held-out sequence relative to the range-adaptive distance alone). Drop the fields, Matcher_Cov2Cov params, NearestPointWithCovCapable’s guard against them, and the corresponding test coverage, keeping only the range-adaptive matching distance.
Keep nn_search_cov2cov() source-compatible with older map implementations The flat-threshold overload goes back to being the pure virtual, and the MatchingDistanceProfile one becomes a non-pure overload whose default implementation forwards to it. A map class written against an earlier release therefore keeps compiling, overriding and behaving exactly as before, which is what the downstream CI jobs need: they resolve mola_metric_maps to the last released binary package, which is older than this source tree. The forwarding default refuses, rather than silently ignores, a profile such an implementation cannot honor: a range-adaptive distance or an active ambiguity gate throws instead of quietly degrading to a flat threshold. Adds MP2P_ICP_HAS_MATCHING_DISTANCE_PROFILE so downstream packages can detect the new API. Since MatchingDistanceProfile.h is an entirely new header, a plain __has_include() settles it for consumers; the macro is what survives if the struct later grows members. Also documents the name-hiding consequence: a derived class declaring only one of the two overloads hides the other for calls on that derived type. Harmless for the matchers, which always dispatch through a base reference.
Matcher_Cov2Cov: optional range-adaptive matching distance and ambiguity gate Introduce mp2p_icp::MatchingDistanceProfile, the acceptance criteria used by NearestPointWithCovCapable::nn_search_cov2cov(). It replaces the flat float max_search_distance argument, and is implicitly constructible from a float, so every existing caller keeps compiling and behaving as before. Two opt-in refinements over a single flat distance, both disabled by default: - Range-adaptive distance: thresholdFar, thresholdKneeRange and thresholdTransitionWidth turn the acceptance distance into a logistic function of the query point’s range from the sensor. Map point density falls off with range, so one flat threshold is loose near the sensor and tight far away. - Ambiguity test: firstToSecondDistanceMin rejects a correspondence whose runner-up candidate is within that ratio of the winner’s distance, i.e. the match is too close to call. firstToSecondMinRange restricts the test to beyond a given range, leaving the dense near field, where the runner-up is the same surface seen again, untouched. All five are declared with DECLARE_PARAMETER_OPT so they accept dynamic formulas like “3.0*ADAPTIVE_THRESHOLD_SIGMA”, exactly as threshold does. A static YAML load would convert such a string by stopping at the first non-numeric character and silently yield a fixed value in meters. No shipped pipeline enables either refinement; defaults reproduce the previous behavior bit for bit, including the k=1 KD-tree query and no per-point range computation.
Merge pull request #88 from MOLAorg/fix/matcher-param-formula-parsing fix: load matcher/filter distance parameters as dynamic formulas
fix: load matcher/filter distance parameters as dynamic formulas Several numeric parameters that pipelines legitimately want to express as formulas were read with MCP_LOAD_REQ/MCP_LOAD_OPT, a static YAML load. mrpt::containers::yaml::as<double>() truncates a string at the first non-numeric character, so “2.0*ADAPTIVE_THRESHOLD_SIGMA” was silently turned into the constant 2.0, with no warning and no error, and was never re-evaluated afterwards. Since ADAPTIVE_THRESHOLD_SIGMA is the output of the LO adaptive-threshold controller, the affected parameters were left frozen at a value that is not even in the right units. Switched to DECLARE_PARAMETER_*, the same macros the equivalent parameters of the other matchers already use: * Matcher_Point2Line::distanceThreshold. Same name and same role as Matcher_Point2Plane::distanceThreshold, which is dynamic and is set to “1.0*ADAPTIVE_THRESHOLD_SIGMA” in the shipped lidar3d-ndt pipeline. * Matcher_Adaptive::absoluteMaxSearchDistance, minimumCorrDist and planeMinimumDistance: all distances in meters gating pairing acceptance. * FilterCurvature::max_cosine, min_clearance and max_gap, which the sibling GeneratorEdgesFromCurvature already declares dynamically. Also added checkAllParametersAreRealized() to Matcher_Point2Line, Matcher_Adaptive and FilterCurvature, so an unrealized formula now fails loudly instead of running with a stale value. Tests: test-mp2p_matcher_pt2pt_parameterizable now covers Point2Plane, Point2Line and Adaptive; test-mp2p_matcher_pt2ln gets an end-to-end check that the formula actually gates pairings across two realize() calls; new test-mp2p_FilterCurvature. All of them fail against the previous code.
Merge pull request #87 from MOLAorg/perf/decimate-adaptive-flat-voxel-index-array Store voxel point indices in one flat array (recovers the FilterDecimateAdaptive regression)
Store voxel point indices in one flat array PointCloudToVoxelGrid kept a std::vector per voxel, that is, one heap allocation per voxel, rebuilt from scratch for every processed cloud. The point indices now live in a single contiguous array, with each voxel holding just an offset and a count into it, and are stored as 32 bit to halve the memory traffic. FilterDecimateAdaptive also merges all of its per-thread grids in one single call, so that the flat array is laid out once instead of once per merged grid. This matters because the thread-local grids accumulate over successive runs, and most of them are empty on any given one. The output is unchanged: voxel contents and their canonical ordering are the same as before.
Merge per-thread voxel grids in FilterDecimateAdaptive (#86) * Merge per-thread voxel grids in FilterDecimateAdaptive Under TBB the input cloud is binned in parallel into one grid per thread, but the voxel key is global, so a single spatial voxel was left split into one fragment per thread that saw points in it. The fragments were then visited as if they were independent voxels. That had three consequences: - The output depended on how TBB happened to split and steal work, so two identical runs gave different decimated clouds. - Decimation emitted up to one representative per thread per voxel instead of one per voxel, so the output was denser and less uniform than requested. - minimum_input_points_per_voxel was applied per fragment, so a voxel legitimately above the threshold was dropped whenever its points were split across threads. For a rotating lidar, whose consecutive point indices sweep azimuth, this affected most voxels. Reassemble the per-thread grids by voxel key before sampling, into an owning grid, which keeps voxel_t a plain non-owning span. Voxel point indices are then sorted so the contents no longer depend on the binning order. The voxel list is also given a canonical order, by the first input point of each voxel, in both the parallel and the sequential paths. The resampling walks this list with a stride, so which voxels reach the output would otherwise depend on hash map traversal order, which is an implementation detail of the map type. Adds regression tests over a cloud that revisits voxels at distant input indices, as a rotating lidar does. All three checks fail before this change. * Apply clang-format * Test mergeFrom() directly, independently of worker count The end-to-end fragmentation checks only exercise the merge when TBB actually runs the blocks concurrently, so on a low-core machine they would pass without touching it. Bin a cloud in three unequal pieces, merge, and assert the result equals binning it in one pass. * Require a matching map type in mergeFrom(), and cover both The four-way dispatch handled combinations that cannot occur: only FilterDecimateAdaptive merges grids, and it always uses robin maps. Assert the two agree and keep the two reachable branches. The merge test now runs over both backing map types and asserts they produce the same voxel contents, which is what the canonical voxel ordering is built from.
update robin-map to latest version (avoid cmake deprecation)
Merge pull request #85 from MOLAorg/fix/decimate-voxels-maybe-uninitialized-warning fix: spurious -Wmaybe-uninitialized in FilterDecimateVoxels::initialize_filter
fix: spurious -Wmaybe-uninitialized in FilterDecimateVoxels::initialize_filter Both optional grid members were unconditionally .reset() before .emplace()-ing only one of them, so the one about to be (re)constructed was torn down twice in quick succession (once explicitly, once inside emplace()). GCC 15’s flow analysis loses track of the pimpl’s function-pointer deleter across that redundant double-teardown and warns on an unrelated unique_ptr destructor. Only reset the other optional, which was the only thing the explicit reset() was ever needed for. Verified by compiling the function in isolation before/after: warning count goes from 1 to 0, no new warnings, and the pre-existing test suite (112/112) is unaffected.
Merge pull request #83 from MOLAorg/feat/decimate-adaptive-multi-output FilterDecimateAdaptive: several output layers from one voxelization pass, plus decimate_method
FilterDecimateAdaptive: several output layers from one voxelization pass, plus decimate_method Two independent additions to FilterDecimateAdaptive: 1) An optional “outputs” YAML sequence, each entry holding its own output_pointcloud_layer + desired_output_point_count. All of them are sampled from ONE voxelization pass (the dominant cost), each walking the shared voxel list with its own stride. The single-output keys keep working exactly as before, and are mutually exclusive with “outputs”. This lets a consumer obtain a dense cloud for its local map and a sparse one for ICP without paying for two full passes. Chaining two filters also computed the second cloud from already-decimated voxel statistics rather than from the full scan. Measured on test-mp2p_gicp_pipeline_benchmark (new env var MP2P_BENCH_SINGLE_DECIMATION=1 selects the single-pass variant there), 1st-pass filtering drops from ~15.0 ms to ~13.0 ms per 100k-point scan, with unchanged ICP quality and recovered pose. 2) Support for decimate_method, as in FilterDecimateVoxels. The DecimateMethod enum moves to its own header, mp2p_icp_filters/DecimateMethod.h, now shared by both filters (FilterDecimateVoxels.h includes it, so its users are unaffected). Since this filter revisits voxels in as many rounds as needed to reach the requested point count, the methods behave differently here: - FirstPoint (default, previous behavior) and RandomPoint take successive points out of each voxel, in insertion order or from a random per-voxel offset. - ClosestToAverage and VoxelAverage summarize the whole voxel and hence emit one point per voxel only, so the output cannot exceed the number of valid voxels. VoxelAverage synthesizes points, so per-point fields are not propagated. Their per-voxel results are precomputed once and reused by all output targets. Unit tests cover the multiple outputs, the mutually-exclusive parameters and the four decimation methods.
Merge pull request #82 from MOLAorg/feat/filter-transform-pointcloud Add FilterTransformPointCloud: rigidly transform a point-cloud layer by a pose or its inverse
test: cover view-direction field rotation in FilterTransformPointCloud Addresses CodeRabbit nitpick: the existing tests only exercised XYZ transform, never the rotateViewDirectionFields() path the filter also runs. Mirrors the fixture pattern already used in test-mp2p_FilterMerge_view.cpp.
fix: apply clang-format-14 to test-mp2p_FilterTransformPointCloud.cpp CI’s pinned clang-format-14 wants different alignment on the pose assignment line than my local (newer) clang-format produced.
Add FilterTransformPointCloud: rigidly transform a point-cloud layer by a pose or its inverse Some odometry systems (e.g. DLIO) voxelize incoming scans after transforming them into the map/world frame, so voxel membership is anchored to the map rather than to the vehicle’s instantaneous local frame. No existing filter in this library exposes that as a composable building block: FilterMerge only inserts local points into an existing target map (one direction, and tied to insertObservation()), so there was no way to get a plain, freshly transformed point-cloud layer usable as input to another filter (e.g. FilterDecimateAdaptive), nor any way to transform back by a pose’s inverse. FilterTransformPointCloud fills that gap: input layer -> output layer, replacing each point p_i by pose (+) p_i (or pose^-1 (+) p_i if invert_pose is set), reusing CPointsMap::insertAnotherMap() plus the same rotateViewDirectionFields() call FilterMerge already relies on to keep view-direction fields consistent.
fix format
Add robust_max_range(): percentile-based, outlier-robust point cloud extent A plain bounding-box max norm is dominated by a single spurious far return. Observed on Livox sensors (TIERS dataset): specular-reflection artifacts hundreds of meters away in an otherwise <20 m scene inflate any consumer’s observation-radius estimate 10-40x, which then starves a downstream range filter or voxel grid of the real, near-field points. robust_max_range() returns a percentile of the per-point range (default 0.95) instead of the raw maximum, computed with std::nth_element (O(n) average, no full sort). Not wired into any consumer yet; that is a separate follow-up.
Merge pull request #80 from MOLAorg/fix/viz-skip-non-finite-points fix: skip non-finite points when rendering a point map layer
fix: also drop non-finite points on the map’s own-renderer viz path get_visualization_map_layer() returns early when the layer is not a CPointsMap, or when keep_original_cloud_color asks for the map’s own colors: both delegate to map->getVisualizationInto(). That return happens before the source-map filtering added previously, so a map holding blank (NaN) storage slots could still hang the renderer through this path. Filtering the source beforehand is not possible here: the map draws itself, so there is no input cloud, and in the non-CPointsMap case there is nothing to filter either. Swapping in a filtered copy would discard the custom renderer that this path exists to use. The non-finite points are therefore dropped from the already-built render objects, in place, in the same loops that already walk them to apply the point size. Each surviving point keeps its own color, since colors are per point in the render object.
fix: skip non-finite points when rendering a point map layer get_visualization_map_layer() copied the raw point buffers of a map layer into a CPointCloud / CPointCloudColoured verbatim. Map classes that recycle storage slots expose slots holding no point: mola::IncrementalPointCloud blanks them to NaN, precisely so that generic code walking the inherited CPointsMap buffers does not resurrect evicted geometry. A single non-finite coordinate is enough to hang the GUI thread: MRPT’s LOD octree splits a node at the mean of its points, so the mean becomes NaN, every comparison against it is false, all points land in the same child and the recursive split repeats identically forever. Each level costs two passes over the whole cloud, so it presents as a frozen viewer rather than a crash. It only triggers once a cloud exceeds OCTREE_RENDER_MAX_POINTS_PER_NODE (1e6 by default), which is why it appears at one reproducible point of a mapping run. The source map is filtered, not the render object, because recolorize3Dpc() skips colorization unless both hold the same number of points. Maps whose points are all finite (the usual case) are passed through untouched, with no copy.
Merge pull request #78 from MOLAorg/feature/serialize-gravity-prior-in-icplog Record the GravityPrior in .icplog files
Record the GravityPrior in .icplog files mp2p_icp::GravityPrior was passed to ICP::align() but never serialized, so the verticality observation the solver actually received could not be recovered from a log afterwards. That matters because its sigma is not necessarily the configured one: callers may widen it at run time (mola_lidar_odometry’s adaptive_sigma does), and the only way to see the effective value was to re-run the whole dataset with debug-level logging. - Add CArchive operators for GravityPrior, plus DECLARE_TTYPENAME_CLASSNAME, which std::optional serialization requires. - Add LogRecord::gravityPrior, bumping the serialization version 2 -> 3. Older logs still load: the new field is read only when version >= 3. - Populate it in ICP::align(), next to the existing motion prior.
fix: remove racy per-point pt2pt weight path in Gauss-Newton solver (#77) * fix: remove racy per-point pt2pt weight path in Gauss-Newton solver Pairings::point_weights fed a sequential run-length-encoded cursor that optimal_tf_gauss_newton.cpp consumed from inside a tbb::parallel_reduce lambda captured by reference. Concurrent threads processing different point sub-ranges raced on the same shared cursor state, and the cursor was never reset across inner Gauss-Newton iterations either, making the per-layer weight: in pointLayerMatches silently unreliable in TBB builds. visit_correspondences.h had the same cursor pattern, though it was safe there since it only ever runs sequentially. The per-layer weighting granularity was never exercised with differing weights in any real pipeline (every pointLayerMatches block in-tree uses a single layer pair), so instead of making the cursor thread-safe, this drops per-point/per-layer pt2pt weighting entirely. All pt2pt pairs now use the single PairWeights::pt2pt scalar, matching every other pairing type. Matcher_Points_Base keeps pointLayerMatches for selecting which local/global layers to pair, just without a per-entry weight. Pairings serialization bumped to v3; older archives are still read correctly (the removed field is skipped). * docs: fix stale pointLayerMatches doc comment in Matcher_Points_Base Flagged by CodeRabbit on #77: initialize()’s docstring still described pointLayerMatches as relative weights after the per-layer weighting removal; the class member’s own docstring had already been updated.
feat: yaw-free rank-2 gravity prior for the Gauss-Newton solver (#76) Adds an optional gravity (“verticality”) observation, independent of the existing SE(3) prior, that constrains only the two tilt DOFs and leaves rotation about gravity and all three translations exactly free. Residual: r(R) = B^T (R u_body) in R^2, with B an orthonormal basis of the plane orthogonal to up_map, weighted isotropically by 1/sigma^2. The isotropy is what makes the cost invariant to rotation about gravity: the induced 6x6 information has rank 2, a zero translation block, and null space exactly span(u_body), so yaw stays free at any attitude. This is the correct way to express a verticality constraint, versus encoding tilt into a 6x6 SE(3) prior information matrix, whose diagonal only isolates roll/pitch near yaw=0 and inevitably couples into translation. Purely additive and inert unless a caller sets it. Also documents that the prior information matrix is expressed in the SE(3) Lie tangent, not MRPT’s (x,y,z,yaw,pitch,roll) Euler ordering; the two swap indices 3 and 5. New unit test with 4 cases: yaw recovery to <5e-7 deg at yaw in {0,45,90,170,-120}; leveling of a tilted solution; zero translation injected by a consistent prior; convergence with a non-level map frame.
Merge pull request #75 from MOLAorg/feat/expose-cov2cov-alpha Expose cov2cov_alpha and cov2cov_auto_balance_with_prior in Solver_Ga…
Expose cov2cov_alpha and cov2cov_auto_balance_with_prior in Solver_GaussNewton These OptimalTF_GN_Parameters knobs were hard-coded to their struct defaults (1.0 and true). Load them via MCP_LOAD_OPT and forward them to gnParams so a pipeline YAML can scale the cov-to-cov data block against the pose prior (e.g. to let an IMU gravity pitch/roll prior carry more relative weight).
sm-cli info: report the first keyframe’s pose Useful to recover the absolute orientation a map was actually built with, which is not always the one it was seeded with: MOLA-LO’s IMU-based initial leveling overwrites the configured pitch/roll at mapping time, so the only reliable source is the stored keyframe itself.
fix: dont throw if saving icplogs to cwd
fix(sm-cli): restore brackets stripped by CLI11 from tf pose argument CLI11 unconditionally strips a leading/trailing bracket pair from variadic option values, so the “[x y z yaw pitch roll]” pose string passed to sm-cli tf loses its brackets before CPose3D::fromString() sees it, which then throws since it requires them.
cmake: add git submodule early fail
Contributors: Jose Luis Blanco-Claraco
2.12.0 (2026-07-10)
add missing changelogs
Merge pull request #74 from MOLAorg/split-core-viz-packages Split repo into mp2p_icp_core (headless) + mp2p_icp_viz (GUI) + mp2p_icp (metapackage)
fix: apply clang-format-14 to TCLAP->CLI11 converted files Address CI failure: several files from the TCLAP-to-CLI11 conversion weren’t run through clang-format-14 before the previous commit.
Split repo into mp2p_icp_core (headless) + mp2p_icp_viz (GUI) + mp2p_icp (metapackage) Consumers that only need the headless C++ libraries and CLI apps no longer need to pull in mrpt_libgui (nanogui/GLFW/X11) or build the GUI apps. mp2p_icp remains as a backward-compatible metapackage depending on both, so existing <depend>mp2p_icp</depend> consumers are unaffected. - mp2p_icp_core: mp2p_icp_common/map/filters + the ICP algorithms lib + 13 headless CLI apps. Root find_package(MRPT COMPONENTS …) drops “gui”; mrpt::opengl/system/expr remain available transitively via maps->obs and tfest->poses->bayes->config. - mp2p_icp_viz: mm-viewer and icp-log-viewer, the only two apps that actually use mrpt::gui (sm-cli’s dependency on it was vestigial and dropped instead). - Removed the one real GUI leak in the mp2p_icp (ICP) library: an interactive CDisplayWindow debug feature in QualityEvaluator_RangeImageSimilarity (debug_show_all_in_window), which was the only reason that library linked mrpt::gui at all. The headless debug_save_all_matrices option remains. - Dropped the mola_common git submodule and its standalone-build bootstrap logic; standalone (non-colcon) plain-CMake builds are no longer supported, matching every other MOLAorg package in this ecosystem (find_package(mola_common REQUIRED) via the ROS workspace). robin-map stays vendored (no rosdep key, private dep of filters). - Replaced TCLAP with CLI11 for all CLI apps’ argument parsing (rosdep key “cli11”, same pattern already used elsewhere in MOLAorg repos). Behavior-preserving: same flags, defaults, and help text everywhere. - Removed .circleci/config.yml: its bare-cmake + PPA-installed libmrpt-dev jobs tested exactly the standalone-build path being dropped, and can’t provide mola_common without reintroducing the bootstrap complexity just removed from CMakeLists. The colcon-based GH Actions jobs (humble/jazzy) are now the CI. - Updated docs (agents.md, README.md, docs/source/index.rst) and check-clang-format.yml/formatter.sh for the new layout. Verified: mp2p_icp_core, mp2p_icp_viz, mp2p_icp all build via colcon; all 104 mp2p_icp_core tests pass; mp2p_icp_core binaries have zero GUI/nanogui/GLFW linkage; mm-viewer still loads and renders a real georeferenced .mm file; a real downstream consumer (mola_metric_maps) builds unmodified against the new layout. Note: mp2p_icp/CMakeLists.txt shows as “modified” rather than deleted+added in the diff – this is a git rename-detection artifact from the old mp2p_icp/ (ICP library) subfolder and the new mp2p_icp/ (metapackage) folder sharing the same repo-relative path after the library moved one level deeper into mp2p_icp_core/mp2p_icp/.
Contributors: Jose Luis Blanco-Claraco
2.11.0 (2026-07-04)
Merge pull request #72 from MOLAorg/fix/filterdeskew-graceful-imu-anchor fix(FilterDeskew): never let IMU trajectory errors crash the pipeline
Merge pull request #71 from MOLAorg/feat/prior-referenced-robust-kernel Prior-referenced robust kernel for the Gauss-Newton solver
Merge pull request #70 from MOLAorg/fix/filtermerge-view-vector-rotation Fix view-direction vector frame mismatch in FilterMerge
chore: demo sm2mm files updated for LIO localization maps
demos: add sm2mm_pointcloud_voxelize_merged_keyframe_map.yaml
Merge pull request #69 from MOLAorg/feat/filter-decimate-range-adaptive feat: FilterDecimateRangeAdaptive (EllipseLIO range-adaptive scan filter)
feat: add FilterBase::enabled for env-based pipeline toggling Adds an enabled boolean field to FilterBase (default: true)
feat: add FilterDecimateRangeAdaptive (EllipseLIO range-adaptive scan filter)
Fix build status badge for ROS 2 Lyrical arm64
docs: add ROS 2 Lyrical badge row, update Rolling to Ubuntu 26.04 (resolute)
Merge pull request #68 from MOLAorg/feature/filter-polygon-2d
Contributors: Jose Luis Blanco-Claraco
2.10.3 (2026-05-24)
Merge pull request #67 from MOLAorg/fix/deskew-empty-trajectory FIX: robust against temporary lack of IMU in Deskew
Merge pull request #66 from MOLAorg/add-gicp-benchmark test: add new end-to-end gicp test as benchmark
test: add new end-to-end gicp test as benchmark
fix: icp-log-viewer bug in translations if view prior was enabled
Contributors: Jose Luis Blanco-Claraco
2.10.2 (2026-05-11)
Merge pull request #65 from MOLAorg/simplify-ci CI: simplify CI scripts and docker install
chore: don’t use anymore map classes deprecated and to be removed in mrpt 3.0.0
fix: maps creating multiple CPointsCloud won’t have all with pointSize honored
Merge pull request #64 from MOLAorg/bump-cmake bump min req cmake version to 3.22
CI: Use sensible names for jobs matrix
bump min req cmake version to 3.22
Contributors: Jose Luis Blanco-Claraco
2.10.1 (2026-05-04)
FIX: sm2mm pipeline for keyframe maps need valid KF poses
FIX: copy/paste error in guard against missing layer
mm-viewer: UI now has an easier near/far clipping plane tool
chore: map contents as string made less verbose (hide full covariance)
Merge pull request #63 from MOLAorg/feat/optional-final-run-quality-matchers feat: optional last run of matchers with final ICP_ITERATION for adaptive thresholds to see final thresholds
feat: optional last run of matchers with final ICP_ITERATION for adaptive thresholds to see final thresholds
fix: wrong decimation applied in sm2mm filters
Merge pull request #62 from MOLAorg/feat/prior-weight-mitigations feat: Implement Birge-ratio auto-balance for cov2cov and prior weighting
feat: Implement Birge-ratio auto-balance for cov2cov and prior weighting
fix: icp-log-viewer didn’t show the prior covariance at its correct location
Contributors: Jose Luis Blanco-Claraco
2.10.0 (2026-05-02)
CI: Update actions for new ROS rolling
icp-log-viewer: better formatting of uncertainties
demo sm2mm file: store as independent keyframes
Merge pull request #60 from MOLAorg/feat/censi3d-covariance Feat: Censi3D covariance method
feat: Add new covariance method (Censi, 3D version)
demo sm2mm files: add Keyframe map variant
Contributors: Jose Luis Blanco-Claraco
2.9.1 (2026-04-29)
Merge pull request #59 from MOLAorg/fix/cov2cov-covariance-whitening Fix cov2cov whitening and add residual-variance scaling in covariance()
Fix cov2cov whitening and add residual-variance scaling in covariance() The cov2cov branch in covariance.cpp whitened residuals with the full information matrix (cov_inv * e), so the assembled Hessian became J^T * cov_inv^2 * J instead of J^T * cov_inv * J. Combined with hundreds of pairings this drove det(cov) down to ~1e-20 and made the estimate unusable. - Use the Cholesky factor L^T (with L L^T = cov_inv) to whiten the cov2cov residual, matching what optimal_tf_gauss_newton accumulates. - Multiply the inverse-Hessian by chi^2 / (m - 6), the standard a-posteriori unit-weight variance, to rescale the (otherwise optimistic) result by the empirical residual level.
Merge pull request #58 from MOLAorg/feat/icp-viewer-show-prior feat: icp-logs now store the prior SE(3) PDF
feat: icp-logs now store the prior SE(3) PDF
icp-log-viewer: safer against exceptions in gui thread
demo sm2mm files: ignore_accelerometer=true in all deskew stages by default (prevent noisy maps from low-quality IMUs)
Contributors: Jose Luis Blanco-Claraco
2.9.0 (2026-04-22)
Merge pull request #57 from MOLAorg/feat/deskew-filter-ignore-acc FilterDeskew: add new option “ignore_accelerometer”
Merge pull request #55 from MOLAorg/feat/mm-viewer-read-bin-files mm-viewer: can be also open .bin files with serialized CGenericPointsMap
Merge pull request #54 from MOLAorg/feat/mm-apps-plugins mm-info, mm2grid, mm2las, mm2ply, mm2txt now have a –load-plugins flag
Optimization in PointCloudToVoxelGridSingle
Add <stdexcept> to all required files (don’t depend on transitive includes)
mm-info, mm2grid, mm2las, mm2ply, mm2txt now have a –load-plugins flag
Merge branch ‘generator-generic-cloud’ into develop
cloud rendering: Implement observing the autoBoundingBoxOutliersPercentile
Merge pull request #53 from MOLAorg/generator-generic-cloud Generator: creates CGenericPointsMap by default; add sanity checks in most filters
remove more old mrpt version guards
New sanity check function: warn_on_field_padding_mismatch()
FilterDeskew: guard against new MRPT behavior to keep all field lengths in sync
Add sanity checks in filters
Bump minimum MRPT version to 2.15.4 (and remove now old dead code)
Generator new param ‘filterOutPointsAtZero’, set to true in demo pipelines
Generator: now has a param ‘default_pointcloud_class’ which defaults to ‘CGenericPointsMap’
Code clean up (remove now dead code; mrpt backwards compatibility)
Merge pull request #52 from MOLAorg/icp-log-viewer-quality-filter icp-log-viewer: add –min-quality filter CLI flag
Contributors: Jose Luis Blanco-Claraco
2.8.1 (2026-04-06)
Merge pull request #51 from MOLAorg/fix/new-mrpt-api Update to build against mrpt >=2.15.13
Update to build against mrpt >=2.15.13 (pointcloud field names as std::string instead of string_view)
Contributors: Jose Luis Blanco-Claraco
2.8.0 (2026-04-01)
BUGFIX: Fix potential crash (regression in former voxel parallelization)
Merge pull request #50 from MOLAorg/fix/ram-usage Fix/ram usage
Process points by chunks to limit RAM usage
reduce memory allocations in voxel views
Smarter usage of reserve() in tsl maps
Add agents.md
Merge pull request #49 from MOLAorg/mm2las/georef mm2las: export flag ‘–frame geodetic’ for georeferenced clouds
Use geodetic coords cache
Correctly honor exportGeodetic
mm2las: Add sanity checks
Fix: X=lon, Y=lat coord order for WKT2
mm2las: export flag ‘–frame geodetic’ for georeferenced clouds
Contributors: Jose Luis Blanco-Claraco
2.7.1 (2026-03-08)
Merge pull request #48 from MOLAorg/fix/filter-adaptive-avoid-fpe FIX: Avoid potential division by zero in FilterDecimateAdaptive
Update minimum required MRPT version 2.15.0
FIX: Avoid potential division by zero in FilterDecimateAdaptive
Merge pull request #47 from MOLAorg/fix/georef-yaml-missing-fields FIX: georeferencing yaml serialization missed orientation fields
FIX: georeferencing yaml serialization missed orientation fields
Merge pull request #46 from MOLAorg/feat/sm2mm-in-enu-frame sm2mm: new feature to directly use georef as input and produce maps (and apply filters!) in ENU frame
sm2mm: new feature to directly use georef as input and produce maps in ENU frame
Contributors: Jose Luis Blanco-Claraco
2.7.0 (2026-03-03)
Merge pull request #45 from MOLAorg/feat/mm2grid Add new cli app: mm2grid
Add new cli app: mm2grid
Merge pull request #44 from MOLAorg/feat/view-vector Generators now optionally generate a ‘view’ direction vector per point
FilterMLS: now uses the view-direction vectors to ensure normals point outwards
Generator: add safety consistency check
Generators are now also included into sm2mm profiler
Generators now optionally generate a ‘view’ direction vector per point
mm-viewer: FIX: show/hide all buttons did not apply to extra viz layers
mm-georef: Fix creation of empty output map if input didn’t exist
mm2txt: Fix wrong console message saying mm-info instead of mm2txt
mm-viewer UI: show XY grid plane at the root frame of reference (ENU or map)
Merge pull request #43 from MOLAorg/feat/export-enu-frame Export tools now accept “–frame enu” to generate XYZ data in ENU frame
Export tools now accept “–frame enu” to generate XYZ data in ENU frame
Contributors: Jose Luis Blanco-Claraco
2.6.0 (2026-02-16)
Merge pull request #42 from MOLAorg/feat/filter-remove-several-point-fields FilterRemovePointCloudField now accepts multiple fields
FilterRemovePointCloudField now accepts multiple fields
Merge pull request #41 from MOLAorg/feat/functor-save-log-file Add functor_should_generate_debug_file to override the decision of whether to write .icplog files
Add functor_should_generate_debug_file to override the decision of whether to write .icplog files
Merge pull request #40 from MOLAorg/feat/new-clear-remove-field-filters Add new filters: FilterClear and FilterRemovePointCloudField
Add new filters: FilterClear and FilterRemovePointCloudField
docs: refer to the online pipeline editor
docs: add missing docs for FilterRenameLayer
Contributors: Jose Luis Blanco-Claraco
2.5.0 (2026-02-04)
Merge pull request #39 from MOLAorg/feat/permit-missing-externals sm2mm: Add optional flag –permit-missing-externals
sm2mm: Add optional flag –permit-missing-externals
Merge pull request #38 from MOLAorg/feat/mls-optimizations FilterMLS performance optimizations
Merge pull request #37 from MOLAorg/feat/use-zstd Use ZStd compression by default (for MRPT>=2.15.7)
docs: sm2mm add “–compression-method”
Explicitly include mrpt/core/Clock.h where used
Use ZStd compression by default (for MRPT>=2.15.7)
Merge pull request #36 from MOLAorg/feat/mm2txt-missing-fields-dont-emit mm2txt, mm2ply: don’t emit columns of zeros for missing user-given fields
Merge pull request #35 from MOLAorg/feat/mm2txt-ignore-fields mm2txt and mm2ply: Add new option –ignore-missing-fields
Merge pull request #34 from MOLAorg/feat/mm2txt-mm2ply-more-precision Exploit the maximum float32/float64 precision in exported txt formats
Merge pull request #33 from MOLAorg/feat/sm2mm-new-options Feat/sm2mm-new-options
sm2mm cli app: add new flags for downsampling options
sm2mm: add new downsample options and refactor into update_velocity_buffer_from_obs()
Contributors: Jose Luis Blanco-Claraco
2.4.1 (2026-01-27)
Add more unit tests
Update README to keep it in sync with the provided apps and libraries
Merge pull request #29 from MOLAorg/fix/cov Fix bugs in covariance estimation for some cases
fix covariance estimation bugs; add unit tests for cov2cov
Update commit for mola_common
Merge pull request #28 from MOLAorg/feat/mm-viewer-3d-layers mm-viewer: add CLI flags to load overlaid 3D scenes for visualization
mm-viewer: add CLI flags to load overlaid 3D scenes for visualization
Contributors: Jose Luis Blanco-Claraco
2.4.0 (2026-01-21)
Merge pull request #27 from MOLAorg/feat/new-filter-voxel-sor
Add new unit test for class factory
Add new FilterVoxelSOR filter
Merge pull request #26 from MOLAorg/feat/mm2las
Add mm2las CLI tool
Contributors: Jose Luis Blanco-Claraco
2.3.1 (2026-01-14)
Merge pull request #25 from MOLAorg/feat/naive-decimate Add trivial FilterDecimate for fast downsampling without spatial awareness
lint fixes
Add trivial FilterDecimate for fast downsampling without spatial awareness
Parameterizable: add virtual base dtor
Remove the NormalizeIntensity stage in the demo pipelines; visualization does that already
docs: fill missing manpages
docs: add sm2mm pipelines page
Clarify map layers and simple maps descriptions Updated references to CMetricMap and CGenericPointsMap in the documentation for clarity and accuracy.
Contributors: Jose Luis Blanco-Claraco
2.3.0 (2026-01-08)
Merge pull request #24 from MOLAorg/feat/mm2txt-select-fields mm2txt and mm2ply now have a –export-fields flag
mm2txt and mm2ply now have a –export-fields flag
Merge pull request #23 from MOLAorg/fix/some-deprecations Fix usage of deprecated cloud types
Provide shortcut names for common cloud field names
More deprecated cloud usage
FIX bug: FilterDecimateVoxel, if using flatten, did not propagate all input cloud fields
Fix usage of some deprecated cloud types
FilterSOR: create output layers even if input is empty
FilterDeskew: propagate input fields even if the cloud is empty
FilterByExpression: show debug-level stats
FilterNormalizeIntensity: do not throw on empty clouds
Merge pull request #22 from MOLAorg/feat/new-filters Add new filter FilterRenameLayer
Added filter FilterRenameLayer
mm2txt: prepare for deprecated classes in 3.0.0
FilterAdjustTimestamps: new method ‘None’ to bypass filter
Fix: sm2mm did not attach to ParameterSource the final_filter elements
sm2mm: did not observe the optional profiler parameter for the final_filter stage
Fix: FilterMLS did not properly copy all point fields when using upsampling
Fix: FilterAbsoluteTimestamp now also works for accumulated points in one layer
Contributors: Jose Luis Blanco-Claraco
2.2.1 (2026-01-06)
Merge pull request #21 from MOLAorg/feat/abs-stamp-filter Added new filter: FilterAbsoluteTimestamp
Fix the logic of the FilterEdgePlane filter parameters
Added new filter: FilterAbsoluteTimestamp
mm2txt: also export uint8 fields (missing in last release)
Merge pull request #20 from MOLAorg/feat/more-unit-tests More unit tests
Add generators unit tests
More unit tests
Contributors: Jose Luis Blanco-Claraco
2.2.0 (2025-12-28)
docs: explain FilterSOR
Merge pull request #19 from MOLAorg/feat/mm2ply Add mm2ply CLI tool
Merge pull request #18 from MOLAorg/feat/new-sor-filter Add FilterSOR: Statistical Outlier Rejection
More unit tests: cover MLS
Merge pull request #17 from MOLAorg/feat/filter-by-expr Add new filter: FilterByExpression
More code coverage; fix protected-level initialize methods
Add new filter: FilterByExpression
FIX: missing uint8 fields in Deskew
sanityCheck: also check all double/uint8 fields
ui: fix case without geo-ref
mm-viewer: show lat/lon coordinates for mouse selected points
mm-viewer: GUI now shows the ENU & map miniviews for orientation hints
mm-viewer: handle special coloring channel groups ‘rgb’ and ‘rgbf’
clean non used code in deskew test
map viz: use new mrpt 2.15.3 coloring modes
sm2mm: auto-guess lazy-load externals directory for .simplemap files
mm-viewer: implement colorize clouds by any field
MLS filter: add ‘output_layer_class’ parameter
mm2txt: export all fields of CGenericPointsMap layers
Merge pull request #16 from MOLAorg/fix/generic-cloud-deskew-fields Add test for missing fields in generic cloud deskew
FIX: correctly register arbitrary pointcloud fields into output clouds
Add test for missing fields in generic cloud deskew
sm2mm: FIx console message on “FinalFilters” did not obey verbosity level
Fix clang-tidy warnings
Contributors: Jose Luis Blanco-Claraco
2.1.2 (2025-11-28)
mm-viewer: Automatic retry to load maps if missing plugins, trying to reload with libmola_metric_maps.so
Enable coverage run for noble docker image
Add new unit tests
docs: fix broken formatting of filters page
Merge pull request #14 deskew filter: refactoring to handle arbitrary point fields
get code ready for API update in MRPT 2.15.3
Fix build w/o imu library
mm-viewer: add keystrokes shift+cursor arrows to move up/down
Add Codecov badge to README
MLS filter: add progress report logging
Contributors: Jose Luis Blanco-Claraco
2.1.1 (2025-11-08)
FIX: SanityCheck was triggering as errors optional pointcloud fields in XYZIRT clouds
FIX: Throw exception instead of crashing if FilterDeskew is invoked with an empty local velocity buffer
Fix yaml file for not using mola_yaml extensions
Add more sm2mm demo pipelines
Contributors: Jose Luis Blanco-Claraco
2.1.0 (2025-10-28)
Merge pull request #13 from MOLAorg/fix/filterdecimate-bug
Add unit test for FilterDecimateVoxel
mm-viewer: ensure proper order of opengl object destruction
Add more debug traces for Filters
Generator: Add more info on layer contents in debug traces
FIX: Avoid crash in FilterVoxelSlice if there are no points in the ROI
Merge pull request #12 from MOLAorg/feat/mls Implemented MLS filter
Merge pull request #11 from MOLAorg/feat/use-faster-insertion Use the faster insertion-with-context in MRPT 2.15.0
Update to build using CGenericPointsMap in upcoming MRPT >=2.15
Merge pull request #10 from MOLAorg/fix/ci
Upgrade CircleCI images to u24.04
Fix build out of ROS and w/o IMU preintegration
Update mola_common version
Fix: Warning message missing new line
FilterDecimateVoxels: add new parameter ‘minimum_points_per_voxel’
Fix docs typo
Update docs for Deskew filter
Update README.md badges
Contributors: Jose Luis Blanco-Claraco
2.0.0 (2025-10-13)
Merge pull request #9 from MOLAorg/feature/better-lio Better LIO
sm2mm cli app: add –profiler flag
demo sm2mm pipelines: add deskew method entry
CI: Add another pipeline without TBB
FIX: bug in non-TBB serial implementation of GN optimizer
CI: add running unit tests
FIX: potential crash in FilterDeskew
Add deskew unit tests
Add unit test for cov2cov optimizer
Add ‘name’ property to all generators and filters for disaggregated stats
Allow building without the IMU library
Update mola_common to 0.5.1
clang-tidy fixes
Remove dead code
Refactor errorTerm for pt2pt for better reusability
mm-viewer: add combo box to select intensity colormap
Add docs for filters
BUGFIX: FilterMerge would lost all point fields except XYZ
Remove external libpointmatcher
Update formatter script
icp log viewer: more options for cov2cov visualization
Define virtual API MetricMapMergeCapable
fix bug in FilterByIntensity params parser
Render cov2cov pairings
FilterByRange new parameter: metric_l_infinity
FilterDecimateAdaptive now exploits parallelization
Progress visualizing cov2cov pairings
New cov2cov ICP optimizer
Implement a new Cov2Cov matcher
Make Matcher_Points_Base::transform_local_to_global() to use TBB, and remove unused parameters
Add [[nodiscard]] to estimate_points_eigen()
BBox filter: fix target layer must be same type than input
Use fractional integers for faster sampling
New FPS filter
Remove FilterDecimateVoxelsQuadratic
Style: public ‘params_’ rename as ‘params’
New interface ‘IcpPrepareCapable’
Add new virtual interface NearestPointWithCovCapable
Refactor mp2p_icp_map into mp2p_icp_common for IMU-related parts
Finished integration of new IMU API package
Depend on imu external library
Move code out to the imu preintegration package
Move LocalVelocityBuffer to the IMU repository
Add [[nodiscard]] to icp_pipeline_from_yaml()
deskew filter: new option ‘in_place’ to avoid allocating a new cloud whenever possible
Finish implementation of higher-order IMU interpolator
Implement trajectory reconstruction for deskew
cmake files: prefer spaces indentation
Add imu preintegration package as dependency
FilterNormalizeIntensity can now use a fixed min/max range given by hand
Docs: update mp2p_icp_basics for better searchability of simplemaps
New option to set pointcloud alpha channel
Contributors: Jose Luis Blanco-Claraco
1.8.0 (2025-08-26)
Modernize and unify license notes in all files
Merge pull request #8 from MOLAorg/feat/precise-deskew Precise scan deskew: - Implement LocalVelocityBuffer inside ParameterSource’s - Update LocalVelocityBuffer from IMU data from Generators. - Export / Import LocalVelocityBuffer to/from YAML - Implement precise cloud undistortion in FilterDeskew - Use precise cloud undistortion in the context of sm2mm.
sm2mm: Use local velocity buffer if available
add serialization to velocity buffer
Generators now handle IMU readings and forward them to the velocity buffer
Update to latest mola_common for embedded builds
linter: clang-tidy fixes
fix param name for better consistency
feature: Option to use std::map instead of tsl robin_map in voxelization filters
docs: fill txt2mm man page
Feature: txt2mm new import format ‘xyzrgb_normalized’
remove code to support older MRPT versions; code style clean ups
Fix: FilterAdjustTimestamps may trigger exception if input cloud is empty
Contributors: Jose Luis Blanco-Claraco
1.7.1 (2025-06-20)
docs: Populate sm2mm app page
New feature: all pipeline modules now has an optional “plugin” YAML field to load them from user-provided plugins.
Update REAME ROS badges
Contributors: Jose Luis Blanco-Claraco
1.7.0 (2025-06-02)
metric map data type: add new metadata YAML field
Update broken link to ROS Index
docs: change references to default branch master->develop
Default generator: more details in debug traces when ignoring an observation
Update package license tag to “BSD-3-Clause”
Integrate vscode with colcon custom settings and clang-tidy
Fix build unit tests with older gcc versions
Drop apparently useless build dep
Contributors: Jose Luis Blanco-Claraco
1.6.7 (2025-04-03)
mm-georef cli app: support reading/writing georef info in YAML format
georeferencing metadata now can be read/writen as YAML files
clang-format: switch to column limit=100
Update to robin-map v1.4.0
Contributors: Jose Luis Blanco-Claraco
1.6.6 (2025-02-26)
Docs: add page for mm-georef
docs: Update 2025 paper citation
print metric_map_t as string: show lat/lon coordinates in a format directly compatible with Google Map searches.
New cli tool: mm-georef, to manipulate the geo-referencing metadata of metric map files
Contributors: Jose Luis Blanco-Claraco
1.6.5 (2025-01-28)
Add GitHub actions
Add pole-detector filter
mm-filter app: add –load-plugins flag too
Add sanity check assert in FilterDeskew
Contributors: Jose Luis Blanco-Claraco
1.6.4 (2024-12-18)
merge two docs pages in one to shorten the docs TOC
Update README.md: Mark ROS2 Iron as EOL
Also use TBB for parallel solving point-to-plane pairings
Contributors: Jose Luis Blanco-Claraco
1.6.3 (2024-11-11)
icp-log-viewer: also reduce GUI refresh rate
mm-viewer: avoid useless GUI refresh (CPU usage reduction)
txt2mm: Add input filter xyzrgb
mm-viewer: add a ‘fit view to map’ button
New cli app rawlog-filter
FilterCurvature: better handling scans with <=3 points in some rings
new subcommand ‘sm-cli tf’
Contributors: Jose Luis Blanco-Claraco
1.6.2 (2024-09-14)
Expose << and >> operators for geo-reference data structures
Fix missing build_dep
Contributors: Jose Luis Blanco-Claraco
1.6.1 (2024-09-11)
Fix missing catkin buildtoo_depend for ROS1 builds
Update RTTI macros for upcoming MRPT 2.14.0
Contributors: Jose Luis Blanco-Claraco
1.6.0 (2024-09-08)
Port Point2Plane matcher to use the new NN-for-planes API
mp2p_icp_map library: add NearestPlaneCapable virtual API
cmake: move from glob expressions to explicit lists of source files
clarify eigenvalues order in headers
Contributors: Jose Luis Blanco-Claraco
1.5.6 (2024-09-07)
sm2mm cli: show map contents before writing to disk
add another demo sm2mm file for the mola tutorials
Add another sm2mm demo file w/o deskew for the mola mapping tutorial
Matcher_Point2Plane: fix build error in armhf
Fix build with embedded mola_common
README: Add ROS badges for all architectures
Contributors: Jose Luis Blanco-Claraco
1.5.5 (2024-08-27)
Explicitly add tbb as dependency in package.xml
Depend on new mrpt_lib packages (deprecate mrpt2)
FIX: build errors in armhf arch
Contributors: Jose Luis Blanco-Claraco
1.5.4 (2024-08-20)
Do not use Eigen::Vector for compatibility with Eigen3 <3.4 in ROS Noetic
Contributors: Jose Luis Blanco-Claraco
1.5.3 (2024-08-20)
Re-add ROS1 Noetic as supported distribution
Generator sanity check asserts: more informative error messages
sm-cli: new command ‘join’ to merge simplemaps
icp-log-viewer UI: new keybind ‘I’ to switch initial/final pose
icp-log-viewer UI: add option to visualize voxelmaps empty space
Contributors: Jose Luis Blanco-Claraco
1.5.2 (2024-07-24)
Add sm2mm yaml example for dynamic/static obstacles
Update sample sm2mm pipelines to use de-skew
docs: add mm-filter example
Fix pointcloud ptr typo
More safety sanity checks added in mm-viewer and sm2mm
BUGFIX: Generator should not create empty maps for GPS observations
Contributors: Jose Luis Blanco-Claraco, Raúl Aguilera López
1.5.1 (2024-07-03)
Update docs
ICP: Add optional functors for before-logging maps
icp-log-viewer UI: fix potential out-of-range exception when autoplay is on
FilterAdjustTimestamps: add new param ‘time_offset’ useful for multiple LiDARs setups
Contributors: Jose Luis Blanco-Claraco
1.5.0 (2024-06-21)
ICP: Add optional user-provided per-iteration hooks
Add new filter: FilterByRing
Add new filter: FilterAdjustTimestamps
Add sanity checks for point cloud fields.
Fix typo in default class for FilterDeskew
generators API: add bool return type to detect if observation was actually processed
generic Generator: handle velodyne observations so timestamps are generated
Contributors: Jose Luis Blanco-Claraco
1.4.3 (2024-06-11)
Add pointcloud_sanity_check() auxiliary function
Generator: more DEBUG level traces
BUGFIX: FilterDeskew generated buggy output points if the input does not contain timestamps
Add sanity checks for point cloud fields
ICP log records now also store the dynamic variables. icp-log-viewer displays them.
ICP log files: automatically create output directory if it does not exist
Update ros2 badges (added Jazzy)
Contributors: Jose Luis Blanco-Claraco
1.4.2 (2024-05-28)
mm-viewer: add check-all, check-none to layer filters
Add new filter: FilterRemoveByVoxelOccupancy
mm-viewer: camera travelling keyframes-based animations
mm-viewer: navigate the map with keyboard arrows; add a load button
mm-viewer: can now also draws a TUM trajectory overlaid with the map
UI apps: smoother rendering
icp-log-viewer and mm-viewer: the UI now has a XYZ corner overlay
sm-cli: command “export-kfs” now has an optional flag ‘–output-twist’
FilterDeskew: ignore empty input maps
More debug-level traces
deskew filter: Fix case of variable names in docs
sm-cli app: Add new command ‘trim’ to cut simplemaps by bounding box
mm-viewer: show mouse pointing coordinates
Contributors: Jose Luis Blanco-Claraco
1.4.1 (2024-05-19)
Fix build for older mrpt versions
ICP pipelines: Implement loading
quality_checkpointsparameter from YAML config fileQuality evaluators: add the option for ‘hard discard’
Update QualityEvaluator_Voxels to use prebuilt voxel layers from input maps. Add unit tests.
BUGFIX: Fix deserializing georeferenced .mm files stored in <1.4.0 format
ICP: quality evaluators can now have formulas in their parameters too
mm-viewer and icp-log-viewer: extend zoom range so maps of tens of kms can be viewed at once
Contributors: Jose Luis Blanco-Claraco
1.4.0 (2024-05-06)
Update commit for robin-map to latest version (patch contributed upstream)
icp-log-viewer: UI now has a slider for each map point size
ICP: Add a new quality_checkpoint parameter to early abort ICP attempts
georeferenced maps: T_enu_to_map now has a covariance field
mm-viewer: display ENU frame too
Contributors: Jose Luis Blanco-Claraco
1.3.3 (2024-04-30)
Add minimum_input_points_to_filter option to FilterDecimateVoxels
FIX: QualityEvaluator_PairedRatio throws when one of the reference maps is empty
FIX BUG: Won’t try to match 2D pointclouds if their height is different
Clarify comments in metricmap.h about geodetic references
Fix printing metric_map_t contents when it only has a gridmap
Fix potential dangling references (g++ 13 warning)
Fix potential use of uninitialized point index
Bump cmake_minimum_required to 3.5
Contributors: Jose Luis Blanco-Claraco
1.3.2 (2024-04-22)
tsl::robin_map library is no longer exposed neither in the public API nor as public headers (PIMPL pattern) This is to prevent Debian-level collisions with other packages also exposing it.
add first icp-log-viewer docs
Contributors: Jose Luis Blanco-Claraco
1.3.1 (2024-04-16)
mm-viewer and icp-log-viewer: saves UI state in persistent user config file
FIX: missing UI refresh when clicking showPairings checkbox
renamed apps for less verbose names: icp-run, icp-log-viewer
ICP core now defines a variable ICP_ITERATION for use in programmable formulas in pipelines
icp-log-viewer: much faster rendering of ICP iteration details
mm-viewer: fix bug in calculation of bounding box
Merge docs with main MOLA repo
Contributors: Jose Luis Blanco-Claraco
1.3.0 (2024-03-10)
mm-viewer: new options to visualize georeferenced maps
New sm-cli commands: –cut, –export-keyframes, –export-rawlog
propagate cmake deps downstream
metric_map_t: add georeferencing optional field
mm-filter: add –rename operation
GetOrCreatePointLayer() moved to its own header and uses shared ptrs
FilterMerge: add param input_layer_in_local_coordinates
Contributors: Jose Luis Blanco-Claraco
1.2.0 (2024-02-16)
Add new apps: sm-cli, mm-info, txt2mm, mm2txt, mm-filter
Improved documentation.
new filter FilterByIntensity
FilterNormalizeIntensity: add option for intensity range memory
FilterByRange: renamed params to simplify them (removed param ‘keep_between’)
FIX: missing intensity channel in decimate voxel when using some decimation methods
sm-cli: new subcommand ‘level’ to maximize the ‘horizontality’ of built maps
add optional profiler to filter pipelines
Contributors: Jose Luis Blanco-Claraco
1.1.1 (2024-02-07)
MergeFilter: now also handles CVoxelMap as inputs
more memory efficient defaults
FilterCurvature: now based on ring_id channel
Use hash map min_factor to speed up clear()s
add missing hash reserve
PointCloudToVoxelGridSingle: Fix wrong initialization of point count
Contributors: Jose Luis Blanco-Claraco
1.1.0 (2024-01-25)
FilterDecimateVoxels: Replace 3 bool parameters with an enum
Fix clang warnings
Save and visualize ICP step partial solutions
QualityEvaluator_PairedRatio: now does not require parameters
Add filter: Bonxai VoxelMap -> 2D gridmap. Bayesian filtering of voxel columns
Generator: allow defining custom metric maps directly in the YAML configuration
Contributors: Jose Luis Blanco-Claraco
1.0.0 (2024-01-20)
Gauss-Newton solver: Add optional prior term
Added FilterMerge and modifications to allow sm2mm to build any type maps
sm2mm: add option for lazy-load external directory
Decimate filter: add flatten_to option to efficiently convert 3D->2D point clouds
FilterBoundingBox: parameter name changed for clearer split of inside / outside bbox
Deskew: add option to bypass de-skew operation
bump minimum required mrpt version
Better coloring; add option to export mm layers
Use new mrpt api to propagate point properties; add final_filter stage to sm2mm
sm2mm: add verbosity flag
bbox filter: allow processing variables too
Introduce robot_{x,y,z} variables
Better mm-viewer; update sm2mm demo file
Progress with RST docs
Add missing robotPose argument to generators; progress with mm-viewer
Add sm2mm app
Add FILE attribute to license tag
More dynamic parameters
fix print format
Add Deskew filter
update CI to u22.04
Introduce Parameterizable interface
New layers: create of the same input cloud type
Add FilterCurvature
filter: optional additional layer for deleted points
FIX: important error in robust gradient
expose GN params as public
new generators and filters
Filters: use tsl robin_map, faster than std::unordered_map
prefer nn_radius_search() to exploit nanoflann rknn
Minor UI updates
gui: autoplay
estimate_points_eigen.h moved to the mp2p_icp_map library
Solvers: add option to select by correction magnitude
add [[nodiscard]] to generator API
Add specialized implementation of voxelize for 1 pt/vx
add Cauchy robust kernel
Add support for TBB for parallelization
add angularThresholdFactor; add max plane-to-pt distance
viewer UI: show number of points per layer
Prefer Teschner’s spatial hash
Use nn_single_search() when possible
viewer: add follow local checkbox
Add new filter: FilterDecimateVoxelsQuadratic
FilterDecimateVoxels: new option use_closest_to_voxel_average
FilterDecimateVoxels: new param use_random_point_within_voxel
less unnecesary mem allocs
generator: create map layers first, then filter by observation name/class filter
port to NN radius search
add “enabled” property to base Matcher class
Solvers: add property ‘enabled’
Add robust kernels to GN solver
Add optional profiler to ICP
New parameter decimationDebugFiles
Add plugin option to viewer
VoxelFilter: is now ~7 times faster and does not need a bounding box parameter, thanks to using an associative container.
viewer: add new flag -f to load one single log file
viewer: increase slider range for max far plane
Options to recolorize maps in icp log viewer
Fix regression in rendering options for point clouds
Matcher: new parameter bounding_box_intersection_check_epsilon
New env var MP2P_ICP_GENERATE_DEBUG_FILES can be use to override generation of icp log files
BUGFIX: Ignored sensorPose for Generator::filterPointCloud()
Allow ICP matching against voxel metric map types
mp2p_icp_filters::Generator now can create a map from a generic INI file (e.g. voxelmaps)
fix references to old pointcloud_t -> metric_map_t
Remove support for MRPT<2.4.0
Contributors: Jose Luis Blanco-Claraco
0.2.2 (2023-09-08)
Fix missing cmake dependencies between libraries
Update mola_common
Refactor into a new small library mp2p_icp_map with just the metric_map_t class
sync mola_common submodule
Update submodule mola_common
Remove redundant section
Update ROS badges
Contributors: Jose Luis Blanco-Claraco
0.2.1 (2023-09-02)
Update copyright date
Update to new name of mola_common
update ros badges
Contributors: Jose Luis Blanco-Claraco
0.2.0 (2023-08-24)
First release as MOLA submodule.
0.1.0 (2023-06-14)
First official release of the mp2p_icp libraries
Contributors: FranciscoJManasAlvarez, Jose Luis Blanco-Claraco