1. 程式人生 > >PCL 3D-NDT演算法點雲配準

PCL 3D-NDT演算法點雲配準

本節我們將介紹如何使用正態分佈變換演算法來確定兩個大型點雲(都超過100,000個點)之間的剛體變換。正態分佈變換演算法是一個配准算法,它應用於三維點的統計模型,使用標準最優化技術來確定兩個點雲間的最優的匹配,因為其在配準過程中不利用對應點的特徵計算和匹配,所以時間比其他方法快,更多關於正態分佈變換演算法的詳細的資訊,請看Martin Magnusson博士的博士畢業論文“The Three-Dimensional Normal Distributions Transform – an Efficient Representation for Registration, Surface Analysis, and Loop Detection”
官方教程

http://pointclouds.org/documentation/tutorials/normal_distributions_transform.php#normal-distributions-transform
中文版
http://www.pclcn.org/study/shownews.php?lang=cn&id=80
NDT的理解 http://ghx0x0.github.io/2014/12/30/NDT-match/
程式碼

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.
h> #include <pcl/registration/ndt.h> #include <pcl/filters/approximate_voxel_grid.h> #include <pcl/visualization/pcl_visualizer.h> #include <boost/thread/thread.hpp> int main(int argc, char** argv) { //載入房間的第一次掃描 pcl::PointCloud<pcl::PointXYZ>::Ptr target_cloud(new pcl::PointCloud
<pcl::PointXYZ>); if (pcl::io::loadPCDFile<pcl::PointXYZ>("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; //載入從新視角得到的房間的第二次掃描 pcl::PointCloud<pcl::PointXYZ>::Ptr input_cloud(new pcl::PointCloud<pcl::PointXYZ>); if (pcl::io::loadPCDFile<pcl::PointXYZ>("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; //將輸入的掃描過濾到原始尺寸的大概10%以提高匹配的速度。 pcl::PointCloud<pcl::PointXYZ>::Ptr filtered_cloud(new pcl::PointCloud<pcl::PointXYZ>); pcl::ApproximateVoxelGrid<pcl::PointXYZ> 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) pcl::NormalDistributionsTransform<pcl::PointXYZ, pcl::PointXYZ> ndt; //設定依賴尺度NDT引數 //為終止條件設定最小轉換差異 ndt.setTransformationEpsilon(0.01); //為More-Thuente線搜尋設定最大步長 ndt.setStepSize(0.1); //設定NDT網格結構的解析度(VoxelGridCovariance) ndt.setResolution(1.0); //設定匹配迭代的最大次數 ndt.setMaximumIterations(35); // 設定要配準的點雲 ndt.setInputCloud(filtered_cloud); //設定點雲配準目標 ndt.setInputTarget(target_cloud); //設定使用機器人測距法得到的初始對準估計結果 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(); //計算需要的剛體變換以便將輸入的點雲匹配到目標點雲 pcl::PointCloud<pcl::PointXYZ>::Ptr output_cloud(new pcl::PointCloud<pcl::PointXYZ>); ndt.align(*output_cloud, init_guess); std::cout << "Normal Distributions Transform has converged:" << ndt.hasConverged() << " score: " << ndt.getFitnessScore() << std::endl; //使用建立的變換對未過濾的輸入點雲進行變換 pcl::transformPointCloud(*input_cloud, *output_cloud, ndt.getFinalTransformation()); //儲存轉換的輸入點雲 pcl::io::savePCDFileASCII("room_scan2_transformed.pcd", *output_cloud); // 初始化點雲視覺化介面 boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer_final(new pcl::visualization::PCLVisualizer("3D Viewer")); viewer_final->setBackgroundColor(0, 0, 0); //對目標點雲著色(紅色)並可視化 pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> target_color(target_cloud, 255, 0, 0); viewer_final->addPointCloud<pcl::PointXYZ>(target_cloud, target_color, "target cloud"); viewer_final->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "target cloud"); //對轉換後的目標點雲著色(綠色)並可視化 pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> output_color(output_cloud, 0, 255, 0); viewer_final->addPointCloud<pcl::PointXYZ>(output_cloud, output_color, "output cloud"); viewer_final->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "output cloud"); // 啟動視覺化 viewer_final->addCoordinateSystem(1.0); viewer_final->initCameraParameters(); //等待直到視覺化視窗關閉。 while (!viewer_final->wasStopped()) { viewer_final->spinOnce(100); boost::this_thread::sleep(boost::posix_time::microseconds(100000)); } return (0); }

例程中先用立方體方法精簡模型,並根據掃描房間的時候機器人給出的估計轉換矩陣輸入到NDT作為猜測值。