PCL 点云配准 3D-NDT算法(精配准)

ops/2024/10/24 17:11:55/

目录

一、概述

1.1原理

1.2实现步骤

1.3应用场景

二、代码实现

2.1关键函数

2.1.1 加载点云数据函数

2.1.2 执行 NDT 算法配准函数

2.1.3 可视化配准结果函数

2.2完整代码

三、实现效果


PCL点云算法汇总及实战案例汇总的目录地址链接:

PCL点云算法与项目实战案例汇总(长期更新)


一、概述

        3D-NDT (3D Normal Distributions Transform) 是一种基于概率分布的点云配准算法在 3D-NDT 中,点云被划分为多个体素单元,每个体素内的点云分布被建模为一个高斯分布,配准的目标是通过最大化源点云在目标点云的高斯模型下的对数似然来优化位姿变换矩阵。

        与经典的 ICP (Iterative Closest Point) 算法相比,3D-NDT 对噪声点云的鲁棒性较强,且可以更高效地进行大规模点云数据的配准。

1.1原理

NDT 算法的主要步骤包括:

  1. 网格划分:将空间划分为固定大小的网格。
  2. 高斯分布拟合:对目标点云的每个网格单元拟合一个高斯分布。
  3. 源点云匹配:通过源点云的点与目标点云对应网格的高斯分布进行匹配,评估配准质量。
  4. 优化刚体变换:通过优化使得源点云与目标点云的高斯分布尽量匹配。

        NDT 配准的核心是找到能够最小化源点云在目标点云高斯分布中的分布差异的刚体变换矩阵。

1.2实现步骤

  1. 加载点云数据:加载源点云和目标点云。
  2. 初始化 NDT 对象:设置 NDT 配准对象,并指定所需的参数,如分辨率、迭代次数、最小转换差异等。
  3. 执行配准:通过 3D-NDT 进行点云配准,输出最终的变换矩阵。
  4. 可视化:通过 PCL 的 PCLVisualizer 对源点云、目标点云和配准后的点云进行可视化。

1.3应用场景

  1. 大规模环境建模:在 3D 地图重建中,使用 3D-NDT 可以对多帧点云数据进行有效配准。
  2. 自动驾驶环境感知:在自驾驶系统中,3D-NDT 可用于精确对齐激光雷达的点云数据。
  3. 机器人导航:在室内或室外的导航过程中,通过 3D-NDT 对当前点云与已知地图进行配准,以确定机器人的姿态和位置。

二、代码实现

2.1关键函数

2.1.1 加载点云数据函数

pcl::PointCloud<pcl::PointXYZ>::Ptr load_point_cloud(const std::string& file_path)
{pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);if (pcl::io::loadPCDFile<pcl::PointXYZ>(file_path, *cloud) == -1){PCL_ERROR("无法读取文件 %s\n", file_path.c_str());return nullptr;}std::cout << "从 " << file_path << " 读取了 " << cloud->size() << " 个点." << std::endl;return cloud;
}

2.1.2 执行 NDT 算法配准函数

void run_ndt(pcl::PointCloud<pcl::PointXYZ>::Ptr& source, pcl::PointCloud<pcl::PointXYZ>::Ptr& target, Eigen::Matrix4f& final_transform, pcl::PointCloud<pcl::PointXYZ>::Ptr& aligned_cloud)
{// 初始化 NDT 对象pcl::NormalDistributionsTransform<pcl::PointXYZ, pcl::PointXYZ> ndt;ndt.setResolution(1.0);  // 设置 NDT 的分辨率ndt.setStepSize(0.1);  // 设置优化步长ndt.setTransformationEpsilon(1e-6);  // 设置终止条件ndt.setMaximumIterations(35);  // 设置最大迭代次数// 设置输入点云ndt.setInputSource(source);ndt.setInputTarget(target);// 执行 NDT 配准ndt.align(*aligned_cloud);final_transform = ndt.getFinalTransformation();  // 获取最终变换矩阵std::cout << "NDT 配准完成,最终得分: " << ndt.getFitnessScore() << std::endl;
}

2.1.3 可视化配准结果函数

void visualize_registration(pcl::PointCloud<pcl::PointXYZ>::Ptr& source, pcl::PointCloud<pcl::PointXYZ>::Ptr& target, pcl::PointCloud<pcl::PointXYZ>::Ptr& aligned)
{boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("NDT 配准结果"));viewer->setBackgroundColor(0, 0, 0);  // 设置背景颜色为黑色// 目标点云为红色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> target_color(target, 255, 0, 0);viewer->addPointCloud(target, target_color, "target cloud");// 源点云为绿色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> source_color(source, 0, 255, 0);viewer->addPointCloud(source, source_color, "source cloud");// 配准后的点云为蓝色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> aligned_color(aligned, 0, 0, 255);viewer->addPointCloud(aligned, aligned_color, "aligned cloud");viewer->spin();
}

2.2完整代码

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/registration/ndt.h>    // NDT 头文件
#include <pcl/visualization/pcl_visualizer.h> // 可视化
#include <boost/thread/thread.hpp>using namespace std;// NDT配准函数
void ndt_registration(pcl::PointCloud<pcl::PointXYZ>::Ptr& source_cloud,pcl::PointCloud<pcl::PointXYZ>::Ptr& target_cloud,Eigen::Matrix4f& final_transform, pcl::PointCloud<pcl::PointXYZ>::Ptr& aligned_cloud)
{// 初始化NDT对象pcl::NormalDistributionsTransform<pcl::PointXYZ, pcl::PointXYZ> ndt;// 设置NDT参数ndt.setResolution(1.0);                  // 设置分辨率ndt.setMaximumIterations(35);            // 设置最大迭代次数ndt.setTransformationEpsilon(1e-8);      // 为终止条件设置最小转换差异ndt.setStepSize(0.1);                    // 设置步长ndt.setInputSource(source_cloud);        // 设置输入源点云ndt.setInputTarget(target_cloud);        // 设置输入目标点云// 设置初始变换矩阵Eigen::Matrix4f initial_guess = Eigen::Matrix4f::Identity();// 执行NDT配准ndt.align(*aligned_cloud, initial_guess);// 检查配准是否成功if (ndt.hasConverged()) {final_transform = ndt.getFinalTransformation();std::cout << "NDT配准成功,配准得分: " << ndt.getFitnessScore() << std::endl;}else {std::cerr << "NDT配准失败!" << std::endl;}
}// 可视化函数
void visualize_registration(pcl::PointCloud<pcl::PointXYZ>::Ptr& source,pcl::PointCloud<pcl::PointXYZ>::Ptr& target,pcl::PointCloud<pcl::PointXYZ>::Ptr& aligned)
{boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("配准结果"));viewer->setBackgroundColor(0, 0, 0);  // 设置背景颜色为黑色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> target_color(target, 255, 0, 0);viewer->addPointCloud(target, target_color, "target cloud");/*pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> source_color(source, 0, 255, 0);viewer->addPointCloud(source, source_color, "source cloud");*/pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> aligned_color(aligned, 0, 0, 255);viewer->addPointCloud(aligned, aligned_color, "aligned cloud");viewer->spin();
}int main()
{// --------------------加载点云数据-----------------------pcl::PointCloud<pcl::PointXYZ>::Ptr source(new pcl::PointCloud<pcl::PointXYZ>);pcl::io::loadPCDFile<pcl::PointXYZ>("1.pcd", *source);pcl::PointCloud<pcl::PointXYZ>::Ptr target(new pcl::PointCloud<pcl::PointXYZ>);pcl::io::loadPCDFile<pcl::PointXYZ>("2.pcd", *target);// 输出点云数据个数std::cout << "源点云点数: " << source->points.size() << std::endl;std::cout << "目标点云点数: " << target->points.size() << std::endl;// NDT配准pcl::PointCloud<pcl::PointXYZ>::Ptr aligned(new pcl::PointCloud<pcl::PointXYZ>);Eigen::Matrix4f final_transform;ndt_registration(source, target, final_transform, aligned);// 输出变换矩阵std::cout << "最终变换矩阵:\n" << final_transform << std::endl;// 可视化配准结果visualize_registration(source, target, aligned);return 0;
}

三、实现效果

NDT配准成功,配准得分: 2.24138e-05
最终变换矩阵:0.923224   -0.37732   0.072716  0.01792180.353408   0.908046   0.224846 -0.0605567-0.150868  -0.181885   0.971677  0.03672890          0          0          1


http://www.ppmy.cn/ops/128107.html

相关文章

数据仓库构建的两种方法:自上向下、自下向上

一、数据仓库基本介绍 数据来源&#xff1a;数据仓库的数据来源多样&#xff0c;它是数据处理、转换和加载到数据库的中央存储&#xff0c;能够让使用者轻易从数据仓库获取数据&#xff0c;并且借助商业智能和分析工具&#xff0c;将数据用于分析和决策制定。 数据仓库运行工…

如何快速解决谷歌网站页面收录难题?

在外贸网站的运营中&#xff0c;页面无法被谷歌收录是一个常见的困扰。即便你的内容再优秀&#xff0c;如果搜索引擎的爬虫无法抓取到你的页面&#xff0c;那一切努力都将白费。而GPC爬虫池服务可以帮助你快速解决网站页面的收录问题。它通过千万级的爬虫池资源&#xff0c;强力…

小程序开发语言Java跟php的区别

在小程序开发中&#xff0c;Java 和 PHP 主要在后端服务开发方面有所不同。 一、语言特性 Java&#xff1a; 强类型语言&#xff0c;语法严谨&#xff0c;具有良好的面向对象编程特性&#xff0c;支持封装、继承和多态。运行在 Java 虚拟机&#xff08;JVM&#xff09;上&…

Go入门指南-3.9与其它语言进行交互

3.9.1 与 C 进行交互 工具 cgo 提供了对 FFI&#xff08;外部函数接口&#xff09;的支持&#xff0c;能够使用 Go 代码安全地调用 C 语言库&#xff0c;你可以访问 cgo 文档主页&#xff1a;http://golang.org/cmd/cgo。cgo 会替代 Go 编译器来产生可以组合在同一个包中的 Go…

ABAQUS应用11——支座弹簧

文章目录 0、背景1、ABAQUS中几类弹簧的简介2、SPRING1的性质初探 0、背景 1、ABAQUS中几类弹簧的简介 先说参考来源&#xff0c;ABAQUS2016的帮助文档里第4卷&#xff0c;32.1.1节&#xff0c;有三种弹簧&#xff08;SPRING1 、SPRING2 以及SPRINGA&#xff09;。 三种弹簧里…

项目管理新趋势!2024年,Jira与禅道你更倾向谁?

一、 项目管理软件新趋势概述 2024 年&#xff0c;项目管理软件呈现出诸多新趋势&#xff0c;这些趋势对于项目管理的重要性日益凸显。 在数字化转型方面&#xff0c;项目管理软件成为企业实现数字化转型的关键工具。越来越多的企业认识到&#xff0c;通过项目管理软件可以实…

程序员节-回顾篇

回顾&#xff1a; 时间如白驹过隙&#xff0c;转眼间&#xff0c;我们又走过了一个充满挑战与机遇的年份。回顾过去的一年&#xff0c;心中充满了感慨与收获。 一、个人成长 这一年里&#xff0c;我在各个方面都有了显著的成长。在工作上&#xff0c;我通过不断学习和实践&a…

如何在 JavaScript 项目中限制Node.js版本

前言 在现代JavaScript开发中&#xff0c;Node.js扮演了一个非常重要的角色。不同版本的Node.js可能会带来不同的特性和行为&#xff0c;因此在项目中限制Node版本是一个很关键的步骤。 本文将通过通俗易懂的方式&#xff0c;教你如何在JavaScript项目中限制Node.js的版本。 …