PCL SAC-IA 算法实现点云粗配准(永久免费版)
目录
- 一、概述
- 1.1 原理
- 1.2 实现步骤
- 1.3应用场景
- 二、关键函数
- 2.1 头文件
- 2.2 重定义
- 2.3 计算法向量和FPFH
- 2.4 关键点提取
- 2.5 去除点云中的NAN点的函数
- 2.6 SAC-IA 算法核心代码
- 2.7 可视化
- 三、完整代码
- 四、结果展示
即日起,付费专栏所有内容将以永久免费形式陆续进行发表!!!
一、概述
1.1 原理
SAC-IA (Sample Consensus Initial Alignment) 算法是一种常用的点云粗配准方法,它通常用于无人车、机器人视觉系统等领域的点云数据对之间的快速初始配准。
1.2 实现步骤
这个算法基于特征匹配的思想,步骤大致包括:
- 采样关键点:从每个点云中选取一些关键点(通常是局部兴趣点或描述符),如SIFT、SURF或SHOT等。
- 特征匹配:计算关键点之间的对应关系,例如通过匹配模板描述符寻找最相似的关键点对。
- 一致性检验:使用RANSAC (Random Sample Consensus) 技术,随机选取一组候选对,在小样本集上尝试找到最佳的配准变换(比如刚体变换),同时估计误差模型。
- 迭代优化:如果RANSAC成功找到了一个好的解,就使用这个解初始化非线性优化器(如Levenberg-Marquardt或ICP),进一步优化配准结果,提高精度。
- 重复抽样:RANSAC会多次重复上述过程,每次从数据集中选择不同的样本,取所有成功的变换的平均值作为最终配准结果。
SAC-IA算法的优点在于其鲁棒性和效率,适合大规模点云间的初步配准,但它可能会错过一些真实的对应关系。后续通常会结合更精确的全局优化方法来进行精细配准。
1.3应用场景
SAC-IA (Simultaneous Localization and Mapping with Inertial Alignment) 算法是一种结合了视觉、惯性测量单元(IMU)数据的粗略定位与地图构建技术。它的主要应用场景包括:
- 室内导航与机器人定位:在没有GPS信号的室内环境中,如商场、办公室或地下车库,SAC-IA利用相机捕捉的特征点和IMU的数据进行实时位置估计。
- 自动驾驶汽车:车辆在起步阶段或者城市高架桥等GPS信号较弱的地方,SAC-IA可以辅助自动驾驶系统快速确定初始位置并建立环境模型。
- 大型无人机航拍:无人机在起飞或切换飞行模式时,需要对当前位置进行快速粗校准,SAC-IA能够提供准确的基础定位信息。
- 智能手机AR应用:增强现实应用程序在移动设备上运行时,可能依赖于这种算法来稳定设备姿态,并在用户视场中放置虚拟内容。
二、关键函数
2.1 头文件
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/filters/voxel_grid.h> // 体素滤波器,用于点云下采样
#include <pcl/features/normal_3d_omp.h> // 使用多线程加速法向量估计
#include <pcl/features/fpfh_omp.h> // FPFH加速计算
#include <pcl/registration/ia_ransac.h> // SAC-IA初始配准算法
#include <boost/thread/thread.hpp>
#include <pcl/visualization/pcl_visualizer.h>
2.2 重定义
为代码书写方便,对复杂代码进行重定义简化处理。
typedef pcl::PointCloud<pcl::PointXYZ> pointcloud; // 点云类型
typedef pcl::PointCloud<pcl::Normal> pointnormal; // 法向量类型
typedef pcl::PointCloud<pcl::FPFHSignature33> fpfhFeature; // FPFH特征类型
2.3 计算法向量和FPFH
SAC-IA 算法需要特征描述子
// 计算FPFH特征的函数
fpfhFeature::Ptr compute_fpfh_feature(pointcloud::Ptr input_cloud, pcl::search::KdTree<pcl::PointXYZ>::Ptr tree)
{// 1. 估计法向量pointnormal::Ptr normals(new pointnormal); // 用于存储计算得到的法向量pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> ne; // 使用OMP加速法向量计算ne.setInputCloud(input_cloud); // 设置输入点云ne.setNumberOfThreads(8); // 设置并行计算的线程数ne.setSearchMethod(tree); // 使用KD树加速搜索ne.setKSearch(10); // 设置K近邻点数量为10ne.compute(*normals); // 计算法向量// 2. 计算FPFH特征fpfhFeature::Ptr fpfh(new fpfhFeature); // 创建FPFH特征存储容器pcl::FPFHEstimationOMP<pcl::PointXYZ, pcl::Normal, pcl::FPFHSignature33> fpfh_estimator; // FPFH特征估计器fpfh_estimator.setNumberOfThreads(8); // 设置并行计算线程数fpfh_estimator.setInputCloud(input_cloud); // 设置输入点云fpfh_estimator.setInputNormals(normals); // 设置法向量fpfh_estimator.setSearchMethod(tree); // 使用KD树加速搜索fpfh_estimator.setKSearch(10); // 设置K近邻点数量为10fpfh_estimator.compute(*fpfh); // 计算FPFH特征return fpfh;
}
2.4 关键点提取
点数太多计算效率会很慢,这里使用下采样的方法获取关键点。
// 点云下采样函数
pointcloud::Ptr downsample_cloud(pointcloud::Ptr input_cloud, float leaf_size)
{// 使用体素滤波器对点云进行下采样pcl::VoxelGrid<pcl::PointXYZ> voxel_grid;voxel_grid.setLeafSize(leaf_size, leaf_size, leaf_size); // 设置体素的大小pointcloud::Ptr filtered_cloud(new pointcloud); // 创建存储下采样后的点云voxel_grid.setInputCloud(input_cloud); // 设置输入点云voxel_grid.filter(*filtered_cloud); // 执行下采样return filtered_cloud; // 返回下采样后的点云
}
2.5 去除点云中的NAN点的函数
一些点云中存在无效点,使用该函数进行无效点的删除。
// 去除点云中的NAN点的函数
void remove_nan_points(pointcloud::Ptr input_cloud)
{// 移除输入点云中的NAN点vector<int> indices;pcl::removeNaNFromPointCloud(*input_cloud, *input_cloud, indices); // 去除NAN并保存有效索引
}
2.6 SAC-IA 算法核心代码
// 使用SAC-IA进行点云配准pcl::SampleConsensusInitialAlignment<pcl::PointXYZ, pcl::PointXYZ, pcl::FPFHSignature33> sac_ia;sac_ia.setInputSource(source); // 设置源点云sac_ia.setSourceFeatures(source_fpfh); // 设置源点云的FPFH特征sac_ia.setInputTarget(target); // 设置目标点云sac_ia.setTargetFeatures(target_fpfh); // 设置目标点云的FPFH特征sac_ia.setMinSampleDistance(0.1); // 设置样本点之间的最小距离sac_ia.setCorrespondenceRandomness(6); // 设置邻居数量,用于随机特征对应选择pointcloud::Ptr aligned_cloud(new pointcloud); // 用于存储配准后的点云sac_ia.align(*aligned_cloud); // 执行配准
2.7 可视化
该函数可视化去噪前和去噪后的点云数据。
// 点云可视化函数
void visualizePointCloudsRegistration(pcl::PointCloud<pcl::PointXYZ>::Ptr source, pcl::PointCloud<pcl::PointXYZ>::Ptr target,pcl::PointCloud<pcl::PointXYZ>::Ptr icp_cloud)
{boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("Registration Result"));int v1 = 0, v2 = 1;viewer->createViewPort(0, 0, 0.5, 1, v1); // 创建左视口viewer->createViewPort(0.5, 0, 1, 1, v2); // 创建右视口viewer->setBackgroundColor(0, 0, 0, v1); // 设置背景颜色viewer->setBackgroundColor(0.05, 0, 0, v2);// 设置点云的颜色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> src_h(source, 255, 0, 0); // 源点云颜色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> tgt_h(target, 0, 0, 255); // 目标点云颜色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> transe(icp_cloud, 255, 0, 0); // 配准后的点云颜色viewer->addPointCloud(source, src_h, "source cloud", v1); // 添加源点云viewer->addPointCloud(target, tgt_h, "target cloud", v1); // 添加目标点云viewer->addPointCloud(target, tgt_h, "target cloud1", v2); // 目标点云viewer->addPointCloud(icp_cloud, transe, "pcs cloud", v2); // 添加配准后的点云// 启动可视化窗口while (!viewer->wasStopped()){viewer->spinOnce(100);boost::this_thread::sleep(boost::posix_time::microseconds(10000));}
}
三、完整代码
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/filters/voxel_grid.h> // 体素滤波器,用于点云下采样
#include <pcl/features/normal_3d_omp.h> // 使用多线程加速法向量估计
#include <pcl/features/fpfh_omp.h> // FPFH加速计算
#include <pcl/registration/ia_ransac.h> // SAC-IA初始配准算法
#include <boost/thread/thread.hpp>
#include <pcl/visualization/pcl_visualizer.h>using namespace std;typedef pcl::PointCloud<pcl::PointXYZ> pointcloud; // 点云类型
typedef pcl::PointCloud<pcl::Normal> pointnormal; // 法向量类型
typedef pcl::PointCloud<pcl::FPFHSignature33> fpfhFeature; // FPFH特征类型// 计算FPFH特征的函数
fpfhFeature::Ptr compute_fpfh_feature(pointcloud::Ptr input_cloud, pcl::search::KdTree<pcl::PointXYZ>::Ptr tree)
{// 1. 估计法向量pointnormal::Ptr normals(new pointnormal); // 用于存储计算得到的法向量pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> ne; // 使用OMP加速法向量计算ne.setInputCloud(input_cloud); // 设置输入点云ne.setNumberOfThreads(8); // 设置并行计算的线程数ne.setSearchMethod(tree); // 使用KD树加速搜索ne.setKSearch(10); // 设置K近邻点数量为10ne.compute(*normals); // 计算法向量// 2. 计算FPFH特征fpfhFeature::Ptr fpfh(new fpfhFeature); // 创建FPFH特征存储容器pcl::FPFHEstimationOMP<pcl::PointXYZ, pcl::Normal, pcl::FPFHSignature33> fpfh_estimator; // FPFH特征估计器fpfh_estimator.setNumberOfThreads(8); // 设置并行计算线程数fpfh_estimator.setInputCloud(input_cloud); // 设置输入点云fpfh_estimator.setInputNormals(normals); // 设置法向量fpfh_estimator.setSearchMethod(tree); // 使用KD树加速搜索fpfh_estimator.setKSearch(10); // 设置K近邻点数量为10fpfh_estimator.compute(*fpfh); // 计算FPFH特征return fpfh;
}// 点云下采样函数
pointcloud::Ptr downsample_cloud(pointcloud::Ptr input_cloud, float leaf_size)
{// 使用体素滤波器对点云进行下采样pcl::VoxelGrid<pcl::PointXYZ> voxel_grid;voxel_grid.setLeafSize(leaf_size, leaf_size, leaf_size); // 设置体素的大小pointcloud::Ptr filtered_cloud(new pointcloud); // 创建存储下采样后的点云voxel_grid.setInputCloud(input_cloud); // 设置输入点云voxel_grid.filter(*filtered_cloud); // 执行下采样return filtered_cloud; // 返回下采样后的点云
}// 去除点云中的NAN点的函数
void remove_nan_points(pointcloud::Ptr input_cloud)
{// 移除输入点云中的NAN点vector<int> indices;pcl::removeNaNFromPointCloud(*input_cloud, *input_cloud, indices); // 去除NAN并保存有效索引
}// 点云可视化函数
void visualizePointCloudsRegistration(pcl::PointCloud<pcl::PointXYZ>::Ptr source, pcl::PointCloud<pcl::PointXYZ>::Ptr target,pcl::PointCloud<pcl::PointXYZ>::Ptr icp_cloud)
{boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("Registration Result"));int v1 = 0, v2 = 1;viewer->createViewPort(0, 0, 0.5, 1, v1); // 创建左视口viewer->createViewPort(0.5, 0, 1, 1, v2); // 创建右视口viewer->setBackgroundColor(0, 0, 0, v1); // 设置背景颜色viewer->setBackgroundColor(0.05, 0, 0, v2);// 设置点云的颜色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> src_h(source, 255, 0, 0); // 源点云颜色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> tgt_h(target, 0, 0, 255); // 目标点云颜色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> transe(icp_cloud, 255, 0, 0); // 配准后的点云颜色viewer->addPointCloud(source, src_h, "source cloud", v1); // 添加源点云viewer->addPointCloud(target, tgt_h, "target cloud", v1); // 添加目标点云viewer->addPointCloud(target, tgt_h, "target cloud1", v2); // 目标点云viewer->addPointCloud(icp_cloud, transe, "pcs cloud", v2); // 添加配准后的点云// 启动可视化窗口while (!viewer->wasStopped()){viewer->spinOnce(100);boost::this_thread::sleep(boost::posix_time::microseconds(10000));}
}// 主函数
int main(int argc, char** argv)
{// 加载源点云数据pcl::PointCloud<pcl::PointXYZ>::Ptr source_cloud(new pcl::PointCloud<pcl::PointXYZ>);if (pcl::io::loadPCDFile<pcl::PointXYZ>("1.pcd", *source_cloud) < 0){PCL_ERROR("Couldn't read file \n");return -1;}// 加载目标点云数据pcl::PointCloud<pcl::PointXYZ>::Ptr target_cloud(new pcl::PointCloud<pcl::PointXYZ>);if (pcl::io::loadPCDFile<pcl::PointXYZ>("2.pcd", *target_cloud) < 0){PCL_ERROR("Couldn't read file \n");return -1;}// 去除源点云和目标点云中的NAN点remove_nan_points(source_cloud);remove_nan_points(target_cloud);// 对点云进行下采样pointcloud::Ptr source = downsample_cloud(source_cloud, 0.005f); // 源点云下采样pointcloud::Ptr target = downsample_cloud(target_cloud, 0.005f); // 目标点云下采样// 创建KD树用于加速搜索pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());// 计算源点云和目标点云的FPFH特征fpfhFeature::Ptr source_fpfh = compute_fpfh_feature(source, tree);fpfhFeature::Ptr target_fpfh = compute_fpfh_feature(target, tree);// 使用SAC-IA进行点云配准pcl::SampleConsensusInitialAlignment<pcl::PointXYZ, pcl::PointXYZ, pcl::FPFHSignature33> sac_ia;sac_ia.setInputSource(source); // 设置源点云sac_ia.setSourceFeatures(source_fpfh); // 设置源点云的FPFH特征sac_ia.setInputTarget(target); // 设置目标点云sac_ia.setTargetFeatures(target_fpfh); // 设置目标点云的FPFH特征sac_ia.setMinSampleDistance(0.1); // 设置样本点之间的最小距离sac_ia.setCorrespondenceRandomness(6); // 设置邻居数量,用于随机特征对应选择pointcloud::Ptr aligned_cloud(new pointcloud); // 用于存储配准后的点云sac_ia.align(*aligned_cloud); // 执行配准// 输出配准结果cout << "配准得分: " << sac_ia.getFitnessScore() << endl;cout << "变换矩阵:\n" << sac_ia.getFinalTransformation() << endl;// 可视化源点云、目标点云和配准后的点云visualizePointCloudsRegistration(source_cloud, target_cloud, aligned_cloud);return 0;
}
四、结果展示
从源点云中读取 40256 个点
从目标点云中读取 40097 个点ICP has converged, score is 1.09437e-05
变换矩阵:0.827048 0.0104631 -0.562035 0.0358102-0.0186147 0.999788 -0.00877939 5.45643e-050.561824 0.0177231 0.827067 0.03758170 0 0 1