Optimize Lidar Odometry: Unified Hessian Loop and Bucket Linking #327
+386
−249
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description
This PR improves performance of
optimize_lidar_odometry()by reducing redundantcomputation and hash-map lookups in the Hessian computation step.
Key changes
in a single
parallel_for, reducing overhead and improving cache locality.cov_inverseis computed once per bucketupdate and reused during optimization (no per-point matrix inversion).
integer ratio, indoor buckets store a direct pointer to the corresponding
outdoor bucket, avoiding hash lookups.
Performance
Local measurements show a ~10–12% speedup compared to the previous version
for the optimized code path.
Note
The intent was to preserve the same underlying math while changing how work is
organized (single hessian computation loop, bucket linking). I tested on a reference dataset
and the trajectory looked consistent, but I would appreciate a careful review of all changes.
Detailed Changes
1. NDT::Bucket struct (
core/include/ndt.h)Eigen::Matrix3d cov_inverse- precomputed inverse of covariance matrixconst Bucket* coarser_bucket- pointer to corresponding outdoor bucket (for integer bucket ratios)2. New helper functions (
lidar_odometry_utils.cpp)is_integer_bucket_ratio()- checks if outdoor/indoor bucket ratio is integer (enables pointer linking)link_buckets_to_coarser()- links indoor buckets to outdoor buckets viacoarser_bucketpointerupdate_rgd_hierarchy()- parallel update of indoor and outdoor buckets usingtbb::parallel_invoke, followed by linking3. Precomputed cov_inverse (
lidar_odometry_utils.cpp)update_rgd()now computescov_inverseafter eachcovupdateupdate_rgd_spherical_coordinates()also updated for consistency4. Unified hessian computation (
lidar_odometry_utils_optimizers.cpp)compute_hessian()function replaces separate indoor/outdoor loopsadd_indoor_hessian_contribution(),add_outdoor_hessian_contribution()cov_inverseinstead of computingcov.inverse()per pointsquaredNorm()instead ofnorm()for range checks (avoids sqrt)5. Lookup statistics (
lidar_odometry_utils.h)LookupStatsstruct:indoor_lookups,outdoor_lookups,outdoor_pointer_hits,link_time_seconds6. NDTBucketMapType (
lidar_odometry_utils.h)ankerl::unordered_dense::segmented_mapfor pointer stability (required forcoarser_bucketoptimization)Additional details
Integer bucket ratios explanation:
When outdoor bucket size is an integer multiple of indoor bucket size (e.g., 0.6m / 0.3m = 2), indoor buckets align perfectly within outdoor buckets. This enables:
Behavior Notes
Important: Pointer Stability Requirement
The
coarser_bucketpointer optimization requires that pointers to bucket values remain valid after map insertions. This is whyNDTBucketMapTypeusesankerl::unordered_dense::segmented_mapwhich guarantees pointer stability.