PCL 点云配准 基于目标对称的ICP算法(精配准)

目录

一、概述

1.1原理

1.2实现步骤

1.3应用场景

二、代码实现

2.1关键函数

2.1.1计算点云的法线

2.1.2基于对称误差估计的ICP配准

2.1.3可视化

2.2完整代码

三、实现效果


PCL点云算法汇总及实战案例汇总的目录地址链接:

PCL点云算法与项目实战案例汇总(长期更新)


一、概述

        基于目标对称的ICP算法(Symmetric ICP)是一种改进的迭代最近点算法(Iterative Closest Point, ICP)。在传统ICP中,配准过程依赖于最小化源点云和目标点云之间的欧氏距离。然而,传统ICP对有对称性特征的场景配准时容易陷入局部最优解,无法充分利用目标的对称性特征进行精确配准。

1.1原理

        基于目标对称的ICP算法通过对称点到平面的误差估计方法(Symmetric Point-to-Plane)优化变换矩阵的估计。该算法同时最小化源点到目标点云表面的距离以及目标点云到源点云表面的距离,从而对对称性场景有更好的适应性。它能够有效地处理含有对称结构的点云,提升配准的精度和稳定性。

1.2实现步骤

  1. 加载源点云和目标点云:读取待配准的源点云和目标点云数据。
  2. 计算法线信息:为源点云和目标点云计算法线,并将点云与法线信息合并。
  3. 基于对称误差估计的ICP配准:利用对称的点到平面距离误差估计,进行点云配准。
  4. 结果输出与可视化:输出配准后的变换矩阵,并可视化源点云、目标点云和配准后的结果点云。

1.3应用场景

  1. 对称物体的3D点云配准,如汽车、飞机等结构体的点云匹配。
  2. 自动驾驶、机器人等场景中,基于对称目标进行定位与姿态估计。
  3. 在含有对称结构的复杂场景中,提高点云配准的精度与效率。

二、代码实现

2.1关键函数

2.1.1计算点云的法线

// 计算点云的法线并与点云数据拼接,生成带法线的点云
void cloud_with_normal(pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, 
                       pcl::PointCloud<pcl::PointNormal>::Ptr& cloud_normals) 
{
    // 使用OMP加速法线计算
    pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> ne;
    pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);

    // 设置KD树搜索方法
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
    ne.setNumberOfThreads(8);  // 设置使用的线程数
    ne.setInputCloud(cloud);   // 输入点云
    ne.setSearchMethod(tree);  // KD树搜索
    ne.setKSearch(10);         // 设置K近邻点个数
    ne.compute(*normals);      // 计算法线

    // 拼接点云数据和法线信息,生成带法线的点云
    pcl::concatenateFields(*cloud, *normals, *cloud_normals);
}

2.1.2基于对称误差估计的ICP配准

// 执行基于对称点到平面的ICP配准
void perform_symmetric_icp(pcl::PointCloud<pcl::PointNormal>::Ptr& source_with_normals,
    pcl::PointCloud<pcl::PointNormal>::Ptr& target_with_normals,
    pcl::PointCloud<pcl::PointNormal>::Ptr& aligned_cloud,
    Eigen::Matrix4f& final_transformation)
{
    // 创建对称点到平面ICP对象
    pcl::IterativeClosestPoint<pcl::PointNormal, pcl::PointNormal> symm_icp;

    // 使用对称点到平面的变换估计方法
    pcl::registration::TransformationEstimationSymmetricPointToPlaneLLS<pcl::PointNormal, pcl::PointNormal>::Ptr symm_point_to_plane
    (new pcl::registration::TransformationEstimationSymmetricPointToPlaneLLS<pcl::PointNormal, pcl::PointNormal>);

    // 设置ICP算法参数
    symm_icp.setTransformationEstimation(symm_point_to_plane);  // 设置对称点到平面距离
    symm_icp.setInputSource(source_with_normals);               // 设置源点云
    symm_icp.setInputTarget(target_with_normals);               // 设置目标点云
    symm_icp.setMaxCorrespondenceDistance(10);                  // 设置最大对应点对之间的距离
    symm_icp.setTransformationEpsilon(1e-10);                   // 设置终止条件:最小转换差异
    symm_icp.setEuclideanFitnessEpsilon(0.001);                 // 设置收敛条件:均方误差
    symm_icp.setMaximumIterations(50);                          // 设置最大迭代次数

    // 执行配准
    symm_icp.align(*aligned_cloud);

    // 输出配准结果
    if (symm_icp.hasConverged()) {
        std::cout << "Symmetric ICP has converged, score is " << symm_icp.getFitnessScore() << std::endl;
        final_transformation = symm_icp.getFinalTransformation();
        std::cout << "变换矩阵:\n" << final_transformation << std::endl;
    }
    else {
        PCL_ERROR("Symmetric ICP未收敛。\n");
        exit(-1);
    }
}

2.1.3可视化

// 可视化配准结果
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();
}

2.2完整代码

#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/registration/icp.h> // icp算法
#include <pcl/features/normal_3d_omp.h> // 法线计算头文件
#include <pcl/visualization/pcl_visualizer.h> // 可视化

using namespace std;

// 计算点云法线并生成带法线的点云
void cloud_with_normal(pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud,
    pcl::PointCloud<pcl::PointNormal>::Ptr& cloud_normals)
{
    // 使用OMP加速法线计算
    pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> ne;
    pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);

    // KD树用于近邻搜索
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);

    ne.setNumberOfThreads(8);   // 使用多线程加速法线计算
    ne.setInputCloud(cloud);    // 输入点云
    ne.setSearchMethod(tree);   // KD树搜索
    ne.setKSearch(10);          // 设置K近邻点的个数
    ne.compute(*normals);       // 计算法线

    // 合并点云与法线信息,生成带法线的点云
    pcl::concatenateFields(*cloud, *normals, *cloud_normals);
}

// 执行基于对称点到平面的ICP配准
void perform_symmetric_icp(pcl::PointCloud<pcl::PointNormal>::Ptr& source_with_normals,
    pcl::PointCloud<pcl::PointNormal>::Ptr& target_with_normals,
    pcl::PointCloud<pcl::PointNormal>::Ptr& aligned_cloud,
    Eigen::Matrix4f& final_transformation)
{
    // 创建对称点到平面ICP对象
    pcl::IterativeClosestPoint<pcl::PointNormal, pcl::PointNormal> symm_icp;

    // 使用对称点到平面的变换估计方法
    pcl::registration::TransformationEstimationSymmetricPointToPlaneLLS<pcl::PointNormal, pcl::PointNormal>::Ptr symm_point_to_plane
    (new pcl::registration::TransformationEstimationSymmetricPointToPlaneLLS<pcl::PointNormal, pcl::PointNormal>);

    // 设置ICP算法参数
    symm_icp.setTransformationEstimation(symm_point_to_plane);  // 设置对称点到平面距离
    symm_icp.setInputSource(source_with_normals);               // 设置源点云
    symm_icp.setInputTarget(target_with_normals);               // 设置目标点云
    symm_icp.setMaxCorrespondenceDistance(10);                  // 设置最大对应点对之间的距离
    symm_icp.setTransformationEpsilon(1e-10);                   // 设置终止条件:最小转换差异
    symm_icp.setEuclideanFitnessEpsilon(0.001);                 // 设置收敛条件:均方误差
    symm_icp.setMaximumIterations(50);                          // 设置最大迭代次数

    // 执行配准
    symm_icp.align(*aligned_cloud);

    // 输出配准结果
    if (symm_icp.hasConverged()) {
        std::cout << "Symmetric ICP has converged, score is " << symm_icp.getFitnessScore() << std::endl;
        final_transformation = symm_icp.getFinalTransformation();
        std::cout << "变换矩阵:\n" << final_transformation << std::endl;
    }
    else {
        PCL_ERROR("Symmetric ICP未收敛。\n");
        exit(-1);
    }
}

// 可视化配准结果
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::PointCloud<pcl::PointXYZ>::Ptr source(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::io::loadPCDFile<pcl::PointXYZ>("1.pcd", *source);
    cout << "从源点云中读取 " << source->size() << " 个点" << endl;

    // -------------------加载目标点云----------------------
    pcl::PointCloud<pcl::PointXYZ>::Ptr target(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::io::loadPCDFile<pcl::PointXYZ>("2.pcd", *target);
    cout << "从目标点云中读取 " << target->size() << " 个点" << endl;

    // 计算源点云的法线
    pcl::PointCloud<pcl::PointNormal>::Ptr source_with_normals(new pcl::PointCloud<pcl::PointNormal>);
    cloud_with_normal(source, source_with_normals);

    // 计算目标点云的法线
    pcl::PointCloud<pcl::PointNormal>::Ptr target_with_normals(new pcl::PointCloud<pcl::PointNormal>);
    cloud_with_normal(target, target_with_normals);

    // 创建对齐后的点云
    pcl::PointCloud<pcl::PointNormal>::Ptr aligned_cloud(new pcl::PointCloud<pcl::PointNormal>);
    Eigen::Matrix4f final_transformation = Eigen::Matrix4f::Identity();

    // 执行对称ICP配准
    perform_symmetric_icp(source_with_normals, target_with_normals, aligned_cloud, final_transformation);

    // 使用创建的变换对输入点云进行变换
    pcl::PointCloud<pcl::PointXYZ>::Ptr final_cloud(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::transformPointCloud(*source, *final_cloud, final_transformation);

    // 可视化配准结果
    visualize_registration(source, target, final_cloud);

    return 0;
}

三、实现效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/895248.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【火山引擎】调用火山大模型的方法 | SDK安装 | 配置 | 客户端初始化 | 设置

豆包 (Doubao) 是字节跳动研发的大规模预训练语言模型。 目录 1 安装 2 配置访问凭证 3 客户端初始化 4 设置地域和访问域名 5 设置超时/重试次数 1 安装 通过pip安装PYTHON SDK。 pip install volcengine-python-sdk[ark] 2 配置访问凭证 获取 API Key 访问凭证具体步…

理工科考研想考计算机,湖南大学、重大、哈工大威海、山东大学,该如何选择?

C哥专业提供——计软考研院校选择分析专业课备考指南规划 计算机对理工科同学来说&#xff0c;还是性价比很高的&#xff0c;具有很大的优势&#xff01; 一、就业前景广阔 高需求行业 在当今数字化时代&#xff0c;计算机技术几乎渗透到了各个领域&#xff0c;无论是互联网…

YOLO11 目标检测 | 自动标注 | 预标注 | 标签格式转换 | 手动校正标签

本文分享使用YOLO11进行目标检测时&#xff0c;实现模型推理预标注、自动标注、标签格式转换、以及使用Labelme手动校正标签等功能。 目录 1、预训练权重 2、生成预标注 3、分析YOLO11的目标检测格式 4、分析Labelme标注工具的格式 5、生成json标注文件 6、手动校正标签 …

SQL Server-导入和导出excel数据-注意事项

环境&#xff1a; win10&#xff0c;SQL Server 2008 R2 之前写过的放在这里&#xff1a; SqlServer_陆沙的博客-CSDN博客 https://blog.csdn.net/pxy7896/category_12704205.html 最近重启ASP.NET项目&#xff0c;在使用sql server导出和导入数据时遇到一些问题&#xff0c;特…

Qt键盘按下事件和定时器事件及事件的接收和忽略

定时器事件 //设置多少毫秒调用一次 1s1000timerId this->startTimer(1000);timerId2 this->startTimer(500);void MyWidget::timerEvent(QTimerEvent* t) {static int sec 0;//通过判断当前ID来实现不同定时器的调用时间if(t->timerId() this->timerId){//隔一…

AtCoder Beginner Contest 375 A-E 题解

我的老师让我先做最后再交&#xff0c;看正确率&#xff08;即以OI赛制打abc&#xff09; 所以我用的小号&#xff08;… …&#xff09; C 卡了老半天才出来&#xff0c;我把题读错了 难度&#xff1a; A. Seats 题意 给你一个字符串 S S S&#xff0c;仅包含 . 和 #&…

kubernets(二)

集群操作 查看集群信息 kubectl get查看各组件信息 格式&#xff1a;kubectl get 资源类型 【资源名】 【选项】 events #查看集群中的所有日志信息 -o wide # 显示资源详细信息&#xff0c;包括节点、地址... -o yaml/json #将当前资源对象输出至 yaml/json 格式文…

2024.10.20 进制转换 删除根节点为x的整个子树

进制转换 十进制转换为任意进制 #include <stdio.h> int main(){char res [32] {0};int num;int index;scanf("%d %d",&num,&index);char table[] "0123456789ABCDEF";int i 0;if(num0) res[0] 0;else if(num!0){while(num>0){res[i…

Java重修笔记 UDP 网络通信

UDP 网络通信原理 1. 类 DatagramSocket 和 DatagramPacket [数据包/数据报] 实现了基于 UDP协议网络程序。 2. UDP 数据报通过数据报套接字 DatagramSocket 发送和接收&#xff0c;系统不保证UDP数据报一定能够安全送到目的地&#xff0c;也不能确定什么时候可以抵达&#…

【机器学习】决策树算法

目录 一、决策树算法的基本原理 二、决策树算法的关键概念 三、决策树算法的应用场景 四、决策树算法的优化策略 五、代码实现 代码解释&#xff1a; 在机器学习领域&#xff0c;决策树算法是一种简单直观且易于理解的分类和回归方法。它通过学习数据特征和决策规则&#…

电力系统IEC-101报文主要常用详解

文章目录 1️⃣ IEC-1011.1 前言1.2 101规约简述1.3 固定帧格式1.4 可变帧格式1.5 ASDU1.5.1 常见类型标识1.5.2 常见结构限定词1.5.3 常见传送原因1.5.4 信息体地址 1.6 常用功能报文1.6.1 初始化链路报文1.6.2 总召报文1.6.3 复位进程1.8.4 对时1.8.4.1时钟读取1.8.4.2时钟写…

R语言医学数据分析实践-R编程环境的搭建

【图书推荐】《R语言医学数据分析实践》-CSDN博客 《R语言医学数据分析实践 李丹 宋立桓 蔡伟祺 清华大学出版社9787302673484》【摘要 书评 试读】- 京东图书 (jd.com) R语言编程_夏天又到了的博客-CSDN博客 R语言对编程环境的要求不高&#xff0c;可以在多种操作系统平台上…

数据结构——顺序表的基本操作

前言 介绍 &#x1f343;数据结构专区&#xff1a;数据结构 参考 该部分知识参考于《数据结构&#xff08;C语言版 第2版&#xff09;》24~28页 补充 此处的顺序表创建是课本中采用了定义方法为SqList Q来创建&#xff0c;并没有使用顺序表指针的方法&#xff0c;具体两个…

【Linux系列】查询nginx相关的进程

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

推荐IDE中实用AI编程插件,目前无限次使用

插件介绍 一款字节跳动推出的“基于豆包大模型的智能开发工具” 以vscode介绍【pycharm等都可以啊】&#xff0c;这个插件提供智能补全、智能预测、智能问答等能力&#xff0c;节省开发时间 直接在IDE中使用&#xff0c;就不用在网页中来回切换了 感觉还可以&#xff0c;响应速…

欧盟 RED 网络安全法规 EN 18031

目录 1. &#x1f4c2; EN 18031 1.1 背景 1.2 专业术语 1.3 覆盖产品范围 1.4 EN 18031标准主要评估内容&#xff1a; 1.5 EN 18031标准主要评估项目&#xff1a; 1.6 EN 18031 与 ETSI EN 303 645 的主要差异 1.7 RED 网络安全法规解读研讨会 2. &#x1f531; EN 1…

LabVIEW示波器通信及应用

基于LabVIEW平台开发的罗德与施瓦茨示波器通信与应用系统实现了示波器的远程控制及波形数据的实时分析&#xff0c;通过TCP/IP或USB接口与计算机通信&#xff0c;利用VISA技术进行指令传输&#xff0c;从而实现高效的数据采集与处理功能。 项目背景 随着现代电子测试需求的日益…

【解决Docker无剩余存储磁盘空间问题】

【解决Docker无剩余存储磁盘空间问题】 目录 【解决Docker无剩余存储磁盘空间问题】一、问题概述二、问题原因三、解决方案1、方案一&#xff1a;清除Docker磁盘空间2、方案二&#xff1a;更换Docker磁盘存储目录 一、问题概述 执行Docker build -t [镜像名] [源目录] 命令报错…

2.1 HTML5 - Canvas标签

文章目录 引言Canvas标签概述定义实例&#xff1a;创建画布 理解Canvas坐标系概述实例&#xff1a;获取Canvas坐标 获取Canvas环境上下文概述实例&#xff1a;获取Canvas上下文设置渐变色效果 结语 引言 大家好&#xff0c;今天我们要一起探索HTML5中一个非常有趣且强大的特性…

如何将本地 Node.js 服务部署到宝塔面板:完整的部署指南

文章简介&#xff1a; 将本地开发的 Node.js 项目部署到线上服务器是开发者常见的工作流程之一。在这篇文章中&#xff0c;我将详细介绍如何将本地的 Node.js 服务通过宝塔面板&#xff08;BT 面板&#xff09;上线。宝塔面板是一个强大的服务器管理工具&#xff0c;具有简洁的…