logo资料库

点云库PCL学习教程 13.3.3官方中文文档.docx

第1页 / 共10页
第2页 / 共10页
第3页 / 共10页
第4页 / 共10页
第5页 / 共10页
第6页 / 共10页
第7页 / 共10页
第8页 / 共10页
资料共10页,剩余部分请下载后查看
如何使用正态分布变换
代码
说明
编译和运行程序
如何使用正态分布变换 在本教程中,我们将介绍如何使用正态分布变换(NDT)算法来确定两个超过 100,000 点的两 个大点云之间的刚性变换。NDT 算法是一种配准算法,使用应用于 3D 点统计模型的标准优化 技术来确定两个点云之间最可能的配准。有关 NDT 算法内部工作原理的更多信息,请参阅 Martin Magnusson 博士的博士论文“三维正态分布变换 - 用于配准,表面分析和环路检测的高效表示 法”。 代码 首先,下载数据集 room_scan1.pcd 和 room_scan2.pcd 并将它们保存到磁盘。这些点云包含来 自不同视角的同一房间的 360 度扫描。 然后,在您最喜欢的编辑器中创建一个文件,并将以下内容放入。我用 normal_distributions_transform.cpp 这个教程。 1 2 3 4 5 6 7 8 9 10 11 12 13 #include #include #include #include #include #include #include int main (int argc, char** argv) { // Loading first scan of room. pcl::PointCloud::Ptr target_cloud (new pcl::PointCloud); if (pcl::io::loadPCDFile ("room_scan1.pcd", *target_cloud) == -1) { PCL_ERROR ("Couldn't read file room_scan1.pcd \n"); return (-1); } std::cout << "Loaded " << target_cloud->size () << " data points from room_scan1.pcd" << std::endl; // Loading second scan of room from new perspective.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 pcl::PointCloud::Ptr input_cloud (new pcl::PointCloud); if (pcl::io::loadPCDFile ("room_scan2.pcd", *input_cloud) == -1) { PCL_ERROR ("Couldn't read file room_scan2.pcd \n"); return (-1); } std::cout << "Loaded " << input_cloud->size () << " data points from room_scan2.pcd" << std::endl; // Filtering input scan to roughly 10% of original size to increase speed of registration. pcl::PointCloud::Ptr filtered_cloud (new pcl::PointCloud); pcl::ApproximateVoxelGrid approximate_voxel_filter; approximate_voxel_filter.setLeafSize (0.2, 0.2, 0.2); approximate_voxel_filter.setInputCloud (input_cloud); approximate_voxel_filter.filter (*filtered_cloud); std::cout << "Filtered cloud contains " << filtered_cloud->size () << " data points from room_scan2.pcd" << std::endl; // Initializing Normal Distributions Transform (NDT). pcl::NormalDistributionsTransform ndt; // Setting scale dependent NDT parameters // Setting minimum transformation difference for termination condition. ndt.setTransformationEpsilon (0.01); // Setting maximum step size for More-Thuente line search. ndt.setStepSize (0.1); //Setting Resolution of NDT grid structure (VoxelGridCovariance). ndt.setResolution (1.0); // Setting max number of registration iterations. ndt.setMaximumIterations (35); // Setting point cloud to be aligned. ndt.setInputSource (filtered_cloud);
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 // Setting point cloud to be aligned to. ndt.setInputTarget (target_cloud); // Set initial alignment estimate found using robot odometry. Eigen::AngleAxisf init_rotation (0.6931, Eigen::Vector3f::UnitZ ()); Eigen::Translation3f init_translation (1.79387, 0.720047, 0); Eigen::Matrix4f init_guess = (init_translation * init_rotation).matrix (); // Calculating required rigid transform to align the input cloud to the target cloud. pcl::PointCloud::Ptr output_cloud (new pcl::PointCloud); ndt.align (*output_cloud, init_guess); std::cout << "Normal Distributions Transform has converged:" << ndt.hasConverged () << " score: " << ndt.getFitnessScore () << std::endl; // Transforming unfiltered, input cloud using found transform. pcl::transformPointCloud (*input_cloud, *output_cloud, ndt.getFinalTransformation ()); // Saving transformed input cloud. pcl::io::savePCDFileASCII ("room_scan2_transformed.pcd", *output_cloud); // Initializing point cloud visualizer boost::shared_ptr viewer_final (new pcl::visualization::PCLVisualizer ("3D Viewer")); viewer_final->setBackgroundColor (0, 0, 0); // Coloring and visualizing target cloud (red). pcl::visualization::PointCloudColorHandlerCustom target_color (target_cloud, 255, 0, 0); viewer_final->addPointCloud
(target_cloud, target_color, "target cloud"); viewer_final->setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "target cloud"); // Coloring and visualizing transformed input cloud (green). pcl::visualization::PointCloudColorHandlerCustom output_color (output_cloud, 0, 255, 0); viewer_final->addPointCloud (output_cloud, output_color, "output cloud"); viewer_final->setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "output cloud"); // Starting visualizer viewer_final->addCoordinateSystem (1.0, "global"); viewer_final->initCameraParameters (); // Wait until visualizer window is closed. while (!viewer_final->wasStopped ()) { viewer_final->spinOnce (100); boost::this_thread::sleep (boost::posix_time::microseconds (100000)); } return (0); } 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 10 0 10 1 10
2 10 3 10 4 10 5 10 6 10 7 10 8 10 9 说明 现在,让我们逐个分解这个代码。 #include #include 这些是使用正态分布变换算法和用于下采样数据的过滤器所需的头文件。该过滤器可以交换其他 过滤器,但我发现近似体素过滤器产生最好的结果。 // Loading first scan of room. pcl::PointCloud::Ptr target_cloud (new pcl::PointCloud); if (pcl::io::loadPCDFile ("room_scan1.pcd", *target_cloud) == -1) { PCL_ERROR ("Couldn't read file room_scan1.pcd \n"); return (-1); } std::cout << "Loaded " << target_cloud->size () << " data points from room_scan1.pcd" << std::endl; // Loading second scan of room from new perspective. pcl::PointCloud::Ptr input_cloud (new pcl::PointCloud); if (pcl::io::loadPCDFile ("room_scan2.pcd", *input_cloud) == -1) {
PCL_ERROR ("Couldn't read file room_scan2.pcd \n"); return (-1); } std::cout << "Loaded " << input_cloud->size () << " data points from room_scan2.pcd" << std::endl; 上面的代码将两个 pcd 文件加载到 pcl :: PointCloud boost 共享指针中。输入 云将被转换为目标云的参考框架。 // Filtering input scan to roughly 10% of original size to increase speed of registration. pcl::PointCloud::Ptr filtered_cloud (new pcl::PointCloud); pcl::ApproximateVoxelGrid approximate_voxel_filter; approximate_voxel_filter.setLeafSize (0.2, 0.2, 0.2); approximate_voxel_filter.setInputCloud (input_cloud); approximate_voxel_filter.filter (*filtered_cloud); std::cout << "Filtered cloud contains " << filtered_cloud->size () << " data points from room_scan2.pcd" << std::endl; 此部分过滤输入云以提高注册时间。任何对数据进行统一下采样的过滤器都可以用于本节。目标 云不需要过滤,因为 NDT 算法使用的体素网格数据结构不使用单个点,而是使用包含在每个数 据结构体素单元中的点的统计数据。 // Initializing Normal Distributions Transform (NDT). pcl::NormalDistributionsTransform ndt; 这里我们用默认值创建 NDT 算法。内部数据结构直到以后才会被初始化。 // Setting scale dependent NDT parameters // Setting minimum transformation difference for termination condition. ndt.setTransformationEpsilon (0.01); // Setting maximum step size for More-Thuente line search. ndt.setStepSize (0.1); //Setting Resolution of NDT grid structure (VoxelGridCovariance). ndt.setResolution (1.0); 接下来,我们需要修改一些与尺度相关的参数。由于 NDT 算法使用体素化数据结构和 More-Thuente 线搜索,因此需要缩放一些参数以适合数据集。上述参数似乎在我们正在使用的 规模(房间的大小)上运行良好,但为了处理较小的物体(例如咖啡杯的扫描),它们需要大大 减少。 变换 Epsilon 参数分别定义了以米和弧度为单位的变换向量[x,y,z,roll,pitch,yaw]的最小, 允许,增量变化。一旦增量变化低于此阈值,对齐终止。Step Size 参数定义了 More-Thuente 线搜索所允许的最大步长。该线搜索算法确定低于此最大值的最佳步长,缩小步长,因为您接近
最佳解决方案。更大的最大步长将能够以更少的迭代清除更远的距离,但有过冲的风险,并以不 希望的局部最小值结束。最后,分辨率参数定义了内部 NDT 网格结构的体素分辨率。这个结构 很容易搜索,每个体素包含统计数据,平均值,协方差等,与它所包含的点相关联。统计数据用 于将云模拟为一组多元高斯分布,并允许我们计算和优化在体素内任何位置存在点的概率。该参 数是与尺度最相关的。它需要足够大,以使每个体素至少包含 6 个点但足够小以便唯一地描述 环境。 // Setting max number of registration iterations. ndt.setMaximumIterations (35); 该参数控制优化器可以运行的最大迭代次数。大多数情况下,优化器将在达到此限制之前终止转 换 Epsilon,但这有助于防止它在错误的方向上运行太久。 // Setting point cloud to be aligned. ndt.setInputSource (filtered_cloud); // Setting point cloud to be aligned to. ndt.setInputTarget (target_cloud); 在这里,我们将点云传递给 NDT 注册程序。输入云是将要转换的云,目标云是输入云将与之对 齐的参考帧。当添加目标云时,使用目标云数据初始化 NDT 算法的内部数据结构。 // Set initial alignment estimate found using robot odometry. Eigen::AngleAxisf init_rotation (0.6931, Eigen::Vector3f::UnitZ ()); Eigen::Translation3f init_translation (1.79387, 0.720047, 0); Eigen::Matrix4f init_guess = (init_translation * init_rotation).matrix (); 在这部分代码中,我们创建了一个关于对齐点云所需的转换的初始猜测。虽然算法可以在没有这 种初始转换的情况下运行,但您倾向于使用一个更好的结果,特别是在参考帧之间存在较大差异 的情况下。在机器人应用程序中,例如用于生成此数据集的应用程序,通常使用 odometry 数据 生成初始转换。 // Calculating required rigid transform to align the input cloud to the target cloud. pcl::PointCloud::Ptr output_cloud (new pcl::PointCloud); ndt.align (*output_cloud, init_guess); std::cout << "Normal Distributions Transform has converged:" << ndt.hasConverged () << " score: " << ndt.getFitnessScore () << std::endl; 最后,我们准备对齐点云。生成的转换输入云存储在输出云中。然后,我们显示对齐的结果以及 欧几里德健身得分,计算为从输出云到目标云中最近点的平方距离之和。 // Transforming unfiltered, input cloud using found transform.
分享到:
收藏