Skip to content

Add degenercy-aware point-to-plane registeration method DCReg#6432

Open
JokerJohn wants to merge 1 commit intoPointCloudLibrary:masterfrom
JokerJohn:add_dcreg
Open

Add degenercy-aware point-to-plane registeration method DCReg#6432
JokerJohn wants to merge 1 commit intoPointCloudLibrary:masterfrom
JokerJohn:add_dcreg

Conversation

@JokerJohn
Copy link
Copy Markdown

This PR integrates the core DCReg degeneracy-aware point-to-plane solver idea
into PCL's registration module.

Paper:

  • DCReg: Decoupled Characterization for Efficient Degenerate LiDAR
    Registration
  • Authors: Xiangcheng Hu, Xieyuanli Chen, Mingkai Jia, Jin Wu, Ping Tan, Steven L. Waslander
  • arXiv: https://arxiv.org/abs/2509.06285

Reference implementation and project documentation:

DCReg targets degenerate LiDAR registration. Its core idea is to decouple
rotation and translation observability through Schur complements, characterize
weak physical motion axes by aligning eigenspaces to coordinate axes, and
stabilize weak directions through targeted eigenvalue clamping and
preconditioned solving.

This PR is not a direct import of the full standalone DCReg research system.
It ports the solver and diagnostic ideas into PCL's existing
pcl::registration::TransformationEstimation API:

  • PCL continues to own point cloud containers, correspondence sets, ICP loops,
    and registration pipelines.
  • The estimator uses the same point-to-plane residual model as
    TransformationEstimationPointToPlaneLLS.
  • PointTarget is expected to provide normal_x, normal_y, and normal_z,
    matching PCL's existing point-to-plane estimator convention.
  • Standalone DCReg runners, parking-lot preprocessing, YAML presets,
    visualization, benchmark scripts, and local-plane/local-frame compatibility
    helpers are intentionally out of scope for this first PCL-native PR.

PCL integration summary

This PR adds:

  • pcl::registration::TransformationEstimationPointToPlaneDCReg
  • pcl::registration::DCRegDegeneracyAnalysis
  • getter/setter style solver parameters:
    • setDegeneracyConditionThreshold() / getDegeneracyConditionThreshold()
    • setKappaTarget() / getKappaTarget()
    • setPcgTolerance() / getPcgTolerance()
    • setPcgMaxIterations() / getPcgMaxIterations()
  • getLastDegeneracyAnalysis() to inspect diagnostics from the most recent
    estimation call.

The estimator builds the same 6D point-to-plane normal equation as
TransformationEstimationPointToPlaneLLS, then replaces the solve with:

  • Schur-complement rotation and translation degeneracy analysis
  • axis-aligned weak direction characterization
  • weak eigenvalue clamping for a block preconditioner
  • preconditioned conjugate-gradient solving
  • dense / QR / minimum-norm fallback paths for non-finite or rank-deficient
    systems

Diagnostics

DCRegDegeneracyAnalysis reports:

  • whether correspondences were available
  • full Hessian condition number
  • rotation and translation Schur condition numbers
  • raw Schur eigenvalues
  • axis-aligned eigenvalues
  • weak rotation and translation axis flags in x/y/z order
  • rank-deficient state
  • whether Schur factorization was available
  • solver path
  • PCG convergence and iteration count

Rank-deficient systems still report weak axes through eigensolver/block
fallback diagnostics.

Tests

Added test_registration_dcreg.cpp covering:

  • default parameters and getter/setter behavior
  • empty correspondence behavior and diagnostics
  • agreement with TransformationEstimationPointToPlaneLLS on a
    well-conditioned synthetic cube case
  • finite/stable output and degeneracy reporting on a planar weak-geometry case
  • cylinder degeneracy reporting with axial weak translation

Validation

Local validation was run in /home/xchu/CLionProjects/pcl.

Configuration note: this machine has an old CUDA 10.1 compiler and a mismatched
system GTest installation, so local validation explicitly disabled CUDA and
nanoflann discovery and pointed PCL to a consistent /usr/src/googletest
source/include pair.

cmake -S . -B build-dcreg \
  -DCMAKE_BUILD_TYPE=Release \
  -DWITH_CUDA=OFF \
  -DCMAKE_DISABLE_FIND_PACKAGE_nanoflann=TRUE \
  -DGTEST_SRC_DIR=/usr/src/googletest/googletest \
  -DGTEST_INCLUDE_DIR=/usr/src/googletest/googletest/include \
  -DBUILD_registration=ON \
  -DBUILD_global_tests=ON \
  -DBUILD_tests_registration=ON \
  -DBUILD_apps=OFF \
  -DBUILD_examples=OFF \
  -DBUILD_tools=OFF \
  -DBUILD_simulation=OFF

cmake --build build-dcreg --target pcl_registration -j2
cmake --build build-dcreg --target test_registration_dcreg -j2
build-dcreg/test/registration/test_registration_dcreg
ctest --test-dir build-dcreg/test -R registration_dcreg --output-on-failure
clang-format --dry-run -Werror \
  registration/include/pcl/registration/transformation_estimation_point_to_plane_dcreg.h \
  registration/include/pcl/registration/impl/transformation_estimation_point_to_plane_dcreg.hpp \
  registration/src/transformation_estimation_point_to_plane_dcreg.cpp \
  test/registration/test_registration_dcreg.cpp
git diff --check

Results:

  • pcl_registration: built successfully
  • test_registration_dcreg: built successfully
  • DCReg registration tests: 5 passed
  • CTest registration_dcreg: passed
  • clang-format dry-run: passed
  • git diff --check: passed

Additional non-CI comparison

I also ran a local equivalence check against the standalone DCReg code on two
representative datasets:

  • shifted-cylinder synthetic degeneracy case
  • parking-lot real-scene case

The standalone DCReg examples use kNN local-plane correspondences and a local
SO(3) update convention, while this PCL estimator is a point-to-plane
TransformationEstimation component. To make the comparison meaningful, I
converted the standalone DCReg correspondences from each case into an equivalent
PCL point-to-plane estimation problem, so both implementations solved the same
6D normal equation.

Results:

case original mask PCL mask max transform diff PCG iterations
shifted-cylinder 000001 000001 5.30e-7 6 / 6
parking-lot 000100 000100 3.90e-8 6 / 6

PCL diagnostic descriptions from the same check:

  • shifted-cylinder: weak translation axis z,
    condition_number_translation = 26.805484784, solver pcg
  • parking-lot: weak translation axis x,
    condition_number_translation = 14.485626824, solver pcg

These checks are intentionally not added as CI tests because they depend on
external/local datasets and on the standalone DCReg runner. The CI-facing tests
use small synthetic clouds with no external data dependency.

@JokerJohn JokerJohn changed the title Add DCReg point-to-plane transformation estimation Add degenercy-aware point-to-plane registeration method DCReg Apr 27, 2026
@Neilyooo
Copy link
Copy Markdown

Great job!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants