[定位算法]ICP(迭代最近点)

目前三维配准中最常用的是ICP(迭代最近点)算法,该算法需要提供一个较好的初值,同时由于算法本身缺陷,最终迭代结果可能会陷入局部最优,而不是全局最优.

ICP算法原理

给定点云集X和P(在给定初始估计的旋转矩阵R和平移向量T的条件下),对P中的每一个点寻找X中的对应最近点,构成匹配点对,全部匹配点对的欧式距离之和为误差目标函数error的值,利用SVD(奇异值分解)求出新的R和T,使得error最小,将P按照R和T做旋转变化,并以此为基准再次计算,重新寻找对应点对

缺点:

  • 需要剔除噪声点(点对距离过大或者包含边界点的点对),这可能存在数据丢失情况,导致ICP估计不准
  • 基于点对的配准没有包含局部形状的信息
  • 每次迭代都要搜索最近点,耗时大
  • 可能陷入局部最优

C++代码实现

#include<iostream>
#include<pcl/io/pcd_io.h>
#include<pcl/point_types.h>
#include<pcl/registration/icp.h>
#include <pcl/visualization/pcl_visualizer.h>//可視化頭文件

typedef pcl::PointXYZ PointT;
typedef pcl::PointCloud<PointT> PointCloudT;

bool next_iteration = false;

void print4x4Matrix(const Eigen::Matrix4d & matrix)    //打印旋轉矩陣和平移矩陣
{
	printf("Rotation matrix :\n");
	printf("    | %6.3f %6.3f %6.3f | \n", matrix(0, 0), matrix(0, 1), matrix(0, 2));
	printf("R = | %6.3f %6.3f %6.3f | \n", matrix(1, 0), matrix(1, 1), matrix(1, 2));
	printf("    | %6.3f %6.3f %6.3f | \n", matrix(2, 0), matrix(2, 1), matrix(2, 2));
	printf("Translation vector :\n");
	printf("t = < %6.3f, %6.3f, %6.3f >\n\n", matrix(0, 3), matrix(1, 3), matrix(2, 3));
}

void KeyboardEventOccurred(const pcl::visualization::KeyboardEvent& event, void* nothing)
{  //使用空格鍵來增加迭代次數,並更新顯示
	if (event.getKeySym() == "space" && event.keyDown())
		next_iteration = true;
}


int main(int argc, char** argv)
{
	// 創建點雲指針
	PointCloudT::Ptr cloud_in(new PointCloudT);  // 原始點雲
	PointCloudT::Ptr cloud_tr(new PointCloudT);  // 轉換後的點雲
	PointCloudT::Ptr cloud_icp(new PointCloudT);  // ICP 輸出點雲

	//讀取pcd文件
	if (pcl::io::loadPCDFile<pcl::PointXYZ>("11.pcd", *cloud_in) == -1)
	{
		PCL_ERROR("Couldn't read file1 \n");
		return (-1);
	}
	std::cout << "Loaded " << cloud_in->size() << " data points from file1" << std::endl;

	if (pcl::io::loadPCDFile<pcl::PointXYZ>("22.pcd", *cloud_icp) == -1)
	{
		PCL_ERROR("Couldn't read file2 \n");
		return (-1);
	}
	std::cout << "Loaded " << cloud_icp->size() << " data points from file2" << std::endl;


	int iterations = 1;  // 默認的ICP迭代次數


	//定義旋轉矩陣和平移向量Matrix4d是爲4*4的矩陣
	Eigen::Matrix4d transformation_matrix = Eigen::Matrix4d::Identity();


	//初始化
	double theta = M_PI / 8;  // 旋轉的角度用弧度的表示方法
	transformation_matrix(0, 0) = cos(theta);
	transformation_matrix(0, 1) = -sin(theta);
	transformation_matrix(1, 0) = sin(theta);
	transformation_matrix(1, 1) = cos(theta);

	// Z軸的平移向量 (0.4 meters)
	transformation_matrix(2, 3) = 0.4;

	//打印轉換矩陣
	std::cout << "Applying this rigid transformation to: cloud_in -> cloud_icp" << std::endl;
	print4x4Matrix(transformation_matrix);

	// 執行點雲轉換
	pcl::transformPointCloud(*cloud_in, *cloud_icp, transformation_matrix);
	*cloud_tr = *cloud_icp;  // 備份cloud_icp賦值給cloud_tr爲後期使用

	//icp配準
	pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> icp; //創建ICP對象,用於ICP配準
	icp.setMaximumIterations(iterations);    //設置最大迭代次數iterations=true
	icp.setInputCloud(cloud_icp); //設置輸入點雲
	icp.setInputTarget(cloud_in); //設置目標點雲(輸入點雲進行仿射變換,得到目標點雲)
	icp.align(*cloud_icp);          //匹配後源點雲
	icp.setMaximumIterations(1);  // 設置爲1以便下次調用
	std::cout << "Applied " << iterations << " ICP iteration(s)" << std::endl;
	if (icp.hasConverged())//icp.hasConverged ()=1(true)輸出變換矩陣的適合性評估
	{
		std::cout << "\nICP has converged, score is " << icp.getFitnessScore() << std::endl;
		std::cout << "\nICP transformation " << iterations << " : cloud_icp -> cloud_in" << std::endl;
		transformation_matrix = icp.getFinalTransformation().cast<double>();
		print4x4Matrix(transformation_matrix);
	}
	else
	{
		PCL_ERROR("\nICP has not converged.\n");
		return (-1);
	}


	//可視化
	pcl::visualization::PCLVisualizer viewer("ICP demo");
	// 創建兩個觀察視點
	int v1(0);
	int v2(1);
	viewer.createViewPort(0.0, 0.0, 0.5, 1.0, v1);
	viewer.createViewPort(0.5, 0.0, 1.0, 1.0, v2);
	// 定義顯示的顏色信息
	float bckgr_gray_level = 0.0;  // Black
	float txt_gray_lvl = 1.0 - bckgr_gray_level;
	// 原始的點雲設置爲白色的
	pcl::visualization::PointCloudColorHandlerCustom<PointT> cloud_in_color_h(cloud_in, (int)255 * txt_gray_lvl, (int)255 * txt_gray_lvl,
		(int)255 * txt_gray_lvl);

	viewer.addPointCloud(cloud_in, cloud_in_color_h, "cloud_in_v1", v1);//設置原始的點雲都是顯示爲白色
	viewer.addPointCloud(cloud_in, cloud_in_color_h, "cloud_in_v2", v2);

	// 轉換後的點雲顯示爲綠色
	pcl::visualization::PointCloudColorHandlerCustom<PointT> cloud_tr_color_h(cloud_tr, 20, 180, 20);
	viewer.addPointCloud(cloud_tr, cloud_tr_color_h, "cloud_tr_v1", v1);

	// ICP配準後的點云爲紅色
	pcl::visualization::PointCloudColorHandlerCustom<PointT> cloud_icp_color_h(cloud_icp, 180, 20, 20);
	viewer.addPointCloud(cloud_icp, cloud_icp_color_h, "cloud_icp_v2", v2);

	// 加入文本的描述在各自的視口界面
	//在指定視口viewport=v1添加字符串“white 。。。”其中"icp_info_1"是添加字符串的ID標誌,(10,15)爲座標16爲字符大小 後面分別是RGB值
	viewer.addText("White: Original point cloud\nGreen: Matrix transformed point cloud", 10, 15, 16, txt_gray_lvl, txt_gray_lvl, txt_gray_lvl, "icp_info_1", v1);
	viewer.addText("White: Original point cloud\nRed: ICP aligned point cloud", 10, 15, 16, txt_gray_lvl, txt_gray_lvl, txt_gray_lvl, "icp_info_2", v2);

	std::stringstream ss;
	ss << iterations;            //輸入的迭代的次數
	std::string iterations_cnt = "ICP iterations = " + ss.str();
	viewer.addText(iterations_cnt, 10, 60, 16, txt_gray_lvl, txt_gray_lvl, txt_gray_lvl, "iterations_cnt", v2);

	// 設置背景顏色
	viewer.setBackgroundColor(bckgr_gray_level, bckgr_gray_level, bckgr_gray_level, v1);
	viewer.setBackgroundColor(bckgr_gray_level, bckgr_gray_level, bckgr_gray_level, v2);

	// 設置相機的座標和方向
	viewer.setCameraPosition(-3.68332, 2.94092, 5.71266, 0.289847, 0.921947, -0.256907, 0);
	viewer.setSize(1280, 1024);  // 可視化窗口的大小

	// 註冊按鍵回調函數
	viewer.registerKeyboardCallback(&KeyboardEventOccurred, (void*)NULL);

	//顯示
	while (!viewer.wasStopped())
	{
		viewer.spinOnce();

		//按下空格鍵的函數
		if (next_iteration)
		{
			// 最近點迭代算法
			icp.align(*cloud_icp);
			if (icp.hasConverged())
			{
				printf("\033[11A");  // Go up 11 lines in terminal output.
				printf("\nICP has converged, score is %+.0e\n", icp.getFitnessScore());
				std::cout << "\nICP transformation " << ++iterations << " : cloud_icp -> cloud_in" << std::endl;
				transformation_matrix *= icp.getFinalTransformation().cast<double>();  // WARNING /!\ This is not accurate!
				print4x4Matrix(transformation_matrix);  // 打印矩陣變換

				ss.str("");
				ss << iterations;
				std::string iterations_cnt = "ICP iterations = " + ss.str();
				viewer.updateText(iterations_cnt, 10, 60, 16, txt_gray_lvl, txt_gray_lvl, txt_gray_lvl, "iterations_cnt");
				viewer.updatePointCloud(cloud_icp, cloud_icp_color_h, "cloud_icp_v2");
			}
			else
			{
				PCL_ERROR("\nICP has not converged.\n");
				return (-1);
			}
		}
		next_iteration = false;
	}


	return 0;
}

运行结果

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Travis.X

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值