资讯详情

资讯详情

建站行业动态 · 设计趋势 · 数字化升级干货

经典配准算法二:NDT算法

经典配准算法二:NDT算法 1.定义正态分布变换Normal Distributions TransformNDT是一种经典的三维点云配准算法其核心思想是将目标点云所在空间划分为多个规则的体素网格并利用网格内点云的空间分布特征建立正态分布模型从而将离散的目标点云转换为连续的概率密度表示。在配准过程中通过不断改变源点云的旋转和平移参数使变换后的源点云在目标点云建立的概率分布中的匹配概率最大最终求得两组点云之间的最优刚体变换。与传统ICP算法依赖点与点之间的对应关系不同NDT通过描述局部空间内点云的统计分布建立匹配关系因此不需要显式搜索最近邻对应点在点云初始位姿存在一定偏差的情况下通常具有较好的配准效率和鲁棒性。NDT算法目前广泛应用于三维点云配准、移动机器人定位、激光雷达建图以及自动驾驶等领域。2.具体步骤步骤1输入源点云和目标点云首先将待配准的源点云和目标点云输入NDT算法并根据实际情况设置源点云的初始位姿以及体素网格分辨率等参数。步骤2目标点云空间划分将目标点云所在的三维空间划分为若干规则的体素网格并统计每个网格内部包含的点云数据。步骤3建立正态分布模型对于每一个包含足够点数的体素网格根据其中点云的空间分布计算均值和协方差矩阵从而建立该网格对应的三维正态分布模型。均值用于描述点云的空间中心位置协方差矩阵用于描述点云在不同方向上的分布特征。步骤4变换源点云利用当前估计的旋转参数和平移参数对源点云进行刚体变换将源点云映射到目标点云所在的坐标系中。步骤5计算匹配概率将变换后的源点云映射到目标点云对应的体素网格中根据各体素建立的正态分布模型计算源点落入对应概率分布中的匹配概率并以此构建NDT配准的优化目标函数。步骤6优化位姿参数采用优化算法不断调整源点云的旋转和平移参数使源点云在目标点云概率分布模型中的匹配概率最大从而逐渐减小两组点云之间的配准误差。步骤7判断是否收敛当目标函数达到收敛条件、位姿参数变化小于设定阈值或者达到最大迭代次数时停止迭代并将当前获得的位姿参数作为最终配准结果否则返回步骤4继续进行迭代优化。3.算法缺点​​​​​​虽然NDT算法不需要显式建立点与点之间的对应关系并且在点云初始位姿存在一定偏差时具有较好的配准能力但其仍存在一些局限性。首先NDT算法对体素网格分辨率较为敏感分辨率过大时会导致局部点云结构信息丢失降低配准精度分辨率过小时则会产生大量体素网格增加计算量。其次NDT算法对点云分布特征具有一定依赖性当局部区域点云数量较少或分布较为单一时难以建立稳定、可靠的正态分布模型从而影响配准结果。此外在大规模点云或点云密度较高的情况下NDT需要构建和维护大量体素及其统计模型可能带来较大的计算和存储开销。同时NDT本质上仍属于局部优化方法当两组点云存在非常大的初始位姿差异、重叠区域过小或环境中存在大量重复结构时也可能陷入局部最优。因此在实际点云拼接过程中通常需要结合合理的初始位姿估计、体素分辨率选择以及其他粗配准方法以进一步提高NDT算法的稳定性和配准精度。4.代码实现本文采用Visual Studio 2017作为程序开发环境使用C语言结合PCL 1.8.1点云库实现NDT点云配准。PCL提供了pcl::NormalDistributionsTransformNDT配准接口可以直接完成源点云与目标点云之间的配准。代码实现过程中首先读取待配准的源点云和目标点云然后设置NDT算法的分辨率、步长、最大迭代次数以及收敛阈值等参数最后调用NDT配准函数计算两组点云之间的最优刚体变换并通过最终变换矩阵对源点云进行变换得到配准后的点云结果#include iostream #include string // PCL点云 #include pcl/io/pcd_io.h #include pcl/point_types.h #include pcl/point_cloud.h // NDT配准 #include pcl/registration/ndt.h // 点云变换 #include pcl/common/transforms.h // 点云可视化 #include pcl/visualization/pcl_visualizer.h // Eigen #include Eigen/Dense using namespace std; int main() { // // 1. 定义点云类型 // typedef pcl::PointXYZ PointT; // 源点云 pcl::PointCloudPointT::Ptr sourceCloud( new pcl::PointCloudPointT); // 目标点云 pcl::PointCloudPointT::Ptr targetCloud( new pcl::PointCloudPointT); // NDT配准后的点云 pcl::PointCloudPointT::Ptr alignedCloud( new pcl::PointCloudPointT); // // 2. 设置点云文件路径 // string sourcePath source.pcd; string targetPath target.pcd; cout endl; cout NDT Point Cloud Registration endl; cout endl; // // 3. 读取源点云 // if (pcl::io::loadPCDFilePointT( sourcePath, *sourceCloud) -1) { cerr 无法读取源点云 endl; cerr 路径 sourcePath endl; system(pause); return -1; } // // 4. 读取目标点云 // if (pcl::io::loadPCDFilePointT( targetPath, *targetCloud) -1) { cerr 无法读取目标点云 endl; cerr 路径 targetPath endl; system(pause); return -1; } // // 5. 输出点云信息 // cout endl; cout 源点云点数 sourceCloud-points.size() endl; cout 目标点云点数 targetCloud-points.size() endl; // // 6. 创建NDT对象 // pcl::NormalDistributionsTransformPointT, PointT ndt; // // 7. 设置输入点云 // ndt.setInputSource(sourceCloud); ndt.setInputTarget(targetCloud); // // 8. 设置NDT参数 // // ------------------------------------------------------------ // 8.1 设置NDT分辨率 // ------------------------------------------------------------ // 分辨率越大 // 体素数量减少计算速度提高 // 但局部结构信息可能损失 // // 分辨率越小 // 能够保留更多局部结构 // 但计算量增加 // // 单位与点云坐标单位一致 // ------------------------------------------------------------ ndt.setResolution(1.0); // ------------------------------------------------------------ // 8.2 设置步长 // ------------------------------------------------------------ ndt.setStepSize(0.1); // ------------------------------------------------------------ // 8.3 设置最大迭代次数 // ------------------------------------------------------------ ndt.setMaximumIterations(50); // ------------------------------------------------------------ // 8.4 设置收敛阈值 // ------------------------------------------------------------ ndt.setTransformationEpsilon(0.01); // // 9. 设置初始变换矩阵 // Eigen::Matrix4f initGuess Eigen::Matrix4f::Identity(); // 如果已经通过其他粗配准算法得到 // 初始变换矩阵可以在这里输入。 // // 例如 // // initGuess(0, 3) 0.1; // initGuess(1, 3) 0.2; // initGuess(2, 3) 0.0; // // 10. 执行NDT配准 // cout endl; cout 开始执行NDT配准... endl; ndt.align( *alignedCloud, initGuess); // // 11. 判断NDT是否收敛 // if (!ndt.hasConverged()) { cerr endl; cerr NDT配准失败 endl; cerr 算法没有收敛。 endl; system(pause); return -1; } cout endl; cout NDT配准成功 endl; // // 12. 获取最终变换矩阵 // Eigen::Matrix4f transformation ndt.getFinalTransformation(); // // 13. 输出最终变换矩阵 // cout endl; cout endl; cout NDT配准结果 endl; cout endl; cout 最终变换矩阵 endl; cout transformation endl; // // 14. 获取旋转矩阵 // Eigen::Matrix3f rotation transformation.block3, 3(0, 0); cout endl; cout 旋转矩阵 R endl; cout rotation endl; // // 15. 获取平移向量 // Eigen::Vector3f translation transformation.block3, 1(0, 3); cout endl; cout 平移向量 t endl; cout translation endl; // // 16. 输出NDT迭代次数 // cout endl; cout NDT迭代次数 ndt.getFinalNumIteration() endl; // // 17. 输出Fitness Score // cout NDT Fitness Score ndt.getFitnessScore() endl; // // 18. 保存配准后的点云 // string outputPath ndt_aligned.pcd; pcl::io::savePCDFileBinary( outputPath, *alignedCloud); cout endl; cout 配准后的点云已经保存 endl; cout outputPath endl; // // 19. 创建可视化窗口 // pcl::visualization::PCLVisualizer viewer( NDT Point Cloud Registration); // // 20. 设置背景颜色 // viewer.setBackgroundColor( 0.05, 0.05, 0.05); // // 21. 显示目标点云 // pcl::visualization::PointCloudColorHandlerCustomPointT targetColor( targetCloud, 0, 255, 0); viewer.addPointCloudPointT( targetCloud, targetColor, target); viewer.setPointCloudRenderingProperties( pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, target); // // 22. 显示NDT配准后的源点云 // pcl::visualization::PointCloudColorHandlerCustomPointT sourceColor( alignedCloud, 255, 0, 0); viewer.addPointCloudPointT( alignedCloud, sourceColor, source); viewer.setPointCloudRenderingProperties( pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, source); // // 23. 添加坐标系 // viewer.addCoordinateSystem( 1.0); // // 24. 初始化相机参数 // viewer.initCameraParameters(); // // 25. 显示配准结果 // while (!viewer.wasStopped()) { viewer.spinOnce(100); } // // 26. 程序结束 // cout endl; cout NDT配准程序执行完成 endl; system(pause); return 0; }其中setResolution()是NDT算法中非常关键的参数它决定了目标点云被划分的体素网格大小。对于你的道路路面点云拼接实验可以重点讨论不同分辨率对配准精度、迭代次数和运行时间的影响这也比较适合作为后面的实验分析内容。

相关资讯