目录

一. 简介

二. 基础结构

三. 项目代码

四. 实验结果

总结

Reference


今天的点云深度学习系列博客为大家介绍一个用于点云配准的深度网络:PCRNet [1]。凡是对点云相关应用有些了解的同学,相信都接触过点云配准。配准相关的经典算法,包括ICP,NDT,FPFH,已被广泛应用于工业设计,三维定位,SLAM,自动驾驶等领域。尤其在SLAM和自动驾驶领域,随着家庭清扫机器人和电动汽车的普及,基于激光扫描技术的点云配准技术得以快速发展,并在实际应用中实现部署,可见相关研究的经济价值。自ICP算法被提出,虽然点云配准已经被研究了几十年时间,一些技术难点仍然没有被解决,包括尺度归一化,点对应,局部最优,噪声与点云缺失以及计算效率问题等。PCRNet基于PointNet点云深度学习架构,提出了一个新的点云配准方案。该方案针对噪声鲁棒性,计算速度等问题,提供了新的解决思路,相对于传统方法获得了一定的性能提升。接下来,而我们就来具体探讨一下PCRNet的一些技术细节。


一. 简介

将深度学习的相关工具用于解决点云配准的问题,存在两个难点,第一,如何有效的建立针对点云的表示形式,以适应网络学习;第二,如何建立有效的迭代优化方法,使得配准过程能够利用深度学习强大的特征分析能力。针对第一个问题,2019年前后的配准工作,主要是基于PointNet对点云编码来解决。由于PointNet利用池化技术,有效的提取了点云的全局特征。基于这种全局特征,点云能够被表示为一个与点顺序无关的向量形式。这个过程相当于对点云进行了一次无序编码,使得编码后的特征向量继承了点云的全局特征与语义信息。针对第二个问题,之前的工作还是使用传统的优化方法,如牛顿法,ICP,梯度下降,LK算法等,针对编码后的特征向量实现姿态对齐。主要缺点就是没有利用深度学习来实现对变换矩阵的求解,整个计算过程的效率还是比较低的。

PCRNet的提出,尝试使用一个基于全连接层的深度网络结构,直接输出基于7个参数表示的变换矩阵。因为没有使用传统的姿态优化,而是直接利用全连接层求解变换矩阵,其优点在于计算效率高,有效的学习了大规模样本的配准规律,且对噪声的鲁棒性具备一定优势。下面我们就来介绍下PCRNet的基础结构。


二. 基础结构

PCRNet分为两个部分,第一部分基本等同于PointNet全局特征提取结构,分别由几个MLP组成,数据维度分别变换为(64,64,64,128,1024)。到最后每一个点变换为一个1024维的特征向量,之后做最大池化,每一维取最大值,那么就从一个n*3的点云变成1*1024的特征向量。配准的输入是两个点云,因此就会得到两个1024维的向量,将二者结合,就变成了一个2048维的向量,至此,基于PointNet的编码过程完成,图1展示了该过程。

图1. 基于PointNet的点云编码过程。

比较关键的是第二部分,即PCRNet基于编码的变换矩阵求解过程。这里,PCRNet基于全连接层结构设计了一个神经网络,直接求解变换矩阵。网络结构如下:

图2. 用于求解变换矩阵的全连接层神经网络。

由上图可知,该结构包含五个隐藏层,维度变换为(1024,1024,512,512,256,7)。最后的输出是一个7维的特征矩阵,表示变换矩阵相关的7个参数,前三个用于表示平移,后四个用于表示角度(不知道为啥用4个参数表示角度,感觉3个也没有问题)。基于该结构,PCRNet通过训练能够直接输出变换矩阵。

Loss Function选择使用EMD距离计算,其公式表示如下:

该公式求一个双射函数 ψ,使得基于该函数的点对距离和取最小。EMD的定义是清楚的,相对于最近点距离以及豪斯多夫距离,对点云对应关系的衡量更准确。EMD的计算效率较低,这种针对双射的优化过程,其计算开销必然远超其他度量方法。不过对于训练过程无所谓,因为训练时间开销再大,也不会影响测试过程。参数一旦被确定,实际计算效率仍然是线性的。

类似于PointNetLK中的LK算法以及ICP,对于变换矩阵的求解过程,仍然是一个迭代的过程。经过每一轮求解得到的变换矩阵,将源点云按照变换矩阵进行变换,然后重新输入PointNet提取全局特征,然后继续求解新的变换矩阵。经过一系列的迭代,直到收敛。T为变换矩阵迭代过程。图3展示了变换矩阵的求解过程。

图3. 变换矩阵求解过程。

至此,我们介绍了PCRNet的基础机构以及求解过程。可以看到,其整个过程都是基于MLP以及全连接层来实现优化与计算的,这样就让整个配准过程的计算开销维持在线性水平,进而保证了算法的计算效率。


三. 项目代码

项目主页:https://vinitsarode.weebly.com/pcrnet.html

代码链接:GitHub - vinits5/pcrnet: Point Cloud Registration Network

PCRNet初始化代码:

self.feature_model = feature_model
self.pooling = Pooling(pooling)self.linear = [nn.Linear(self.feature_model.emb_dims * 2, 1024), nn.ReLU(),nn.Linear(1024, 1024), nn.ReLU(),nn.Linear(1024, 512), nn.ReLU(),nn.Linear(512, 512), nn.ReLU(),nn.Linear(512, 256), nn.ReLU()]if droput>0.0:self.linear.append(nn.Dropout(droput))self.linear.append(nn.Linear(256,7))
self.linear = nn.Sequential(*self.linear)

feature_model传入的就是PointNet,后边的Linear用来表示变换矩阵的全连接层。

更新代码:

self.source_features = self.pooling(self.feature_model(source))
y = torch.cat([template_features, self.source_features], dim=1)
pose_7d = self.linear(y)
pose_7d = transform.create_pose_7d(pose_7d)# Find current rotation and translation.
identity = torch.eye(3).to(source).view(1,3,3).expand(batch_size, 3, 3).contiguous()
est_R_temp = transform.quaternion_rotate(identity, pose_7d).permute(0, 2, 1)
est_t_temp = transform.get_translation(pose_7d).view(-1, 1, 3)# update translation matrix.
est_t = torch.bmm(est_R_temp, est_t.permute(0, 2, 1)).permute(0, 2, 1) + est_t_temp
# update rotation matrix.
est_R = torch.bmm(est_R_temp, est_R)source = transform.quaternion_transform(source, pose_7d)    # Ps' = est_R*Ps + est_t

首先用feature_model对source做一步全局特征提取,池化后作为输入到已经定义好的Linear结构,即针对全连接层的姿态优化结构。可以看到,Linear的输出是一个pose_7d,即由7个参数表示的变换矩阵。之后,按照pose_7d变换source,并记录变换矩阵的信息即可。

这里特别说下EMD计算:

def chamfer_distance(template: torch.Tensor, source: torch.Tensor):from .cuda.chamfer_distance import ChamferDistancecost_p0_p1, cost_p1_p0 = ChamferDistance()(template, source)cost_p0_p1 = torch.mean(torch.sqrt(cost_p0_p1))cost_p1_p0 = torch.mean(torch.sqrt(cost_p1_p0))chamfer_loss = (cost_p0_p1 + cost_p1_p0)/2.0return chamfer_loss

这里作者给出了一个基于Cuda的实现,即函数ChamferDistance()

class ChamferDistanceFunction(torch.autograd.Function):@staticmethoddef forward(ctx, xyz1, xyz2):batchsize, n, _ = xyz1.size()_, m, _ = xyz2.size()xyz1 = xyz1.contiguous()xyz2 = xyz2.contiguous()dist1 = torch.zeros(batchsize, n)dist2 = torch.zeros(batchsize, m)idx1 = torch.zeros(batchsize, n, dtype=torch.int)idx2 = torch.zeros(batchsize, m, dtype=torch.int)if not xyz1.is_cuda:cd.forward(xyz1, xyz2, dist1, dist2, idx1, idx2)else:dist1 = dist1.cuda()dist2 = dist2.cuda()idx1 = idx1.cuda()idx2 = idx2.cuda()cd.forward_cuda(xyz1, xyz2, dist1, dist2, idx1, idx2)ctx.save_for_backward(xyz1, xyz2, idx1, idx2)return dist1, dist2@staticmethoddef backward(ctx, graddist1, graddist2):xyz1, xyz2, idx1, idx2 = ctx.saved_tensorsgraddist1 = graddist1.contiguous()graddist2 = graddist2.contiguous()gradxyz1 = torch.zeros(xyz1.size())gradxyz2 = torch.zeros(xyz2.size())if not graddist1.is_cuda:cd.backward(xyz1, xyz2, gradxyz1, gradxyz2, graddist1, graddist2, idx1, idx2)else:gradxyz1 = gradxyz1.cuda()gradxyz2 = gradxyz2.cuda()cd.backward_cuda(xyz1, xyz2, gradxyz1, gradxyz2, graddist1, graddist2, idx1, idx2)return gradxyz1, gradxyz2class ChamferDistance(torch.nn.Module):def forward(self, xyz1, xyz2):return ChamferDistanceFunction.apply(xyz1, xyz2)

整个过程我没有看懂。直观的感觉,这段代码像是动态规划的一种实现,包括前传后传,还用了递归,确实算了不少东西,最后得到双射结果。整个计算过程应该是利用了Cuda并行计算的,否则计算开销实在是太大了。


四. 实验结果

作者给出两个实验对比图,证明在噪声干扰以及数据分布不均匀的双重影响下,PCRNet的性能显著好于ICP。

基于配准结果的对象替换:

 

作者还给出了PCRNet与ICP、PointNetLK的迭代对比结果,如下如:

可以看到,PCRNet能够在收敛速度更快,误差更小。


总结

对于配准任务来说,PCRNet针对变换矩阵的求解还是有一些新意的。脱离传统的ICP,NDT以及LK算法的帮助,利用全连接层直接对变换矩阵求解,不得不说是一个大胆的尝试。经过实验可以看到,PCRNet充分利用了深度学习高效的计算能力,以获得结果。然而,在实际使用中,该算法的泛化性较差。我认为其原因在于,PCRNet并没有解决变换矩阵的求解问题。基于一个大的样本库,并且包含各种各样的姿态,利用深度学习获得一个全局最优的结果,听上去是一个可行的方案。但我们不能保证训练数据能够很好的覆盖各种姿态,各种位置。一旦实际数据的分布不满足训练数据的样本特性,或者训练数据的变换本来就存在较大的偏见,其实际性能就会打折。我个人认为,未来的改进思路还是要放在点对应上。基于深度学习建立更好的点特征描述,并建立基于特征描述的点对应。这样,有了更好的对应关系,就能够利用ICP或者SVD来计算变换矩阵。直接利用网络结构求解变换矩阵,直观感觉还是有些激进,不太看好这条技术路线。


Reference

[1] V. Sarode, X. Li, H. Goforth, et al. Pcrnet: Point cloud registration network using pointnet encoding[J]. arXiv preprint arXiv:1908.07906, 2019.

点云深度学习系列博客(二): 点云配准网络PCRNet相关推荐

  1. 点云深度学习系列博客(一): 点云特征学习网络PCPNet

    目录 一. 简介 二. 基础结构 三. 项目代码 四. 实验结果 总结 Reference 最近开始研究点云分析的相关项目,经过文献调研我发现,近几年比较热的方法,基本都是基于深度学习框架设计的.正好 ...

  2. 点云深度学习系列博客(四): PointNet代码精讲

    目录 1. 代码解析 1.1 初始化 1.2 数据载入 1.3 模型载入 1.4 训练代码 2. 实验结果 Reference 最近开始上手点云深度学习项目,相比之前纸上谈兵的阶段,此时我将把更多的精 ...

  3. 点云深度学习系列由浅入深之--SPLATNet: Sparse Lattice Networks for Point Cloud Processing

    点云深度学习系列由浅入深之--SPLATNet: Sparse Lattice Networks for Point Cloud Processing 0. SplatNet网络主要结构及模块 1. ...

  4. PX4代码学习系列博客(6)——offboard模式位置控制代码分析(之前转载过,这是第二次转载了)

    我刚刚发现这篇文章去年八月份的时候转载过一次了 https://blog.csdn.net/sinat_16643223/article/details/107874349 转载自:https://b ...

  5. 系统学习深度学习(博客转载地址)

    转载深度学习学习系列的一些文章 1. 系统学习深度学习(一) --深度学习与神经网络关系 https://blog.csdn.net/app_12062011/article/details/5431 ...

  6. 【技术博客】2020.04.28-简单塔防游戏和棋牌游戏构架学习 ——学习系列博客(一)构架初探

    序言,保卫萝卜项目作为自己学习整体游戏项目的开始,还是很有收获的. 项目初步实现了分管关卡地图编辑.场景结构.关卡选择.游戏地图等主要功能,同时内部构架采用了MVC加单例的构架,对我这种初学者还是很有 ...

  7. 深度学习系列学习博客

    零基础入门深度学习(1) - 感知器 零基础入门深度学习(2) - 线性单元和梯度下降 零基础入门深度学习(3) - 神经网络和反向传播算法 零基础入门深度学习(4) - 卷积神经网络 零基础入门深度 ...

  8. RPC框架原理及从零实现系列博客(二):11个类实现简单RPC框架

    项目1.0版本源码 https://github.com/wephone/Me... 在上一博文中 跟大家讲了RPC的实现思路 思路毕竟只是思路 那么这篇就带着源码给大家讲解下实现过程中的各个具体问题 ...

  9. android中页面自动跳转,【学习笔记-安卓开发】8. Android Studio如何实现页面自动跳转(安卓学习系列博客)...

    先将上上一篇博客中写在页面里的button以及相关代码删除 8.如何让页面自动跳转 在安卓开发中有一个非常重要的Handler 当我们输入Handler会出现两个提示,一个是os中的,一个是loggi ...

最新文章

  1. 跟我学,轻松安装开源ERP软件Open ERP
  2. 阿里云云服务器硬盘分区及挂载
  3. C#抽象类和抽象方法的特征和用途
  4. Eclipse快捷键大全,导包快捷键:ctrl+Shift+/【转】
  5. 农民约翰是一个惊人的会计_我的朋友约翰在CSS Grid中犯了一个错误。 不要像约翰-这样做。
  6. (01)System Verilog验证理论
  7. wps2000老版本 v3.02.99
  8. 一键把动态IP自动设置为静态IP
  9. aria2使用rpc下载百度云
  10. 都说ApiPost香,它到底香在哪里?
  11. python免费自学爬虫_看这里!免费python网络爬虫一站通
  12. Excel--查找、替换及定位
  13. 打开设备管理器找不到Android Composite ADB Interface
  14. java高德点到ian距离,高德地图(点到线段的最短距离算法)不调用高德API
  15. 2.1、Segment Routing基础之SR关键概念
  16. 『论文笔记』TensorFlow1.6.0+Keras 2.1.5+Python3.5+Yolov3训练自己的数据集!
  17. 楼市传言四起不排除人为制造
  18. linux节点测试,linux中speedtest-cli 选择测试节点(服务器)例子
  19. 2019年国庆假期出行预测报告
  20. IE6与其它浏览器的区别

热门文章

  1. 决策树常见的面试点整理
  2. TGRS2021:Road Segmentation for Remote Sensing ImagesUsing Adversarial Spatial Pyrami对抗性空间金字塔网络的道路分割
  3. 五.linux设备驱动模型
  4. spring配置事务,是否可以实现行级锁(for update)
  5. python大数据运维常用脚本_python大数据运维
  6. html实现点击出现小爱心
  7. Redis存放短信验证码 RedisTemplate =>opsForValue
  8. 深度:戴尔中国十年之变
  9. 【如何高效管理Linkedin账号】
  10. lsnrctl command not found