PFH+ICP算法点云配准研究,另附数据文件数据

FPFH+ICP算法点云配准研究,另附数据文件数据

注:有数据转换c++代码,可以把ply文件转换pcd格式文件
在网上搜了一些文章,感觉比较好的可供参考:
感谢博主的思路
基本思路为:
(1)从待配准点云P中选取n个采样点,为了尽量保证所采样的点具有不同的FPFH特征,采样点两两之间的距离应满足大于预先给定最小距离阈值d。
(2)在目标点云Q中查找与点云P中采样点具有相似FPFH特征的一个或多个点,从这些相似点中随机选取一个点作为点云P在目标点云Q中的一一对应点。
(3) 计算对应点之间刚体变换矩阵, 然后通过求解对应点变换后的“距离误差和”函数来判断当前配准变换的性能。此处的距离误差和函数多使用Huber罚函数表示。

迭代最近点算法(Iterative Cloest Point, ICP)
ICP算法基于SVD,其大致思路如下:
(1) 将初始配准后的两片点云P′(经过坐标变换后的源点云)和Q,作为精配准的初始点集;
(2) 对源点云P’中的每一点pi,在目标点云Q中寻找距离最近的对应点qi,作为该点在目标点云中的对应点,组成初始对应点对;
(3) 初始对应点集中的对应关系并不都是正确的,错误的对应关系会影响最终的配准结果,采用方向向量阈值剔除错误的对应点对;
(4) 计算旋转矩阵R和平移向量T,使最小,即对应点集之间的均方误差最小;
(5) 设定某一阈值ε=dk-dk-1和最大迭代次数Nmax, 将上一步得到的刚体变换作用于源点云P′,得到新点云P”,计算P”和Q的距离误差,,如果两次迭代的误差小于阈值ε或者当前迭代次数大于Nmax,则迭代结束,否则将初始配准的点集更新为P”和Q,继续重复上述步骤,直至满足收敛条件。
ICP算法对参数敏感,在使用前要设置一下几个参数:
1.setMaximumIterations, 最大迭代次数,icp是一个迭代的方法,最多迭代这些次;
2. setEuclideanFitnessEpsilon, 设置收敛条件是均方误差和小于阈值,停止迭代;
3. setTransformtionEpsilon, 设置两次变化矩阵之间的差值(一般设置为1e-10即可);
4. setMaxCorrespondenaceDistance,设置对应点对之间的最大距离(此值对配准结果影响较大)。
在两点云相差较大的情况下,ICP算法容易陷入局部最优解,从而无法得到较好的匹配结果,故需要先给定一个初始变换矩阵。在pcl库中的registration模块可实现ICP算法。

##代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using pcl::NormalEstimation;
using pcl::search::KdTree;
typedef pcl::PointXYZ pointT;
typedef pcl::PointCloud<pointT> PointCloud;

int
main(int argc, char **argv)
{
     
	//加载点云文件
	PointCloud::Ptr cloud_src_o(new PointCloud);//源点云
	PointCloud::Ptr cloud_tgt_o(new PointCloud);//目标点云
	if (pcl::io::loadPCDFile("bun000.pcd", *cloud_src_o) == -1)
	{
     
		PCL_ERROR("couldn'e file! ");
		return(-1);
	}
	
	if (pcl::io::loadPCDFile("bun045.pcd", *cloud_tgt_o) == -1)
	{
     
		PCL_ERROR("couldn'e file! ");
		return(-1);
	}
	clock_t start = clock();

	//出去NAN点
	std::vector<int> indices_src;
	pcl::removeNaNFromPointCloud(*cloud_src_o, *cloud_src_o, indices_src);
	std::cout << "remove *cloud_src_o nan !"<<std::endl;

	//下采样
	pcl::VoxelGrid<pointT> voxel_grid;
	voxel_grid.setLeafSize(0.012, 0.012, 0.012);
	voxel_grid.setInputCloud(cloud_src_o);
	PointCloud::Ptr cloud_src(new PointCloud);
	voxel_grid.filter(*cloud_src);
	std::cout << "down size *cloud_src_o from " << cloud_src_o->points.size() << "to" << cloud_src->size() << endl;
	pcl::io::savePCDFileASCII("bunny_src_down.pcd", *cloud_src);

	//计算表面法线
	pcl::NormalEstimation<pointT, pcl::Normal> ne_src;
	ne_src.setInputCloud(cloud_src);
	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree_src(new pcl::search::KdTree<pcl::PointXYZ>());
	ne_src.setSearchMethod(tree_src);
	pcl::PointCloud<pcl::Normal>::Ptr cloud_src_normals(new pcl::PointCloud<pcl::Normal>);
	ne_src.setRadiusSearch(0.02);
	ne_src.compute(*cloud_src_normals);

	std::vector<int> indices_tgt;
	pcl::removeNaNFromPointCloud(*cloud_tgt_o, *cloud_tgt_o, indices_tgt);
	std::cout << "remove *cloud_tgt_o nan! " << std::endl;

	pcl::VoxelGrid<pcl::PointXYZ> voxel_grid_2;
	voxel_grid_2.setLeafSize(0.012, 0.012,0.012);
	voxel_grid_2.setInputCloud(cloud_tgt_o);
	PointCloud::Ptr cloud_tgt(new pcl::PointCloud<pcl::PointXYZ>);
	voxel_grid_2.filter(*cloud_tgt);
	std::cout << "down size *cloud_tgt_o.pcd from " << cloud_tgt_o->size() << "to" << cloud_tgt->points.size() << endl;
	pcl::io::savePCDFileASCII("bunny_tgt_down.pcd", *cloud_tgt);

	pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne_tgt;
	ne_tgt.setInputCloud(cloud_tgt);
	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree_tgt(new pcl::search::KdTree<pcl::PointXYZ>());
	ne_tgt.setSearchMethod(tree_tgt);
	pcl::PointCloud<pcl::Normal>::Ptr cloud_tgt_normals(new pcl::PointCloud<pcl::Normal>);
	ne_tgt.setRadiusSearch(0.02);
	ne_tgt.compute(*cloud_tgt_normals);

	//计算FPFH
	pcl::FPFHEstimation<pcl::PointXYZ,pcl::Normal,pcl::FPFHSignature33> fpfh_src;
	fpfh_src.setInputCloud(cloud_src);
	fpfh_src.setInputNormals(cloud_src_normals);
	pcl::search::KdTree<pointT>::Ptr tree_src_fpfh(new pcl::search::KdTree<pointT>);
	fpfh_src.setSearchMethod(tree_src_fpfh);
	pcl::PointCloud<pcl::FPFHSignature33>::Ptr fpfhs_src(new pcl::PointCloud<pcl::FPFHSignature33>());
	fpfh_src.setRadiusSearch(0.05);
	fpfh_src.compute(*fpfhs_src);
	std::cout << "compute *cloud_src fpfh" << endl;

	pcl::FPFHEstimation<pcl::PointXYZ, pcl::Normal, pcl::FPFHSignature33> fpfh_tgt;
	fpfh_tgt.setInputCloud(cloud_tgt);
	fpfh_tgt.setInputNormals(cloud_tgt_normals);
	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree_tgt_fpfh(new pcl::search::KdTree<pcl::PointXYZ>);
	fpfh_tgt.setSearchMethod(tree_tgt_fpfh);
	pcl::PointCloud<pcl::FPFHSignature33>::Ptr fpfhs_tgt(new pcl::PointCloud<pcl::FPFHSignature33>);
	fpfh_tgt.setRadiusSearch(0.05);
	fpfh_tgt.compute(*fpfhs_tgt);
	std::cout << "compute *cloud_tgt fpfh" << endl;

	//SAC配准
	pcl::SampleConsensusInitialAlignment<pcl::PointXYZ, pcl::PointXYZ, pcl::FPFHSignature33> scia;
	scia.setInputSource(cloud_src);
	scia.setInputTarget(cloud_tgt);
	scia.setSourceFeatures(fpfhs_src);
	scia.setTargetFeatures(fpfhs_tgt);
	PointCloud::Ptr sac_result(new PointCloud);
	scia.align(*sac_result);
	std::cout << "sac has converged:" << scia.hasConverged() << "  score: " << scia.getFitnessScore() << endl;
	Eigen::Matrix4f sac_trans;
	sac_trans = scia.getFinalTransformation();
	std::cout << sac_trans << endl;
	pcl::io::savePCDFileASCII("bunny_transformed_sac.pcd", *sac_result);
	clock_t sac_time = clock();

	//icp配准
	PointCloud::Ptr icp_result(new PointCloud);
	pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> icp;
	icp.setInputSource(cloud_src);
	icp.setInputTarget(cloud_tgt);
	icp.setMaxCorrespondenceDistance(0.04);
	//最大迭代次数
	icp.setMaximumIterations(50);
	//两次变化矩阵之间的差值
	icp.setTransformationEpsilon(1e-10);
	//均方误差
	icp.setEuclideanFitnessEpsilon(0.2);
	icp.align(*icp_result, sac_trans);
	clock_t end = clock();
	cout << "total time : " << (double)(end - start) / (double)CLOCKS_PER_SEC << "s" << endl;
	//我把计算法线和点特征直方图的时间也算在SAC里面了
	cout << "sac time: " << (double)(sac_time - start) / (double)CLOCKS_PER_SEC << " s" << endl;
	cout << "icp time: " << (double)(end - sac_time) / (double)CLOCKS_PER_SEC << " s" << endl;
	std::cout << "ICP has converged:" << icp.hasConverged()
		<< " score: " << icp.getFitnessScore() << std::endl;

	Eigen::Matrix4f icp_trans;
	icp_trans = icp.getFinalTransformation();
	std::cout << "the icp.getfinaltransformation is " << icp_trans << endl;
	//使用创建的变换对未过滤的点云进行变换
	pcl::transformPointCloud(*cloud_src_o, *icp_result, icp_trans);
	pcl::io::savePCDFileASCII("bunny_transformed_sac_ndt.pcd", *icp_result);
	return (0);
}

生成的图片:
PFH+ICP算法点云配准研究,另附数据文件数据_第1张图片
配准前的点云
PFH+ICP算法点云配准研究,另附数据文件数据_第2张图片
##数据来源一般在网上下载斯坦福大学的数据,但是其数据格式是ply格式,需要转换到pcd格式
斯坦福大学数据链接

转换的代码为

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include   

using namespace pcl;
using namespace pcl::io;
using namespace pcl::console;

int main()
{
     
	//pcl::PointCloud::Ptr cloud(new pcl::PointCloud());
	pcl::io::loadPCDFile("model1.pcd",*cloud);  

	ply文件显示  
	//pcl::PolygonMesh mesh;
	//vtkSmartPointer polydata = vtkSmartPointer::New();

	//pcl::io::loadPolygonFilePLY("raw.ply", mesh);
	 ply另存vtk  
	pcl::io::saveVTKFile("temp.vtk", mesh);  
	//pcl::io::mesh2vtk(mesh, polydata);

	//pcl::io::vtkPolyDataToPointCloud(polydata, *cloud);

	两种存贮方式 pcd另存pcd  
	//pcl::PCDWriter pcdwriter;
	pcdwriter.write("save_ply2vtk2pcd.pcd", *cloud);  
	//pcl::io::savePCDFileASCII("raw1.pcd", *cloud);

	//*VCGLIB生成 的ply
	/*pcl::PCLPointCloud2 clod;
	pcl::io::loadPLYFile("raw.ply", clod);
	pcl::io::savePCDFile("raw11.pcd", clod);*/

	pcl::PCLPointCloud2 clod;
	pcl::PLYReader reader;
	reader.read("bun045.ply", clod);
	pcl::PCDWriter writer;
	writer.writeASCII("bun045.pcd", clod);

	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());
	//对于ply文件中格式为RGBA格式,则上句改写为
	//pcl::PointCloud::Ptr cloud(new pcl::PointCloud());
	pcl::io::loadPCDFile("raw11.pcd", *cloud);

	/*
	boost::shared_ptr viewe(new pcl::visualization::PCLVisualizer("ss"));
	//viewe->initCameraParameters();
	viewe->setBackgroundColor(0, 0, 0);
	//viewe->addCoordinateSystem(1.0f);
	pcl::visualization::PointCloudColorHandlerRGBField color(cloud);
	//对于ply文件中格式为RGBA格式,则上句改写为
	//pcl::visualization::PointCloudColorHandlerRGBField color(cloud);
	viewe->addPointCloud(cloud, color, "cloud");
	//对于ply文件中格式为RGBA格式,则上句改写为
	//viewe->addPointCloud(cloud, color, "cloud");

	//viewe->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_FONT_SIZE, 2, "cloud");
	while (!viewe->wasStopped()) {
		viewe->spinOnce(100);
		boost::this_thread::sleep(boost::posix_time::microseconds(100000));
	}
	*/
	return 0;

}

上面的代码可以转换成无RGB信息的三维点云。也可以在我的百度网盘里直接下载,仅供参考:
百度网盘链接
提取吗:47oq

你可能感兴趣的:(点云数据配准,算法)