PCL 3D-NDT演算法點雲配準
阿新 • • 發佈:2019-01-25
本節我們將介紹如何使用正態分佈變換演算法來確定兩個大型點雲(都超過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作為猜測值。