PCL 点云配准 LM-ICP算法(精配准)
目录
一、概述
1.1原理
1.2实现步骤
1.3应用场景
二、代码实现
2.1关键函数
2.1.1 法线计算函数
2.1.2 执行 LM-ICP 函数
2.2完整代码
三、实现效果
PCL点云算法汇总及实战案例汇总的目录地址链接:
PCL点云算法与项目实战案例汇总(长期更新)
一、概述
LM-ICP(Levenberg-Marquardt Iterative Closest Point)算法是一种基于 Levenberg-Marquardt 非线性优化的 ICP 点云配准算法。相较于传统的 ICP 算法,LM-ICP 在迭代优化过程中引入了更复杂的误差修正机制,能够更有效地处理带有噪声、局部不规则的点云数据,实现更加精确的点云配准。
LM-ICP 通过最小化源点云与目标点云之间的几何误差(例如点到点或点到面的误差)来计算刚体变换矩阵。Levenberg-Marquardt 是一种结合了梯度下降和高斯牛顿法的非线性优化算法,能够在梯度下降较慢时切换到更为快速的优化路径。
1.1原理
Levenberg-Marquardt (LM) 结合了两种优化方法:
- 梯度下降法:在远离最优解时,能够通过学习率较小的更新,慢速地逼近最优解。
- 高斯牛顿法:在接近最优解时,能通过求解二阶导数信息,快速逼近最优解。
LM-ICP 算法的目标是通过这些优化过程找到源点云和目标点云之间的最佳变换矩阵,使得几何误差最小化。其基本步骤包括:
- 计算初始对应关系。
- 使用 Levenberg-Marquardt 优化步骤调整变换矩阵。
- 迭代更新直到收敛。
1.2实现步骤
- 加载点云数据:加载源点云和目标点云。
- 法线计算:计算点云的法线信息,以支持点到面的 ICP 配准。
- 初始化 ICP 对象:设置基于 LM 优化的 ICP 算法。
- 设置配准参数:包括最大迭代次数、最小转换差异、最大对应点距离等。
- 执行配准:通过 ICP 进行点云配准,输出最终的变换矩阵。
- 可视化:通过 PCLVisualizer 对源点云、目标点云和配准后的点云进行可视化。
1.3应用场景
- 3D物体建模:通过对多个视角点云数据的精确对齐,重建完整的 3D 模型。
- 自动驾驶感知系统:对多帧激光雷达点云数据的精确对齐,提升环境感知的精度。
- 机器人导航:在 SLAM(同时定位与地图构建)系统中,使用点云配准进行位置估计。
二、代码实现
2.1关键函数
2.1.1 法线计算函数
pcl::PointCloud<pcl::PointNormal>::Ptr compute_normals(pcl::PointCloud<pcl::PointXYZ>::Ptr input_cloud)
{pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> normal_estimator;pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);normal_estimator.setNumberOfThreads(8); // 使用多线程加速法线计算normal_estimator.setInputCloud(input_cloud); // 设置输入点云normal_estimator.setSearchMethod(tree); // 设置 KD 树搜索normal_estimator.setKSearch(10); // 设置 K 近邻搜索normal_estimator.compute(*normals); // 计算法线// 拼接点云数据与法线pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointNormal>);pcl::concatenateFields(*input_cloud, *normals, *cloud_with_normals);return cloud_with_normals;
}
2.1.2 执行 LM-ICP 函数
void run_lm_icp(pcl::PointCloud<pcl::PointNormal>::Ptr& source_normal,pcl::PointCloud<pcl::PointNormal>::Ptr& target_normal,Eigen::Matrix4f& final_transform, pcl::PointCloud<pcl::PointNormal>::Ptr& icp_cloud)
{// 初始化 LM ICP 对象pcl::IterativeClosestPointNonLinear<pcl::PointNormal, pcl::PointNormal> lm_icp;// 设置 ICP 参数lm_icp.setInputSource(source_normal);lm_icp.setInputTarget(target_normal);lm_icp.setTransformationEpsilon(1e-10); // 设置最小转换差异lm_icp.setMaxCorrespondenceDistance(10); // 设置最大对应点距离lm_icp.setEuclideanFitnessEpsilon(0.001); // 设置均方误差收敛条件lm_icp.setMaximumIterations(50); // 设置最大迭代次数// 执行 LM-ICP 配准lm_icp.align(*icp_cloud);final_transform = lm_icp.getFinalTransformation();std::cout << "LM-ICP 配准完成,最终分数: " << lm_icp.getFitnessScore() << std::endl;
}
2.2完整代码
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/features/normal_3d_omp.h> // 使用OMP加速法向量计算
#include <pcl/registration/icp_nl.h> // 非线性ICP算法
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
#include <pcl/console/time.h> // 控制台时间计时器using namespace std;// 计算法线
pcl::PointCloud<pcl::PointNormal>::Ptr compute_normals(pcl::PointCloud<pcl::PointXYZ>::Ptr input_cloud)
{pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> normal_estimator;pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);normal_estimator.setNumberOfThreads(8); // 使用多线程加速法线计算normal_estimator.setInputCloud(input_cloud); // 设置输入点云normal_estimator.setSearchMethod(tree); // 设置 KD 树搜索normal_estimator.setKSearch(10); // 设置 K 近邻搜索normal_estimator.compute(*normals); // 计算法线// 拼接点云数据与法线pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointNormal>);pcl::concatenateFields(*input_cloud, *normals, *cloud_with_normals);return cloud_with_normals;
}// 执行 LM-ICP 配准
void run_lm_icp(pcl::PointCloud<pcl::PointNormal>::Ptr& source_normal,pcl::PointCloud<pcl::PointNormal>::Ptr& target_normal,Eigen::Matrix4f& final_transform, pcl::PointCloud<pcl::PointNormal>::Ptr& icp_cloud)
{// 初始化 LM ICP 对象pcl::IterativeClosestPointNonLinear<pcl::PointNormal, pcl::PointNormal> lm_icp;// 设置 ICP 参数lm_icp.setInputSource(source_normal);lm_icp.setInputTarget(target_normal);lm_icp.setTransformationEpsilon(1e-10); // 设置最小转换差异lm_icp.setMaxCorrespondenceDistance(10); // 设置最大对应点距离lm_icp.setEuclideanFitnessEpsilon(0.001); // 设置均方误差收敛条件lm_icp.setMaximumIterations(50); // 设置最大迭代次数// 执行 LM-ICP 配准lm_icp.align(*icp_cloud);final_transform = lm_icp.getFinalTransformation();std::cout << "LM-ICP 配准完成,最终分数: " << lm_icp.getFitnessScore() << std::endl;
}// 可视化配准结果
void visualize_registration(pcl::PointCloud<pcl::PointXYZ>::Ptr& source,pcl::PointCloud<pcl::PointXYZ>::Ptr& target,pcl::PointCloud<pcl::PointXYZ>::Ptr& aligned)
{boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("配准结果"));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> source_color(source, 0, 255, 0);viewer->addPointCloud(source, source_color, "source cloud");*/pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> aligned_color(aligned, 0, 0, 255);viewer->addPointCloud(aligned, aligned_color, "aligned cloud");viewer->spin();
}int main()
{pcl::console::TicToc time;// 读取点云数据pcl::PointCloud<pcl::PointXYZ>::Ptr target(new pcl::PointCloud<pcl::PointXYZ>);pcl::io::loadPCDFile<pcl::PointXYZ>("1.pcd", *target);pcl::PointCloud<pcl::PointXYZ>::Ptr source(new pcl::PointCloud<pcl::PointXYZ>);pcl::io::loadPCDFile<pcl::PointXYZ>("2.pcd", *source);// 计算源点云和目标点云的法线pcl::PointCloud<pcl::PointNormal>::Ptr targetNormal = compute_normals(target);pcl::PointCloud<pcl::PointNormal>::Ptr sourceNormal = compute_normals(source);// 执行 LM-ICPpcl::PointCloud<pcl::PointNormal>::Ptr icp_cloud(new pcl::PointCloud<pcl::PointNormal>);Eigen::Matrix4f final_transform;run_lm_icp(sourceNormal, targetNormal, final_transform, icp_cloud);// 输出变换矩阵std::cout << "变换矩阵:\n" << final_transform << std::endl;// 变换点云并进行可视化pcl::PointCloud<pcl::PointXYZ>::Ptr aligned(new pcl::PointCloud<pcl::PointXYZ>);pcl::transformPointCloud(*source, *aligned, final_transform);visualize_registration(source, target, aligned);return 0;
}
三、实现效果
LM-ICP 配准完成,最终分数: 3.21931e-06
变换矩阵:0.918782 0.336633 -0.206198 0.0118966-0.379389 0.897328 -0.225536 0.07359350.109105 0.285448 0.952164 -0.03338070 0 0 1

相关文章:
PCL 点云配准 LM-ICP算法(精配准)
目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 法线计算函数 2.1.2 执行 LM-ICP 函数 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接: PCL点云算法与项目实战案例汇总(长期更新&a…...
Mac 编译 Unreal 源码版本
在Mac上编译Unreal Engine源码需要遵循以下步骤: 安装必要的依赖项: Xcode Python(建议使用2.7版本) Java(使用JDK 8) CMake Ninja SVN(用于获取某些依赖项) 获取Unreal Engi…...
开源vGPU方案 HAMi实现细粒度GPU切分——筑梦之路
前言 为什么需要 GPU 共享、切分等方案? 在使用GPU的过程中我们会发现,直接在裸机环境使用,都可以多个进程共享 GPU,怎么到 k8s 环境就不行了? 1. 资源感知 在 k8s 中资源是和节点绑定的,对于 GPU 资源…...
性能测试工具JMeter
本次使用的博客系统的url: http://8.137.19.140:9090/blog_edit.html 1. JMeter介绍 环境要求:要求java,jdk版本大于8; Apache JMeter 是 Apache 组织基于 Java 开发的压⼒测试⼯具,⽤于对软件做性能测试;…...
Kubernetes ETCD的恢复与备份
在 Kubernetes 中,ETCD 扮演着至关重要的角色: 1. 集群状态存储 2. 服务发现 3. 配置管理 4. 分布式锁和协调 5. 故障恢复 ETCD 存储了 Kubernetes 集群中所有的状态信息,包括节点、Pod、Service、ConfigMap、Secrets 等。ETCD 支持服务发现…...
笔记整理—linux网络部分(2)Linux网络框架
前文说过,在OSI中将网络分为7层,这是理论上将其分为7层,但实际上可以将其分为4层。如TCP协议就是将其分为4层。理论只是提出一种指导意见,但不是行业范本。 驱动层只关系有没有接到包,不关心包经过多少次转发ÿ…...
深度学习500问——Chapter17:模型压缩及移动端部署(5)
文章目录 17.9.5 ShuffleNet- v1 17.9.6 ShuffleNet- v2 17.10 现有移动端开源框架及其特点 17.10.1 NCNN 17.10.2 QNNPACK 17.9.5 ShuffleNet- v1 ShuffleNet 是Face团队提出的,晚于MobileNet两个月在arXiv上公开《ShuffleNet: An Extremely Efficient…...
分布式ID多种生成方式
分布式ID 雪花算法(时间戳41机器编号10自增序列号10) 作用:希望ID按照时间进行有序生成 原理: 即一台带有编号的服务器在毫秒级时间戳内生成带有自增序号的ID,这个ID保证了自增性和唯一性 雪花算法根据结构的生成ID个数的上线时…...
时间序列预测(六)——循环神经网络(RNN)
目录 一、RNN的基本原理 1、正向传播(Forward Pass): 2、计算损失(Loss Calculation) 3、反向传播——反向传播通过时间(Backpropagation Through Time,BPTT) 4、梯度更新&…...
Day2算法
Day2算法 1.算法的基本概念 算法: 对特定问题求解步骤的一种描述,他叔指令的有限序列,其中的每条指令表示一个或多个操作。 算法的特性: 1.有穷性: 一个算法必须总在执行有穷步之后结束,且每一步都可…...
智洋创新嵌入式面试题汇总及参考答案
堆和栈有什么区别 内存分配方式 栈由编译器自动分配和释放,函数执行时,函数内局部变量等会在栈上分配空间,函数执行结束后自动回收。例如在一个简单的函数int add(int a, int b)中,参数a和b以及函数内部的一些临时变量都会在栈上分配空间,函数调用结束后这些空间就会被释放…...
无线网卡知识的学习-- wireless基础知识(nl80211)
1. 基本概念 mac80211 :这是最底层的模块,与hardware offloading 关联最多。 mac80211 的工作是给出硬件的所有功能与硬件进行交互。(Kernel态) cfg80211:是设备和用户之间的桥梁,cfg80211的工作则是观察跟踪wlan设备的实际状态. (Kernel态) nl80211: 介于用户空间与内核…...
除了 Python,还有哪些语言适合做爬虫?
以下几种语言也适合做爬虫: 一、Java* 优势: 强大的性能和稳定性:Java 运行在 Java 虚拟机(JVM)上,具有良好的跨平台性和出色的内存管理机制,能够处理大规模的并发请求和数据抓取任务&#x…...
JS | JS中类的 prototype 属性和__proto__属性
大多数浏览器的 ES5 实现之中,每一个对象都有__proto__属性,指向对应的构造函数的prototype属性。Class 作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。 构造函数的子类有prototype属性。 …...
15分钟学Go 第3天:编写第一个Go程序
第3天:编写第一个Go程序 1. 引言 在学习Go语言的过程中,第一个程序通常是“Hello, World!”。这个经典的程序不仅教会你如何编写代码,还引导你理解Go语言的基本语法和结构。本节将详细介绍如何编写、运行并理解第一个Go程序,通过…...
简单的常见 http 响应状态码
简单的常见 http 响应状态码 HTTP状态码(HTTP Status Code)是用以表示网页服务器超文本传输协议响应状态的3位数字代码。它由 RFC 2616 规范定义,所有状态码的第一个数字代表了响应的五种状态之一。 1. 大体分类 状态码类别解释1xx信息性响…...
2024年【安全员-C证】复审考试及安全员-C证模拟考试题
安全员-C证考试是针对生产经营单位的安全生产管理人员进行的职业资格认证考试。考试内容涵盖安全生产法律法规、安全管理知识、安全技术措施等多个方面。通过考试,可以检验考生对安全生产知识的掌握程度,提高安全管理水平,确保生产安全。 二…...
RT-Thread之STM32使用定时器实现输入捕获
前言 基于RT-Thread的STM32开发,配置使用定时器实现输入捕获。 比如配置特定通道捕获上升沿,该通道对应的引脚有上升沿信号输入,则触发捕获中断。 一、新建工程 二、工程配置 1、打开CubeMX 进行工程配置 2、时钟使用外部高速晶振 3、配置…...
数字图像处理:图像分割应用
数字图像处理:图像分割应用 图像分割是图像处理中的一个关键步骤,其目的是将图像分成具有不同特征的区域,以便进一步的分析和处理。 1.1 阈值分割法 阈值分割法(Thresholding)是一种基于图像灰度级或颜色的分割方法&…...
Java面试宝典-并发编程学习02
目录 21、并行与并发有什么区别? 22、多线程中的上下文切换指的是什么? 23、Java 中用到的线程调度算法是什么? 24、Java中线程调度器和时间分片指的是什么? 25、什么是原子操作?Java中有哪些原子类? 26、w…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
