PCL 点云配准 KD-ICP算法(精配准)
目录
一、概述
1.1原理
1.2实现步骤
1.3应用场景
二、代码实现
2.1关键函数
2.1.1 加载点云函数
2.1.2 构建KD树函数
2.1.3 KD-ICP配准函数
2.1.4 点云可视化函数
2.2完整代码
三、实现效果
PCL点云算法汇总及实战案例汇总的目录地址链接:
PCL点云算法与项目实战案例汇总(长期更新)
一、概述
KD-ICP(基于KD树的ICP)算法 是 ICP(Iterative Closest Point)算法 的一种改进形式,主要通过 KD树(K-Dimensional Tree) 加速最近邻搜索,显著提高了ICP算法的配准效率。KD树的使用使得ICP在处理大规模点云数据时具备更高的性能,因为KD树能够在多维空间中快速找到最近邻点。相比于传统ICP,KD-ICP更适用于实时3D点云处理以及大型点云数据的配准。
1.1原理
ICP算法通过迭代最近邻点配对来计算两个点云之间的刚体变换。KD-ICP使用KD树加速最近邻搜索,主要流程如下:
- 最近邻搜索:使用KD树结构快速查找源点云和目标点云中的最近邻点对。
- 刚体变换计算:通过最小化源点云和目标点云最近邻点之间的误差,计算出最优的刚体变换矩阵。
- 应用刚体变换:将该变换应用于源点云并更新其位置。
- 终止条件:当收敛条件满足时,停止迭代。
1.2实现步骤
- 加载源点云和目标点云。
- 构建KD树:为源点云和目标点云构建KD树结构,加速最近邻搜索。
- 初始化ICP算法:设置ICP的最大迭代次数、距离阈值、转换误差等参数。
- 执行KD-ICP配准:通过KD树进行最近邻搜索,计算刚体变换,并更新源点云的位置。
- 可视化:展示源点云、目标点云及配准后的源点云
1.3应用场景
- 3D物体扫描与拼接:在3D扫描重建过程中,将多个不同角度获取的点云通过KD-ICP配准拼接成一个完整模型。
- 机器人视觉:机器人视觉中通过KD-ICP对环境点云数据进行对齐,实现导航和物体定位。
- 自动驾驶:在自动驾驶中,KD-ICP可用于车辆环境感知的多传感器数据融合,例如激光雷达点云数据的实时配准。
二、代码实现
2.1关键函数
2.1.1 加载点云函数
该函数用于从PCD文件中加载点云数据,源点云和目标点云都会通过此函数读取。
pcl::PointCloud<pcl::PointXYZ>::Ptr loadPointCloud(const std::string& filename) {pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());if (pcl::io::loadPCDFile<pcl::PointXYZ>(filename, *cloud) == -1) {PCL_ERROR("无法读取点云文件 %s\n", filename.c_str());return nullptr;}std::cout << "从文件 " << filename << " 读取点云,包含 " << cloud->size() << " 个点\n";return cloud;
}
2.1.2 构建KD树函数
KD树加速最近邻搜索,分别为源点云和目标点云构建KD树
pcl::search::KdTree<pcl::PointXYZ>::Ptr buildKDTree(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud) {pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());tree->setInputCloud(cloud);return tree;
}
2.1.3 KD-ICP配准函数
该函数用于执行基于KD树的ICP算法,实现精确的点云配准。
Eigen::Matrix4f performKDICP(pcl::PointCloud<pcl::PointXYZ>::Ptr source, pcl::PointCloud<pcl::PointXYZ>::Ptr target, pcl::search::KdTree<pcl::PointXYZ>::Ptr tree1, pcl::search::KdTree<pcl::PointXYZ>::Ptr tree2) {pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> icp;icp.setSearchMethodSource(tree1);icp.setSearchMethodTarget(tree2);icp.setInputSource(source);icp.setInputTarget(target);icp.setMaxCorrespondenceDistance(1); // 设置对应点之间的最大距离icp.setMaximumIterations(35); // 设置最大迭代次数icp.setTransformationEpsilon(1e-10); // 设置收敛条件下的最小变换差异icp.setEuclideanFitnessEpsilon(0.05); // 设置收敛的均方误差阈值pcl::PointCloud<pcl::PointXYZ>::Ptr icp_cloud(new pcl::PointCloud<pcl::PointXYZ>);icp.align(*icp_cloud);if (icp.hasConverged()) {std::cout << "ICP 收敛,得分为 " << icp.getFitnessScore() << std::endl;std::cout << "变换矩阵:\n" << icp.getFinalTransformation() << std::endl;} else {std::cout << "ICP 未能收敛\n";}return icp.getFinalTransformation();
}
2.1.4 点云可视化函数
该函数用于可视化配准前后的点云,配准后的点云显示为绿色,目标点云显示为红色。
void visualizePointClouds(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("KD-ICP 配准结果"));viewer->setBackgroundColor(0, 0, 0);pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> target_color(target, 255, 0, 0);viewer->addPointCloud(target, target_color, "target cloud");pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> icp_color(icp_cloud, 0, 255, 0);viewer->addPointCloud(icp_cloud, icp_color, "icp cloud");viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "target cloud");viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "icp cloud");while (!viewer->wasStopped()) {viewer->spinOnce(100);boost::this_thread::sleep(boost::posix_time::microseconds(100000));}
}
2.2完整代码
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/registration/icp.h> // 引入ICP配准算法
#include <pcl/search/kdtree.h> // 引入KD树加速最近邻搜索
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
#include <pcl/console/time.h> // 用于计算配准时间// 加载点云数据函数
// 该函数用于从PCD文件中加载点云数据,如果文件加载失败会返回nullptr
pcl::PointCloud<pcl::PointXYZ>::Ptr loadPointCloud(const std::string& filename) {pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());if (pcl::io::loadPCDFile<pcl::PointXYZ>(filename, *cloud) == -1) {PCL_ERROR("无法读取点云文件 %s\n", filename.c_str());return nullptr;}std::cout << "从文件 " << filename << " 读取点云,包含 " << cloud->size() << " 个点\n";return cloud;
}// 构建KD树函数
// 为了加速最近邻搜索,该函数为输入的点云构建一个KD树
pcl::search::KdTree<pcl::PointXYZ>::Ptr buildKDTree(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud) {pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());tree->setInputCloud(cloud);return tree;
}// KD-ICP配准函数
// 该函数执行基于KD树的ICP配准,通过设置ICP参数和构建的KD树,进行点云配准并返回最终的变换矩阵
Eigen::Matrix4f performKDICP(pcl::PointCloud<pcl::PointXYZ>::Ptr source, pcl::PointCloud<pcl::PointXYZ>::Ptr target, pcl::search::KdTree<pcl::PointXYZ>::Ptr tree1, pcl::search::KdTree<pcl::PointXYZ>::Ptr tree2) {pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> icp;// 设置KD树用于加速最近邻搜索icp.setSearchMethodSource(tree1);icp.setSearchMethodTarget(tree2);// 设置ICP输入点云,源点云与目标点云icp.setInputSource(source);icp.setInputTarget(target);// 设置ICP的参数icp.setMaxCorrespondenceDistance(1); // 设置对应点对之间的最大距离icp.setMaximumIterations(35); // 设置最大迭代次数icp.setTransformationEpsilon(1e-10); // 为终止条件设置最小转换差异icp.setEuclideanFitnessEpsilon(0.05); // 设置收敛条件:当均方误差和小于该阈值时停止迭代// 存储配准结果的点云pcl::PointCloud<pcl::PointXYZ>::Ptr icp_cloud(new pcl::PointCloud<pcl::PointXYZ>);// 执行ICP配准icp.align(*icp_cloud);// 判断ICP是否收敛并输出结果if (icp.hasConverged()) {std::cout << "ICP 收敛,得分为 " << icp.getFitnessScore() << std::endl;std::cout << "变换矩阵:\n" << icp.getFinalTransformation() << std::endl;} else {std::cout << "ICP 未能收敛\n";}// 返回最终的刚体变换矩阵return icp.getFinalTransformation();
}// 点云可视化函数
// 该函数用于可视化原始点云(源点云与目标点云)及配准后的点云
void visualizePointClouds(pcl::PointCloud<pcl::PointXYZ>::Ptr source, pcl::PointCloud<pcl::PointXYZ>::Ptr target,pcl::PointCloud<pcl::PointXYZ>::Ptr icp_cloud) {// 创建PCL可视化对象boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("KD-ICP 配准结果"));// 设置背景颜色为黑色viewer->setBackgroundColor(0, 0, 0);// 将目标点云上色为红色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> target_color(target, 255, 0, 0);viewer->addPointCloud(target, target_color, "target cloud");// 将配准后的源点云上色为绿色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> icp_color(icp_cloud, 0, 255, 0);viewer->addPointCloud(icp_cloud, icp_color, "icp cloud");// 设置点云的显示属性(点大小为1)viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "target cloud");viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "icp cloud");// 运行可视化窗口,直到关闭窗口while (!viewer->wasStopped()) {viewer->spinOnce(100);boost::this_thread::sleep(boost::posix_time::microseconds(100000));}
}int main(int argc, char** argv) {pcl::console::TicToc time; // 用于计算执行时间// 加载源点云和目标点云pcl::PointCloud<pcl::PointXYZ>::Ptr source = loadPointCloud("1.pcd");pcl::PointCloud<pcl::PointXYZ>::Ptr target = loadPointCloud("2.pcd");// 构建KD树pcl::search::KdTree<pcl::PointXYZ>::Ptr tree1 = buildKDTree(source);pcl::search::KdTree<pcl::PointXYZ>::Ptr tree2 = buildKDTree(target);// 使用KD树加速的ICP配准time.tic(); // 开始计时Eigen::Matrix4f final_transform = performKDICP(source, target, tree1, tree2);std::cout << "配准时间: " << time.toc() << " ms" << std::endl; // 输出配准时间// 配准后将源点云进行变换pcl::PointCloud<pcl::PointXYZ>::Ptr icp_cloud(new pcl::PointCloud<pcl::PointXYZ>());pcl::transformPointCloud(*source, *icp_cloud, final_transform);// 可视化原始点云与配准后的点云visualizePointClouds(source, target, icp_cloud);return 0;
}
三、实现效果
ICP 收敛,得分为 8.32129e-06
变换矩阵:0.914549 -0.38993 0.107491 0.0238710.345635 0.89144 0.293038 -0.0615766-0.210087 -0.230845 0.950039 0.05358960 0 0 1
相关文章:

PCL 点云配准 KD-ICP算法(精配准)
目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 加载点云函数 2.1.2 构建KD树函数 2.1.3 KD-ICP配准函数 2.1.4 点云可视化函数 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接: PCL点云算法…...

uniapp打包安卓apk步骤
然后安装在手机上就可以啦...

Springboot 整合 Java DL4J 实现安防监控系统
🧑 博主简介:历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程,…...

【数据结构与算法】第1课—算法复杂度
文章目录 1. 数据结构2. 算法3. 算法效率4. 算法复杂度5. 算法时间复杂度5.1 大O的渐进表示法5.2 时间复杂度示例 6. 空间复杂度6.1 练习16.2 练习26.3 练习3 1. 数据结构 数据结构是计算机存储、组织数据的方式,指相互之间存在一种和多种特定关系的数据元素的集合&…...
利用高德API获取整个城市的公交路线并可视化(五)
如果说我比别人看得更远些,那是因为我站在了巨人的肩上。——牛顿 参考:使用高德API获取公交线路数据,无需代码_实时公交api-CSDN博客 记录于2024年10月,因数据获取受网站更新策略等影响可能会失效,故记录写作时间,同时拾人牙慧,优化了后半部分数据直接导出为csv和shp…...
DNS:互联网域名系统的核心
什么是 DNS? DNS(Domain Name System,域名系统)是互联网的一项基础服务,它负责将人类容易记忆的域名(如 www.example.com)转换成计算机可以识别的 IP 地址(如 192.0.2.1)…...

小猿口算炸鱼脚本
目录 写在前面: 一、关于小猿口算: 二、代码逻辑 1.数字识别 2.答题部分 三、代码分享: 补充:软件包下载 写在前面: 最近小猿口算已经被不少大学生攻占,小学生直呼有挂。原本是以为大学生都打着本…...

浅谈云原生--微服务、CICD、Serverless、服务网格
往期推荐 浅学React和JSX-CSDN博客 一文搞懂大数据流式计算引擎Flink【万字详解,史上最全】-CSDN博客 一文入门大数据准流式计算引擎Spark【万字详解,全网最新】_大数据 spark-CSDN博客 目录 1. 云原生概念和特点 2. 常见云模式 3. 云对外提供服务的…...

android app执行shell命令视频课程补充android 10/11适配-千里马android
(https://blog.csdn.net/learnframework/article/details/120103471) https://blog.csdn.net/learnframework/article/details/120103471 hi,有学员在学习跨进程通信专题课程时候,在实战app执行一个shell命令的项目时候,对课程本身的android …...

C++笔记-UTF8和UTF8-dom的区别
在文件格式上,UTF-8 和 UTF-8-BOM 是两种不同的编码方式,其中 UTF-8-BOM 包含字节顺序标记(BOM),而 UTF-8 则不包含。 UTF-8: UTF-8 是一种以字节为单位的可变长度字符编码,常用于以字节为单位…...

“探索Adobe Photoshop 2024:订阅方案、成本效益分析及在线替代品“
设计师们对Adobe Photoshop这款业界领先的图像编辑软件肯定不会陌生。如果你正考虑加入Photoshop的用户行列,可能会对其价格感到好奇。Photoshop的价值在于其强大的功能,而它的价格也反映了这一点。下面,我们就来详细了解一下Adobe Photoshop…...

网页复制粘贴助手,Chrome网页复制插件(谷歌浏览器复制插件)
一款解决网页限制复制问题的插件,当你遇到限制复制粘贴和右键的网页是不是很头痛?安装这个插件后,点下插件按钮就能解决了 碰到这种情况 也是非常头疼 chrome拓展-chrome插件-强制复制 当我们浏览网页的时候,看到感兴趣的内容就…...
【C++刷题】力扣-#118-杨辉三角
题目描述 给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。在杨辉三角中,每个数是它正上方两个数的和。 示例 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]示例 2: 输入: numRows 1 输出: [[1]]题解 这个问题…...

Linux下的环境变量
目录 1.引言 1.1bash的部分工作 1.2main函数也有参数 1.3我们可以通过给main函数传入不同的参数,让同一份代码实现不同的功能 1.4先认识一个环境变量PATH,帮助Linux找到指令程序的地址 2.环境变量 2.1环境变量的概念 2.2见见其他的环境变量 2…...

Edge论文的创新点
创新点及其来源 1. 从灰度边缘重建RGB图像的方法(EdgRec) 基于的方法:传统的重建方法,如使用自动编码器或生成模型来重建正常样本的图像,并通过对原始图像和重建图像的比较来检测异常。 重建过程: 训练阶…...

ComfyUI 高级实战:实现华为手机的AI消除功能
大家好,我是每天分享AI应用的萤火君! 不知道大家是否还记得华为 Pura 70的「AI消除」事件,当时使用 华为Pura 70 系列手机的智能消除功能时,该功能可以被用来消除照片中女性胸口处的衣物,这一功能曾引发广泛的关注和伦…...

我记得我曾喜欢过冬天
写在前面 1316 字 | 感触 | 世界 | 情感 | 体验 | 经历 | 想法 | 认知 正文 晚上出门,起电单车,很冷。冻得有些发抖。下车,我第一时间和珍发了消息。 我说,居然在四川感受到了哈尔滨的温度。 哈尔滨的夏天很热,但哈尔…...

最新夜间数据集发布LoLI-Street: 33000帧数据,涵盖19000个目标
最新夜间数据集发布LoLI-Street: 33000帧数据,涵盖19000个目标 Abstract 低光照图像增强(LLIE)对于许多计算机视觉任务至关重要,包括目标检测、跟踪、分割和场景理解。尽管已有大量研究致力于提高在低光照条件下捕捉的低质量图像…...

反向传播算法与随机搜索算法的比较
反向传播算法与随机搜索算法的比较 在这篇文章中,我们将通过一个简单的线性回归问题来比较反向传播算法和随机搜索算法的性能。我们将使用Python代码来实现这两种算法,并可视化它们的梯度下降过程。 反向传播算法 反向传播算法是深度学习和神经网络训…...

【PDF文件】默认被某种软件打开,如何进行修改?
当有时下载某种软件后,电脑中的PDF文件就默认由该种软件打开,每次需要右键选择打开方式才能选择需要的其他软件打开。如下图所示。 修改方法: (1)点击电脑的“设置”,选择应用 (2)…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...

华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...