自适应点云配准(RANSAC、ICP)

news2025/8/12 3:56:29

点云配准

实验目标

  • 任务一:将两个形状、大小相同的点云进行配准,进而估计两个点云之间的位姿。
  • 任务二:将一些列深度图反向投影得到点云,经过配准后,得到每个深度图之间的位姿变换,并将相应的点云融合到一起。

编译运行环境

项目 Python3 编写,实现 RANSAC 配准和 ICP 配准,用到 open3dnumpy 库。运行 python main.py src.ply tgt.ply save_path.ply ,读取源点云 src.ply,目标点云 tgt.ply,保存路径 save_path.ply。会显示三张图,分别为原始点云(源点云用红色标注,目标点云用绿色标注)、粗配准结果、精配准结果。

算法设计

任务一和任务二的主要思路一致,都是先对点云进行降采样、滤波后计算点云特征信息(包括法向量、特征直方图、以及后续特征匹配需要的若干特征),再利用提取的特征使用 RANSAC 的方法进行粗配准,最后使用 ICP 的方法进行精配准。

特征提取

【PCL学习笔记】之快速点特征直方图FPFH - pcl::FPFHSignature33 - Eba_

使用 FPFH(Fast Point Feature Histogram)算法。PFH(Point Feature Histogram)算法提取没给点邻域内两两点对间几何特征(一些角度信息),绘制直方图。FPFH 保留了 PFH 的大部分信息,但忽略了相邻点之间的计算,而是按照距离的反比将一个邻域内的点的直方图加权得到 33 维向量,效率更高。尝试了使用 PCA 进行降维以提高后续特征匹配速度,发现作用不大,遂舍弃。

粗配准

使用 RANSAC 的方法,总而言之就是对于源点云中的三个点,去“蒙”他们和目标点云中的那几个点相对应,然后计算变换矩阵,检验其优劣。简要流程如下:

  1. 在源点云中随机选择 3 个点。
  2. 分别查找他们在目标点云中特征向量最接近的点。这里的“接近”是欧式距离意义下的,查询数据结构使用 KD 树。
  3. 计算两个三角形之间的变换矩阵。其实就是按照 ICP 精配准的过程,求最小二乘法意义下的最优变换矩阵。
  4. 求出源点云在此变换之后的点云,计算其与目标点云的重合度(定义为源点云中阈值半径内存在目标点的点的个数)。
  5. 回到1,重复若干次,维护并返回重合度最高的变换。

实际编码中,由于 4 中 KD 树查询速度比较慢(33 维空间中接近于 O ( n ) O(n) O(n)),因提高在 1、2 中选择三角形对的质量是一个好的做法。这里三角形对的质量一是指他们匹配的可能性,二是通过他们计算出的变换矩阵是否准确。可以想到的方法有很多,比如三角形的边不能太短、两个三角形的对应边长不能差太多、优先选择特征向量远离均值的点等等。我的实现中采用了前两种。此外,在降采样后的点云上配准也可以提高效率。

精配准

【点云精配准】Iterative Closest Point(ICP)- Forrest Ding

具体推导过程就不赘述了。

深度图融合

上面的算法可以较好地完成对形状、大小相同的点云配准。但是,如果直接应用在深度图上,会出现匹配不上的问题(下面为 single-depth 匹配结果的俯视图):

在这里插入图片描述

这是因为 ICP 为每个源点云中的点,都从目标点云中找到一个距离最近的点和它匹配。但在深度图中,两个点云存在很多点本来就是不匹配的。如果强行令他们匹配,那么在最小二乘法的意义下最优解就是上图这样,一个点云被移动到了另一个的“中央”位置。

改进方法就是,对于源点云中的一个点,如果在目标点云中不存在和它的距离小于某个阈值的点,那么在此次迭代中就不考虑该点。同时,这个阈值随着迭代的进行不断减小的(有下限),这样就使得配准越来越精细。

关键代码

RANSAC

实现中还涉及两个细节。

一是匹配阈值设为多大合适?由于不同的数据具有不同的尺寸,因此本算法实现了自适应的过程。我们想要得到点云(在某个表面上的)平均距离。考虑体素下采样 oldPcd.voxel_down_sample(voxel_size),采样到一定程度后,点云密集处的平均距离就是 voxel_size。因此将 RANSAC 的匹配阈值设置为 2.5 * voxel_size

二是如何判断两个三角形的差距?代码中如果对应边长度之差大于 lengthThreshold * 长度平均值。

这样,50 次有效采样就能计算出很好的转移矩阵。从下面的效果可以看出,即使是粗配准的效果就很好了。

def RANSAC():
    maxCount = 0
    jisuan=0
    j = 0
    print("RANSACing...")
    while True:
        j += 1
        // 随机选取三个点
        srcCorr = random.sample(range(srcNum), 3)
        // 如果某两个点距离太近,舍弃
        if not notTooClose3([srcPoints[x] for x in srcCorr]):
            continue
            
        // KD 树特征匹配
        tgtCorr = []
        for id in srcCorr:
            k, idx, dis2 = tgtFpfhKDT.search_knn_vector_xd(srcFpfh[id], knn=1)
            tgtCorr.append(random.choice(idx[0]))
            
        // 如果两个三角形差距太大,也舍弃
        if True in [farAway(srcPoints[i[0]] - srcPoints[j[0]], 
                            tgtPoints[i[1]] - tgtPoints[j[1]]) 
                    for i in zip(srcCorr, tgtCorr) 
                    for j in zip(srcCorr, tgtCorr)]:
            continue
        jisuan += 1
        
        // 得到变换矩阵
        R, T = calculateTrans(np.array([srcPoints[i] for i in srcCorr]), 
                              np.array([tgtPoints[i] for i in tgtCorr]))
        A = np.transpose((R @ srcPoints.T) + np.tile(T, (1, srcNum)))
        
        //计算匹配数
        count = 0
        for point in range(0, srcNum, 1):
            k, idx, dis2 = tgtKDT.search_hybrid_vector_3d(A[point], 
                                                          radius=fitThreshold, max_nn=1)
            count += k
        
        // 更新最大匹配
        if count > maxCount:
            maxCount = count
            bestR, bestT = R, T
        if jisuan > 50 and j > 1000:
            break
        
    print("RANSAC calculated %d times, maximum matches: %d" % (jisuan, maxCount))
    return bestR, bestT

ICP

def ICP(src, tgt):
    limit = fitThreshold
    retR = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
    retT = np.array([[0], [0], [0]])
    trace = []
    for _ in range(400):
        tgtCorr = []
        srcCorr = []
        // 筛选距离近的点对
        for point in src:
            k, idx, dis2 = tgtKDT.search_knn_vector_3d(point, knn=1)
            if dis2[0] < (limit)**2:
                srcCorr.append(point)
                tgtCorr.append(tgt[idx[0]])
        trace.append([limit, len(srcCorr)])
        R, T = calculateTrans(np.array(srcCorr), np.array(tgtCorr))
        retR = R @ retR
        retT = R @ retT + T
        src = np.transpose((R @ src.T) + np.tile(T, (1, srcNum)))
        // 阈值的衰减
        limit = (limit - fitThreshold/1.5) * 0.95 + fitThreshold/1.5
        // 自适应过程,如果匹配点对不变化说明算法收敛,退出迭代
        if len(trace) > 50 and len(set([x[1] for x in trace[-20:]])) == 1:	
            break
    print("ICP trace is:", trace)
    return retR, retT

效果

在所有的测试数据中,算法都能够取得令人满意的结果,这里选择 airplane、person 以及 single-depth 三组数据进行展示。另外,如果直接对原始数据进行配准,会导致源点云和目标点云完全重合,只会显示单一的颜色,不利于观察效果。因此算法对每组数据都做了适量的下采样之后进行配准,因此能够看出是两个点云匹配的结果。

airplane:

配准前:
在这里插入图片描述

粗配准:

在这里插入图片描述

精配准:

在这里插入图片描述

person:

配准前:

在这里插入图片描述

粗配准:

在这里插入图片描述

精配准:

在这里插入图片描述

single-depth:

配准前(降采样后):

在这里插入图片描述

粗配准(降采样后):

精配准:

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

【云原生】Docker镜像的创建,Dockerfile

内容预知 1.Dokcer镜像的创建 1.1 基于现有镜像创建 1.2 基于本地模板创建 1.3 基于Dockerfile 创建 联合文件系统(UnionFS ) 镜像加载原理 容器中操作系统容量小的原因 Docker镜像结构的分层 Dockefile的引入 2. Dockerfile 操作命令的指令 2.1 FROM 镜像 2.2 MA…

股票接口实时交易数据怎么查询?

股票数据接口作为软件应用而言&#xff0c;很多资源和数据是由自身提供的&#xff0c;像其他一些功能还是需要调用第三方提供的服务&#xff0c;这其中就涉及到股票数据接口api的调用功能&#xff0c;通过api来获取实时交易数据查询更方便&#xff0c;因此&#xff0c;如果交易…

【学习笔记17】JavaScript作用域

笔记首发 一、作用域 &#x1f634;我们学变量&#xff0c;不是在任何地方都可以使用&#x1f634;变量有一个使用区间, 这个可以使用的区间就叫做作用域 1、全局作用域 &#x1f62b; script标签内部声明的变量&#xff0c;就是全局作用域的变量&#x1f60f;在全局作用域声明…

计算机网络的定义和分类

计算机网络早期定义&#xff1a;自治互联的计算机集合 计算机网络系统的基本组成为&#xff1a;通信子网资源子网 计算机网络分类&#xff1a; 公用网通常是由电信公司出资建造的大型网络。 专用网通常是由某个部门为满足本单位特殊业务的需要建造的网络&#xff…

基于springboot+jpa+camunda实现简单的请假审批流程

整个camunda的绘图、具体使用&#xff0c;参照上一篇文章Camunda工作流引擎简记。 源码地址&#xff0c;相关技术储备如下 springboot–v2.5.4jpa–v2.5.4postgresql–v11camunda–v7.16.0 整个过程不需要建表&#xff0c;这就是JPA面向对象编程的好处&#xff0c;这也是我认…

33 - C++中的字符串类

---- 整理自狄泰软件唐佐林老师课程 1. 历史遗留问题 C语言 不支持真正意义上的字符串 C语言用 字符数组 和 一组函数 实现字符串操作 C语言 不支持自定义类型&#xff0c;因此无法获得字符串类型 解决方案 从C到C的进化过程 引入了 自定义类型在C中可以通过类完成字符串类型…

iOS上架app store详细教材

1、安装iOS上架辅助软件Appuploader 2、申请iOS发布证书&#xff08;p12&#xff09; 3、申请iOS发布描述文件&#xff08;mobileprovision&#xff09; 4、打包ipa 5、上传ipa到iTunes Conn 1、安装iOS上架辅助软件Appuploader2、申请iOS发布证书&#xff08;p12&#xff09;…

ImmunoChemistry艾美捷牛膜联蛋白V-荧光素凋亡检测试剂盒方案

ImmunoChemistry艾美捷牛膜联蛋白V-荧光素细胞凋亡检测试剂盒提供了一种经验证的方法&#xff0c;可使用重组荧光素结合的牛膜连蛋白V和碘化丙啶&#xff08;PI&#xff09;快速、轻松地区分两个死亡细胞群和活细胞群。这些细胞将被膜联蛋白V-荧光素&#xff08;绿色荧光&#…

docker容器持久化

第六章容器持久化 容器数据卷 什么是容器数据卷&#xff1f; 容器之间可以有一个数据共享技术&#xff01;docker容器中产生的数据&#xff0c;同步到本地&#xff01; 这就是卷技术&#xff01;目录的挂载&#xff0c;将我们容器内的目录&#xff0c;挂在到linux上面&#x…

线性时间选择(Top K)问题(Java)

线性时间选择&#xff08;Top K&#xff09;问题&#xff08;Java&#xff09; 文章目录线性时间选择&#xff08;Top K&#xff09;问题&#xff08;Java&#xff09;1、前置介绍2、分治法求解3、代码实现4、复杂度分析5、扩展6、参考资料1、前置介绍 定义 选择问题&#xff0…

docker可视化与制作和提交自己的镜像

第四章docker可视化 portainer docker的图形化管理工具&#xff01;提供一个后台面板供我们操作&#xff01; docker run -d -p 8080:9000 \--restartalways -v /var/run/docker.sock:/var/run/docker.sock --privilegedtrue portainer/portainer 访问测试&#xff1a;外网…

Python_数据容器_元组tuple

一、元组tuple定义 为什么需要元组 列表是可以修改的&#xff0c;如果想要传递的信息不被篡改&#xff0c;列表就不适合了 元组和列表一样&#xff0c;都是可以封装多个不同类型的元素在内 最大的不同点在于&#xff1a; 元祖一旦定义完成&#xff0c;就不可修改 所以&am…

SpringBoot中使用JDBC

JDBC基础使用&#xff1a; JDBC基础使用_做测试的喵酱的博客-CSDN博客 一、SpringBoot使用JDBC&#xff0c;依赖 依赖&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId&…

linux zookeeper kafka_2.12-2.2.0 集群

文章目录一、zookeeper服务搭建1. 下载2. 解压3. 创建目录4. 调整配置5. 配置myid6. 开放防火墙7.启动验证zk二、kafka集群搭建2.1. 下载软件2.2. 解压2.3. 配置2.5. 启动kafka三、测试验证3.1. 创建一个主题3.2. 发送消息3.3. 消费消息预先准备&#xff1a;上传软件至服务器19…

使用Avalonia跨Linux平台

Avalonia&#xff0c;读&#xff1a;阿瓦隆尼亚 这里的跨平台指的是&#xff0c;使用c#语言开发跨Linux平台。c#在Windows桌面开发中很强&#xff0c;但是在Linux桌面中&#xff0c;不能跨平台&#xff0c;Qt可以在Linux中跨平台&#xff0c;但是那是另外一门语言了。Avalonia…

计算机组成原理4小时速成:计算机运算方法,无符号数和有符号数,定点数和浮点数,移位运算,加减运算,乘法运算,原码,反码,补码

计算机组成原理4小时速成&#xff1a;计算机运算方法&#xff0c;无符号数和有符号数&#xff0c;定点数和浮点数&#xff0c;移位运算&#xff0c;加减运算&#xff0c;乘法运算&#xff0c;原码&#xff0c;反码&#xff0c;补码 2022找工作是学历、能力和运气的超强结合体&…

[激光原理与应用-16]:《激光原理与技术》-2- 光的本质(粒子、波动说、电磁波、量子)

目录 前言&#xff1a;“光” 一、光的微粒说 - 牛顿 二、光的波动说 - 托马斯杨 惠更斯 三、光是电磁波 - 麦克斯韦 四、光的波动说受到的挑战&#xff0c;光的波动说重新唤醒 五、光量子假说 - 爱因斯坦 六、光的理论综合 七、光的重要特性 7.1 光的相干性 7.2 相…

安装ideaIU-2022.1.4报错问题解决办法

原来版本是2020 现在因为要用到JDK17 而IDEA2020支持的JDK只能到14 所以我们现在在下载一个2022 .1.4 这时我们安装好后 会发现如下错误 原因分析&#xff1a; JetBrains 会将各个产品的这些文件也放到这个目录中&#xff0c;而新版本的 IntelliJ IDEA 在安装时遭遇了旧版本…

Web(二)html5基础-文本控制类标签(知识训练和编程训练)

web知识训练_html5_文本控制类标签 第1关_web知识训练_html5_文本控制类标签 -------------------------------------------- web编程训练_html5_文本控制类标签 第1关_html5_网页背景及标题段落标签 编程要求 根据前面的效果图&#xff0c;在右侧编辑器中的Begin - End区域…

10K涨到30K,5年大厂横跳,经验全在这两份Java面试精华总结里了

为了帮助大家跳槽成功多长点薪资&#xff0c;我这边给大家准备了两份面试资料。这两份资料可大有来头&#xff0c;已经帮助五位我认识的人进入大厂。所以说含金量还是很高的。 这两份资料可以让你体验到原有知识从破碎到重建&#xff0c;豁然开朗的过程。5年大厂横跳真实案例加…