Skip to content

Add Normal Distributions Transform (NDT) registration - #7517

Open
Xiang-Zeng wants to merge 8 commits into
isl-org:mainfrom
Xiang-Zeng:feature/ndt-registration
Open

Add Normal Distributions Transform (NDT) registration#7517
Xiang-Zeng wants to merge 8 commits into
isl-org:mainfrom
Xiang-Zeng:feature/ndt-registration

Conversation

@Xiang-Zeng

@Xiang-Zeng Xiang-Zeng commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Type

  • Bug fix (non-breaking change which fixes an issue): Fixes #
  • New feature (non-breaking change which adds functionality). Resolves #
  • Breaking change (fix or feature that would cause existing functionality to not work as expected) Resolves #

Motivation and Context

Open3D provides ICP, Colored ICP, Generalized ICP, RANSAC, and FGR registration
pipelines, but does not currently expose a 3D Normal Distributions Transform
(NDT) registration method. NDT is a common LiDAR / point-cloud registration
method that optimizes a source-to-target rigid transform against voxelized
Gaussian target distributions instead of nearest-neighbor point
correspondences.

Upstream check:

  • Local source search under cpp, python, docs, and examples found no
    existing NDT registration implementation.
  • GitHub API search for active Open3D PRs matching NDT registration returned
    total_count: 0 on 2026-07-08.

Checklist:

  • I have run python util/check_style.py --apply to apply Open3D code style
    to my code.
  • This PR changes Open3D behavior or adds new functionality.
    • Both C++ (Doxygen) and Python (Sphinx / Google style) documentation is
      updated accordingly.
    • I have added or updated C++ and / or Python unit tests OR included test
      results
      (e.g. screenshots or numbers) here.
  • I will follow up and update the code if CI fails.
  • For fork PRs, I have selected Allow edits from maintainers.

Description

This PR adds a legacy CPU registration API:

  • NormalDistributionsTransformOption
  • RegistrationNDT
  • Python binding: open3d.pipelines.registration.registration_ndt

The implementation builds a target voxel Gaussian map once per registration
call, accepts either center-voxel or six-neighbor voxel residuals, regularizes
small covariance eigenvalues, rejects residuals by squared Mahalanobis distance,
and solves the rigid update using Open3D's existing Eigen SE(3) utilities.

The returned RegistrationResult includes fitness, Euclidean RMSE over the returned representative target-point correspondences, the estimated transform, and representative target-point
indices for accepted voxel residuals.

This implementation is based on the Normal Distributions Transform (NDT) registration algorithm originally proposed by Biber and Straßer [1]. The concrete implementation follows the open‑source reference code by Gao [2], while being adapted to Open3D's registration API and coding style.

[1] P. Biber and W. Straßer, "The Normal Distributions Transform: A New Approach to Laser Scan Matching," Proceedings of the 2003 IEEE/RSJ International Conference on Intelligent Robots and Systems (IROS 2003), vol. 3, pp. 2743–2748, IEEE, 2003.
[2] Xiang Gao, SLAM Technology in Autonomous Driving and Robotics: From Theory to Practice (in Chinese), Publishing House of Electronics Industry, 2023. Code available at: https://github.com/gaoxiang12/slam_in_autonomous_driving/blob/master/src/ch7/ndt_3d.cc

Added:

  • C++ tests for invalid option validation, synthetic known-transform recovery,
    and improvement over initial alignment.
  • Python tests for option binding, synthetic known-transform recovery, and
    invalid option validation.
  • Python example: examples/python/pipelines/ndt_registration.py
  • Tutorial page: docs/tutorial/pipelines/ndt_registration.rst

Local verification:

env -u https_proxy -u http_proxy -u HTTPS_PROXY -u HTTP_PROXY \
  /home/qi/.local/bin/cmake -S . -B build \
  -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
  -DBUILD_UNIT_TESTS=ON \
  -DBUILD_PYTHON_MODULE=ON \
  -DBUILD_CUDA_MODULE=OFF \
  -DBUILD_SYCL_MODULE=OFF \
  -DBUILD_ISPC_MODULE=OFF \
  -DBUILD_GUI=OFF \
  -DBUILD_WEBRTC=OFF \
  -DBUILD_EXAMPLES=OFF \
  -DBUILD_BENCHMARKS=OFF \
  -DBUILD_SHARED_LIBS=OFF

env -u https_proxy -u http_proxy -u HTTPS_PROXY -u HTTP_PROXY \
  /home/qi/.local/bin/cmake --build build --target tests pybind --parallel 2

./build/bin/tests --gtest_filter='*NormalDistributionsTransform*'
# [  PASSED  ] 3 tests.

PYTHONPATH=/home/qi/ws/ndt/Open3D/build/lib/python_package \
  python3 -m pytest python/test/pipelines/test_normal_distributions_transform.py -q
# 3 passed

python3 -m py_compile \
  python/test/pipelines/test_normal_distributions_transform.py \
  examples/python/pipelines/ndt_registration.py

Real-data validation: examples/python/pipelines/ndt_registration.py

The Python test was run against the local build package via PYTHONPATH; no
global Open3D package was installed or overwritten.

@Xiang-Zeng Xiang-Zeng changed the title Add Normal Distributions Transform registration Add Normal Distributions Transform (NDT) registration Jul 10, 2026
@Xiang-Zeng
Xiang-Zeng force-pushed the feature/ndt-registration branch 2 times, most recently from 5e0123d to cc0e43c Compare July 10, 2026 15:36
@ssheorey

Copy link
Copy Markdown
Member

Hi @Xiang-Zeng thanks for submitting this great new feature!
Please take a look at the failing CI checks (please ignore if they are due to unrelated changes - the main branch is in some major refactoring at the moment).
Also please provide the citation of the paper that is implemented here (add to docs and also here in the PR).

@ssheorey ssheorey added this to the v0.21 milestone Jul 11, 2026
@ssheorey ssheorey added the status / needs info Waiting for information from reporter / author label Jul 11, 2026
@Xiang-Zeng

Xiang-Zeng commented Jul 12, 2026

Copy link
Copy Markdown
Contributor Author

Hi @ssheorey ,
Thank you for the positive feedback! I will go through the failing CI checks and separate issues caused by this PR.
The Codacy/Cppcheck warnings about missing #include files (e.g., <Eigen/Core>, , ) seems to be false positives.
Some remaining failures appear to be related to GitHub attestation/OIDC permissions in forked PR workflows (Run actions/attest@v4 Error: missing "id-token" permission. Please add "permissions: id-token: write" to your workflow.).
I added the paper citation to the documentation and updated the PR description.

@Xiang-Zeng
Xiang-Zeng force-pushed the feature/ndt-registration branch from b9f2576 to 131ebe5 Compare July 13, 2026 04:49
@ssheorey
ssheorey requested a lite review from Copilot August 15, 2026 06:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds a legacy CPU 3D Normal Distributions Transform (NDT) registration pipeline to Open3D, including C++/Python APIs, docs, examples, and tests.

Changes:

  • Implement NormalDistributionsTransformOption and RegistrationNDT in C++, and expose them via Python bindings (registration_ndt).
  • Add C++ and Python unit tests validating option constraints and known-transform recovery.
  • Add documentation/tutorial page and a runnable Python example for NDT registration.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
python/test/pipelines/test_normal_distributions_transform.py Adds Python tests for the new NDT option + binding + correctness/validation.
examples/python/pipelines/ndt_registration.py Adds a runnable Python example demonstrating NDT registration usage.
docs/tutorial/reference.rst Adds NDT-related references used by the new tutorial page.
docs/tutorial/pipelines/ndt_registration.rst Adds a new tutorial page describing NDT and linking to the example.
docs/tutorial/pipelines/index.rst Adds the NDT tutorial page to the pipelines toctree.
cpp/tests/pipelines/registration/NormalDistributionsTransform.cpp Adds C++ unit tests for option validation and registration behavior.
cpp/tests/pipelines/CMakeLists.txt Registers the new NDT C++ test file in the test target.
cpp/pybind/pipelines/registration/registration.cpp Adds Python bindings for NDT option and registration_ndt.
cpp/open3d/pipelines/registration/NormalDistributionsTransform.h Declares the new NDT option type and registration entrypoint.
cpp/open3d/pipelines/registration/NormalDistributionsTransform.cpp Implements voxel-Gaussian construction, Gauss-Newton solve, and result evaluation for NDT.
cpp/open3d/pipelines/CMakeLists.txt Adds the NDT implementation source to the pipelines library build.
cpp/open3d/Open3D.h.in Exposes the new NDT header in the umbrella Open3D header.
CHANGELOG.md Documents the addition of NDT registration in the changelog.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread cpp/open3d/pipelines/registration/NormalDistributionsTransform.cpp
Comment thread cpp/pybind/pipelines/registration/registration.cpp
Comment thread cpp/pybind/pipelines/registration/registration.cpp Outdated
Comment thread cpp/pybind/pipelines/registration/registration.cpp Outdated
Comment thread cpp/open3d/pipelines/registration/NormalDistributionsTransform.cpp Outdated
Comment thread CHANGELOG.md Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status / needs info Waiting for information from reporter / author

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants