点云处理【七】(点云配准)

news2025/1/12 12:00:51

点云处理

第一章 点云数据采集


1.点云配准

点云配准是将两个或多个点云数据集融合到一个统一的坐标系统中的过程。这通常是为了创建一个完整的模型或融合从不同视角采集的数据。
点云配准一般分为粗配准和精配准,粗配准指的是在两幅点云之间的变换完全未知的情况下进行较为粗糙的配准,目的主要是为精配准提供较好的变换初值;精配准则是给定一个初始变换,进一步优化得到更精确的变换。这里我们主要介绍精配准。

2.点云粗配准算法

2.1 基于特征匹配的配准算法

SAC-IA

2.2 基于穷举搜索的配准算法

4PCS:四点配准算法(4PCS)基于寻找四个点的一致集合,并尝试找到最佳的变换,使得这些点在源和目标点云中都是一致的。
该方法适用于重叠区域较小或者重叠区域发生较大变化场景点云配准,无需对输入数据进行预滤波和去噪,算法能够快速准确的完成点云配准
Sper4PCS

2.3 基于概率分布的配准算法

NDT

3.点云精配准算法

3.1 基于优化的配准方法

大部分基于优化的方法在于找对应点搜索和变换估计。对应点搜索是在另一个点云中找到每个点的匹配点。变换估计就是利用对应关系来估计变换矩阵。这两个阶段将进行迭代,以找到最佳的变换。
基于优化的配准方法大致可分为4种方法:基于ICP的变种方法、基于图优化的、基于GMM的和半定的配准方法

3.1.1 基于ICP的变种方法

3.1.1.1 ICP

迭代最近点(ICP)通过最小化平移矩阵t和旋转矩阵R,使两个点云重合度最高(每个点到点距离最短)。
ICP需要一个相对好的初始估计,否则容易陷入局部最小值。
open3d:

import open3d as o3d
import numpy as np
import copy
def draw_registration_result(source, target, transformation):
    """Visualize the registration result."""
    source_temp = copy.deepcopy(source)
    source_temp.transform(transformation)
    o3d.visualization.draw_geometries([source_temp, target])

# 1. 读取两个点云
source = o3d.io.read_point_cloud("peizhun/1697938934952.pcd")
target = o3d.io.read_point_cloud("peizhun/1697938935423.pcd")

# 2. 下采样点云 (可选)
source_down = source.voxel_down_sample(voxel_size=5)
target_down = target.voxel_down_sample(voxel_size=5)

# 3. 估计法线 (对于某些 ICP 变体可能需要)
# source_down.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=100, max_nn=30))
# target_down.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=100, max_nn=30))

# 4. 执行 ICP
init_guess = np.eye(4)  # 如果你有一个初步的变换猜测,可以替换这里
threshold = 50  # 两点之间的最大距离
reg_p2p = o3d.pipelines.registration.registration_icp(
    source_down, target_down, threshold, init_guess,
    o3d.pipelines.registration.TransformationEstimationPointToPoint())

# 5. 显示结果
print("Transformation is:")
print(reg_p2p.transformation)
draw_registration_result(source, target, reg_p2p.transformation)

请添加图片描述
pcl

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/registration/icp.h>
#include <pcl/visualization/pcl_visualizer.h>

int main(int argc, char** argv) {
    pcl::PointCloud<pcl::PointXYZ>::Ptr source(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::PointXYZ>::Ptr target(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::PointXYZ>::Ptr output(new pcl::PointCloud<pcl::PointXYZ>);

    // 读取点云数据
    if (pcl::io::loadPCDFile<pcl::PointXYZ>("../data/1695551473330.pcd", *source) == -1 || 
        pcl::io::loadPCDFile<pcl::PointXYZ>("../data/1695551473719.pcd", *target) == -1) {
          
        std::cout << "Failed to read the PCD files!" << std::endl;
        return -1;
    }

    // 创建 ICP 对象并设置参数
    pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> icp;
    icp.setInputSource(source);
    icp.setInputTarget(target);
    icp.setMaximumIterations(50);
    icp.setTransformationEpsilon(1e-8);
    icp.setMaxCorrespondenceDistance(50);  // 可以根据需要调整
    icp.align(*output);

    pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("ICP"));
    viewer->setBackgroundColor(0, 0, 0);
    viewer->addPointCloud<pcl::PointXYZ>(target, "target cloud");
    viewer->addPointCloud<pcl::PointXYZ>(output, "output cloud");
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "target cloud");
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "output cloud");
    viewer->addCoordinateSystem(1.0);
    viewer->initCameraParameters();
    viewer->spin();
    

    return 0;
}

请添加图片描述

ICP的改进算法:
(1)Point-to-Plane ICP:将icp中点到点的距离改为点到目标面的距离,这样就不容易陷入局部最优,但也增加了计算量。
(2)Plane-to-Plane ICP:将icp中点到点的距离改为面到面的距离,精度更高,但进一步增加了计算量。
(3)Generalized ICP (GICP):结合了点到点、点到面、面到面的距离,效果更好。
(4)Normal Iterative Closest Point (NICP):考虑了法向量、局部曲率信息,效果进一步提高。
mbicp

3.1.1.2 GICP

pcl和open3d都提供了gicp,可直接调用:

Open3d

import open3d as o3d
import numpy as np
import copy
import os
def delete_zero(pcd):
    # 将点云转为 numpy 数组
    points = np.asarray(pcd.points)

    # 找到非0的点
    non_zero_indices = np.where(np.any(points != 0, axis=1))[0]

    # 根据这些索引筛选点
    filtered_points = points[non_zero_indices]

    # 更新点云对象
    pcd.points = o3d.utility.Vector3dVector(filtered_points)
    return pcd

path = "peizhun"
paths = os.listdir(path)
target = o3d.io.read_point_cloud(os.path.join(path,paths[0]))
target = delete_zero(target)

for index in paths[:2]:

    source = o3d.io.read_point_cloud(os.path.join(path,index))
    print(source)
    print("---"+index)
    source = delete_zero(source)
    print(source)
    threshold = 100  # 距离阈值
    trans_init = np.asarray([[1.0, 0.0, 0.0, 0.0],
                             [0.0, 1.0, 0.0, 0.0],
                             [0.0, 0.0, 1.0, 0],
                             [0.0, 0.0, 0.0, 1.0]])  # 初始变换矩阵,一般由粗配准提供
    # -------------------------------------------------
    # 计算两个重要指标,fitness计算重叠区域(内点对应关系/目标点数)。越高越好。
    # inlier_rmse计算所有内在对应关系的均方根误差RMSE。越低越好。
    evaluation = o3d.pipelines.registration.evaluate_registration(source, target, threshold, trans_init)
    generalized_icp = o3d.pipelines.registration.registration_generalized_icp(
        source, target, threshold, trans_init,
        o3d.pipelines.registration.TransformationEstimationForGeneralizedICP(),
        o3d.pipelines.registration.ICPConvergenceCriteria(max_iteration=35))  # 设置最大迭代次数
    
    source.transform(generalized_icp.transformation)
    target = target+source
    # -----------------可视化配准结果--------------------
o3d.visualization.draw_geometries([target])


请添加图片描述

PCL

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/registration/gicp.h>
#include <pcl/visualization/pcl_visualizer.h>

int main()
{

    pcl::PointCloud<pcl::PointXYZ>::Ptr source(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::PointXYZ>::Ptr target(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::PointXYZ>::Ptr result(new pcl::PointCloud<pcl::PointXYZ>);

    // Load point clouds
    if (pcl::io::loadPCDFile<pcl::PointXYZ>("1697938951044.pcd", *source) == -1 || pcl::io::loadPCDFile<pcl::PointXYZ>("1697938962416.pcd", *target) == -1)
    {
        std::cerr << "Failed to load PCD files." << std::endl;
        return -1;
    }

    // Perform GICP alignment
    pcl::GeneralizedIterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> gicp;
    gicp.setInputSource(source);
    gicp.setInputTarget(target);
    gicp.align(*result);

    if (gicp.hasConverged())
    {
        std::cout << "GICP has converged. Score: " << gicp.getFitnessScore() << std::endl;
        std::cout << "Transformation matrix:" << std::endl;
        std::cout << gicp.getFinalTransformation() << std::endl;
    }
    else
    {
        std::cerr << "GICP did not converge." << std::endl;
        return -1;
    }

    // Visualization
    boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("GICP Viewer"));
    viewer->setBackgroundColor(0, 0, 0);
    viewer->addPointCloud<pcl::PointXYZ>(source, "source cloud");
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 0, 0, "source cloud");
    viewer->addPointCloud<pcl::PointXYZ>(result, "result cloud");
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0, 1, 0, "result cloud");
    viewer->initCameraParameters();
    viewer->spin();

    return 0;
}

请添加图片描述

NICP

const转换错误,去const

缺liblz4
https://github.com/yorsh87/nicp/issues/23

3.1.2 基于图优化的配准方法

将点云转化成图结构,像上文中的GICP就是Graph+ICP。

3.1.3 基于GMM的配准方法

也就是基于高斯混合概率模型的方法,如GICP、NDT、CPD等,上面的GICP就是GMM+ICP实现的。

3.1.3.1 NDT

正态分布变换 (NDT)通过将点云表示为一系列的正态分布来工作。然后,通过最大化两个点云之间的概率分布的重叠来寻找最佳的变换。

PCL

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/registration/ndt.h>
#include <pcl/visualization/pcl_visualizer.h>

int main(int argc, char** argv)
{
    // 加载点云文件
    pcl::PointCloud<pcl::PointXYZ>::Ptr target_cloud (new pcl::PointCloud<pcl::PointXYZ>);
    if (pcl::io::loadPCDFile<pcl::PointXYZ> ("1697938951044.pcd", *target_cloud) == -1)
    {
        PCL_ERROR ("Couldn't read target pcd file\n");
        return (-1);
    }

    pcl::PointCloud<pcl::PointXYZ>::Ptr input_cloud (new pcl::PointCloud<pcl::PointXYZ>);
    if (pcl::io::loadPCDFile<pcl::PointXYZ> ("1697938962416.pcd", *input_cloud) == -1)
    {
        PCL_ERROR ("Couldn't read input pcd file\n");
        return (-1);
    }

    // 设置NDT的参数
    pcl::NormalDistributionsTransform<pcl::PointXYZ, pcl::PointXYZ> ndt;

    ndt.setTransformationEpsilon(10);
    ndt.setStepSize(1);
    ndt.setResolution(100);
    ndt.setMaximumIterations(35);

    ndt.setInputSource(input_cloud);
    ndt.setInputTarget(target_cloud);

    // 配准
    pcl::PointCloud<pcl::PointXYZ>::Ptr output_cloud (new pcl::PointCloud<pcl::PointXYZ>);
    ndt.align(*output_cloud);

    std::cout << "Normal Distributions Transform has converged:" << ndt.hasConverged()
              << " score: " << ndt.getFitnessScore() << std::endl;

    // 可视化结果
    pcl::visualization::PCLVisualizer viewer("NDT");

    // 添加目标点云,设置为白色
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> target_color(target_cloud, 255, 255, 255);
    viewer.addPointCloud<pcl::PointXYZ>(target_cloud, target_color, "target_cloud");

    // 添加输入点云,设置为绿色
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> input_color(input_cloud, 0, 255, 0);
    viewer.addPointCloud<pcl::PointXYZ>(input_cloud, input_color, "input_cloud");

    // 添加输出点云,设置为红色
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> output_color(output_cloud, 255, 0, 0);
    viewer.addPointCloud<pcl::PointXYZ>(output_cloud, output_color, "output_cloud");

    viewer.spin();

    return 0;
}

3.1.4 基于半定规划的配准方法

半定规划在二次规划的基础上进一步扩展研究,半定规划是一类特殊的凸优化问题,属于非凸优化的一类问题。
如CPD、TEASER等算法。

3.2 特征学习的配准方法

这类方法使用深度学习作为特征提取工具提取有效特征点,然后通过简单的RANSAC方法,可以得到准确的配准结果。
如PPF等。

3.3 基于端到端学习的方法

端到端学习方法的基本思想是将配准问题转化为回归问题。

此外还有多视角配准、多源配准等。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JS加密/解密之那些不为人知的基础逻辑运算符

不多说&#xff0c;直接上干货 使用逻辑非运算符 ! 和双重逻辑非运算符 !!&#xff1a;例如 ![]、!![]、!0、!!0 和 !""、!!""。空字符串的转换&#xff1a;!"" 和 !!""。数组和对象的类型转换&#xff1a;[] []、[] - []、{} [] 和…

STM32中除零运算,为何程序不崩溃?

在 C 语言中&#xff0c;除零运算会导致异常吗&#xff1f; 在 C 语言中&#xff0c;当一个数除以零时&#xff0c;会导致除法运算错误&#xff0c;通常表现为“除以零”错误或被称为“浮点异常”&#xff08;floating-point exception&#xff09;。 对于整数除法&#xff0c…

YOLOv5源码中的参数超详细解析(3)— 训练部分(train.py)| 模型训练调参

前言:Hello大家好,我是小哥谈。YOLOv5项目代码中,train.py是用于模型训练的代码,是YOLOv5中最为核心的代码之一,而代码中的训练参数则是核心中的核心,只有学会了各种训练参数的真正含义,才能使用YOLOv5进行最基本的训练。🌈 前期回顾: YOLOv5源码中的参数超详细解析…

IC-705连接wfview

wfview是一款开源的主要针对ICOM的远程控制软件&#xff0c;可以通过USB或者无线控制电台&#xff0c;貌似还支持X6100。 IC-705支持WLAN功能&#xff0c;连接wfview非常方便。 IC-705的WLAN支持两种模式&#xff0c;一种是Station模式&#xff0c;可用于连接WI-FI路由器&#…

vue中使用xlsx插件导出多sheet excel实现方法

安装xlsx&#xff0c;一定要注意版本&#xff1a; npm i xlsx0.17.0 -S package.json&#xff1a; {"name": "hello-world","version": "0.1.0","private": true,"scripts": {"serve": "vue-c…

Fabric.js 图案笔刷

本文简介 带尬猴&#xff0c;我是德育处主任 Fabric.js 有图案画笔功能&#xff0c;这个功能可以简单理解成“刮刮卡”效果。 如果只是看 Fabric.js 文档可能还不太明白 图案画笔 PatternBrush 是如何使用。 本文将讲解如何配置这款画笔的基础属性。 图案画笔&#xff08;笔…

UG\NX二次开发 连接曲线、连结曲线 UF_CURVE_auto_join_curves

文章作者:里海 来源网站:王牌飞行员_里海_里海NX二次开发3000例,里海BlockUI专栏,C\C++-CSDN博客 简介 UG\NX二次开发 连接曲线、连结曲线 UF_CURVE_auto_join_curves 效果 代码 #include "me.hpp" extern DllExport void ufusr(char* param, int* returnC…

TypeScript之索引签名

1. 索引签名 在 TypeScript 中&#xff0c;索引签名是一种定义对象类型的方式&#xff0c;它允许我们使用字符串或数字作为索引来访问对象的属性。 索引签名最主要的作用就是允许我们动态地添加或访问对象的属性&#xff0c;通过使用索引签名&#xff0c;我们可以在编译时无法…

2023年CSP-S赛后总结(2023CSP-S题解+游记)

目录 T1 题目描述 输入格式 输出格式 代码 T2 题目描述 输入格式 输出格式 题目描述 输入格式 输出格式 题意翻译 代码 T3 题目背景 题目描述 输入格式 输出格式 代码 T4 题目描述 输入格式 输出格式 代码 总结 游记 DAY1 DAY0 DAY-1 T1 题目描述…

P-MOS管开关机控制电路(手动按键控制和自动采样信号触发控制)

1. 手动(按键)控制 这种控制适合与消费电子&#xff0c;家庭消费电子领域&#xff0c;用户人为地手动按动机械按键控制P-MOS管导通与断开。例如&#xff1a;电动牙刷、儿童玩具等等&#xff0c;很多都会用到一个按钮控制产品的开关机&#xff0c;调档等等。 1.1 RH6030_JX触摸…

029.Python面向对象_类补充_属性(方法)相关

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…

Qt之菜单栏、工具栏、状态栏介绍及工具栏QAction的动态增删显示实现方式

目的 端应用程序或者编辑器基本都支持工具栏快捷功能的动态增删&#xff0c;即通过在菜单栏上打钩就可以在工具栏上看到相应功能的快捷按钮&#xff0c;取消打钩则在工具栏上就移除了该功能的快捷按钮。那么Qt如何实现这个功能&#xff0c;本篇目的就是记录实现此功能的方法及思…

cmd 命令关闭占用端口

工作中还是偶尔会遇到端口号被占用的情况&#xff0c;之前也有写过另一种关闭方式&#xff0c;但是发现没有命令方便&#xff0c;所以记录一下。 1、 查看 8081 端口占用的 pid 。 命令&#xff1a;netstat -ano |findstr 8081 由上图可知&#xff0c;占用 8081 端口的进程 id…

折叠式菜单怎么做编程,初学编程系统化教程初级1上线

中文编程系统化教程&#xff0c;不需英语基础&#xff0c;学习链接——入门篇课程 https://edu.csdn.net/course/detail/39036中文编程系统化教程&#xff0c;不需英语基础&#xff0c;学习链接—— 初级1课程 https://edu.csdn.net/course/detail/39061 ——————————…

C语言输出以下图案

图案&#xff1a; * * * * * * * * * * * * * * * * * * * * * * * * * 完整代码&#xff1a; /* 输出以下图案 * * * * * * * * * * * * * * * * * * * * * * * * * */ #include<stdio.h>int main(){//图案的行数int n5;for (int i 0; i < n; i){//每一行开头空格…

通过profibus PA转Modbus rtu协议网关把RTU数据传到pa设备上

远创智控PA转modbus rtu协议网关YC-PA-485&#xff0c;解决您的协议转换难题。 这款PA转Modbus RTU协议网关&#xff0c;将Profibus PA和Modbus RTU专用通讯协议进行桥接&#xff0c;为您的数据传输提供稳定、高效的解决方案。它符合Modbus通讯协议&#xff0c;允许Modbus设备…

基于Java的学生在线课程学习系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09; 代码参考数据库参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…

架构设计基础

一、架构图 二、系统背景 业务发展迅速&#xff0c;积累了大量的数据资产&#xff0c;大数据团队承担数据串联、沉淀&#xff0c;反哺和赋能业务的重要职责。大数据团队协同各部门打造了玩个大狗魔方、大狗宝盒数据产品&#xff0c;提供data-center应用发力数据中台&#xff0…

leetcode做题笔记203. 移除链表元素

给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5]示例 2&#xff1a; 输入&#xff1a…

redis archive github

https://github.com/redis/redis/releases/tag/7.2.2https://github.com/redis/redis/releases/tag/7.2.2