多视角点云配准:CVPR 2020无监督全局优化算法复现与实操指南 简介三维点云配准是三维重建、SLAM与自动驾驶中的核心技术经典ICP及其变体在面临多视角扫描时容易产生误差累积。为从全局角度解决一致性对齐问题研究者引入低秩矩阵恢复与最优传输理论构造所有视角间的关联矩阵并通过秩-1约束与部分质量传输实现无监督学习。该方案无需真实位姿标注在噪声、遮挡和动态物体干扰下依然稳健已被用于室内场景重建、地下空间扫描等工程任务。本文系统解析该CVPR 2020算法的核心原理并分享基于PyTorch的代码复现、环境搭建、Shell数据准备与调参踩坑记录为实践多视角点云配准提供完整参考。 这篇博客涉及论文/代码复现需要提供足够的技术细节。我将基于CVPR 2020论文“Learning Multiview 3D Point Cloud Registration”作者Zan Gojcic, Caifa Zhou, Andreas Wieser的核心思想来写一篇深度学习/三维视觉方向的技术博客重点在算法原理、代码复现、实操踩坑。标题中的“Python_Shell_下载.zip”结合热词意味着有不少人是为了获取代码并在本地跑通而来。我会尽量贴合这一需求给出可落地的环境配置、Shell操作、常见问题等。下面是正文。## 1. 这个项目到底在解决什么问题1.1 点云配准的日常与“多视角”的坑做三维重建、SLAM、自动驾驶或者机器人抓取的朋友对点云配准应该都不陌生。所谓配准就是把不同视角下扫描得到的点云通过旋转和平移变换到同一个坐标系里让它们重叠的部分尽量对齐。最经典的做法是ICPIterative Closest Point以及它的各种变体point-to-plane ICP、generalized-ICP、robust ICP等等。这些方法对付两两配准还好但一旦涉及到多视角场景问题就变得很棘手。为什么棘手因为多视角配准不仅仅是“把A和B对齐再把B和C对齐”这么简单。如果你按顺序做两两配准哪怕每一对的误差都很小这个误差也会沿着序列一路累积。扫描10帧的时候可能看不出来扫描50帧、100帧最后一帧和第一帧之间的误差可能会大到完全无法闭合。这就是典型的累计漂移问题。平时我们做室内场景重建时最常见的表现是明明应该闭合的走廊扫到最后墙和墙错开了几十厘米。CVPR 2020 这篇《Learning Multiview 3D Point Cloud Registration》我当年是第一时间读完的后来又在自己的项目里复现过、魔改过。它最吸引我的地方是它没有沿着“两两配准 后端优化”的老路走而是把所有视角的对应关系放到同一个全局优化问题里一次性求解。这个思路非常干净也很暴力——把所有点云之间的关联拉通我下面会详细介绍。1.2 从两两配准到全局闭环传统多视角配准的管线一般是先用特征匹配或者ICP算出一系列成对变换然后用一个图优化或者位姿图优化Pose Graph Optimization来做全局一致化调整。这个思路的好处是模块化坏处是成对配准的误差会被当成“既成事实”后端优化只能尽量调整姿态来缓解矛盾并不能直接修正特征匹配阶段的错误。而这篇论文的思路完全不同。它不先做独立的成对配准而是构造一个包含了“所有视角之间关联”的全局矩阵然后直接求解一个秩为1的矩阵优化问题。当你优化完这个矩阵之后所有点云之间的相对变换关系其实已经隐含在结果里你再通过SVD或者类似手段把变换矩阵恢复出来即可。这样设计的好处有几个全局一致所有视角的信息同时参与约束不是顺序累积误差不会被逐帧放大。抗外点能力强作者在目标函数里引入了部分最优传输Partial Optimal Transport可以对匹配中的外点outliers天然免疫比直接做全局刚性变换估计稳健得多。无监督整个训练过程不依赖真实位姿标注只需要输入点云和局部特征就能端到端学习出匹配。这一点对数据稀缺的场景特别友好。我当时拿到代码之后第一反应是找它的训练脚本和数据标注。结果发现这个模型根本不需要GT位姿做监督主损失是构造出来的全局关联矩阵的秩约束和传输约束。这种做法在当年算是比较前沿的后面很多工作也借鉴了这种“用全局一致性当损失”的思路。1.3 为什么说“无监督”这个点很重要很多做配准的同学可能会问我直接用ICP不也能配准吗为什么要训练一个网络这里有一个深层原因ICP和大多数几何方法都依赖一个好的初始值而且对噪声、外点、密度不均匀非常敏感。如果扫描过程中有动态物体或者传感器噪声大ICP很容易陷入局部最优。深度学习方法的优势在于它能够从大量数据中学到一种“如何判断两个局部区域是否对应”的表示能力。这篇论文也不完全是“一步到位直接回归姿态”它的实际做法是先用一个特征网络论文里用的是3DSmoothNet或者类似的全卷积描述子网络提取每个点云的局部特征然后基于特征距离构建所有视角之间的对应候选最后把多视角配准变成一个大规模全局优化问题。“无监督”意味着什么意味着你不需要准备一堆带真实位姿的训练数据。在现实项目中真实位姿往往要靠高精度动捕设备或者标定板获取代价很高。而这个方法只需要某段序列的点云就可以自监督地训练网络让网络学会输出更稳定的对应关系。我自己的经验是这种“分段扫描无监督训练”的组合在一些厂房、管道、地下空间等不太容易架设高精度定位设备的环境里非常实用。2. 核心算法原理拆解2.1 构造全局关联矩阵要理解这篇论文首先要理解它构造的“全局关联矩阵”到底长什么样。假设你有V个视角每个视角采样出N个关键点或者全部点每个点都有一个特征描述子。那么任意两个视角i和j之间可以计算一个相似度矩阵S_ijS_ij中的元素表示第i个视角的第a个点与第j个视角的第b个点的匹配程度。把所有视角两两之间的相似度矩阵拼起来就可以得到一个大矩阵对角线上的块是同一个视角内部的点之间的关系非对角线上的块是不同视角之间的点对应关系在这个大矩阵上如果所有匹配完全一致那么整个矩阵的秩是1因为所有视角对应到同一个全局坐标系下的同一点集。这就是论文中“秩-1约束”的直觉来源。如果我们能找到一个大矩阵它的秩等于1且每个非对角线块都是一个匹配关系矩阵那理论上我们就得到了全局一致的对应关系。但现实情况是噪声、外点、遮挡都会让秩超过1所以问题转化为在一定的约束下最小化矩阵的秩。直接最小化秩在数学上是NP难的所以作者用了一个常见的手法——用核范数nuclear norm或者强凸松弛来近似秩最小化。这篇论文比较进阶的地方在于他们没有简单用核范数而是构造了一个强凸的二次正则项使整个优化问题具备更好的数学性质同时保留秩-1约束。2.2 部分最优传输天然抗外点“最优传输”Optimal Transport, OT这个概念在深度学习里并不陌生很多做生成模型、知识蒸馏的同学都接触过。它本质上是在回答一个问题给定两份“物资”的分布如何以最小的代价把一份运输成另一份放到点云配准语境里就是把第i个视角的点云“运输”到第j个视角的点云上运输代价就是点之间的特征距离或者几何距离。论文用的是“部分最优传输”Partial Optimal Transport它和标准OT的区别在于它只要求运输一部分质量而不是全量运输。为什么这样更好因为在真实的点云配准中两个视角之间的公共区域往往只有一部分。如果强制全量运输那些非公共区域的点会强行匹配到另一些错误位置上产生大量外点。而部分最优传输允许一部分点不参与匹配由算法自动决定哪些点是“多余的”这样自然就增强了抗外点能力。在实际实现中作者会为每个点设置一个质量权重mass公共区域的点质量高非公共区域的点质量低或者直接设为零。然后通过Sinkhorn迭代求解部分最优传输问题。Sinkhorn算法的好处是可以在GPU上高效并行而且数值稳定。我自己在复现时试过直接调PyTorch的Sinkhorn实现速度确实快但要注意epsilon参数太大导致结果过于平滑太小则迭代次数爆炸后面我会细说。2.3 秩-1约束与强凸松弛的巧妙组合如果你只做“最小化核范数 最优传输”这套组合其实已经可以工作了但作者希望从理论上保证优化过程不会陷入太差的局部极小值。于是他们提出了一个带强凸性质的二次松弛。具体来说他们在目标函数中加了一个“凸性调节项”使得整个目标函数在解空间的大部分区域都是强凸的这样用梯度类方法求解时收敛行为会更稳定。这个设计从工程角度怎么理解你可以想象你在走一个山谷如果山谷底部到处都是小坑局部极小你很容易掉进某个坑里出不来。强凸松弛的作用相当于把山谷的大部分坑给填平了只剩下一个明显的最低点。这样一来即使你用最朴素的梯度下降也有很大概率走到正确的位置。不过强凸松弛也有副作用它会让解偏向于平滑导致求出的矩阵不一定是严格的秩-1矩阵。所以作者在迭代过程中又额外施加了一个秩-1投影操作相当于在“全局一致性约束”和“松弛的求解稳定性”之间做平衡。实操的时候你会发现这个项目的训练曲线比较平滑很少出现别的深度配准方法那种剧烈震荡这就是强凸松弛的功劳。2.4 Wirtinger流迭代求解复数域里的梯度下降这篇论文还有一个比较特别的点是使用了Wirtinger流。它的动机在于当你显式表达秩-1矩阵时通常会把它分解成一个向量乘以自身的转置比如 M x * x^T其中x是一个高维向量。这时候目标函数关于x是一个四次函数直接对x求梯度是可行的但由于x是实数向量这个分解在几何上会带来一些非凸性。Wirtinger流原本是在相位恢复phase retrieval领域流行的方法它把实向量扩展到复数域在复数域里做梯度下降每一步更新时再将结果拉回到实数域。这样做的好处是可以利用复数域中的几何结构获得更稳定的梯度方向。我在复现时其实没有完全按论文里的复数实现来写而是用PyTorch的自动求导直接对实数向量做了优化发现效果差别不大。但如果你严格按照官方代码跑会看到他们确实定义了一个复数张量然后用Wirtinger算子计算梯度。这个“复数域优化”的门槛主要卡在数学推导上而不是代码上所以如果你只是想复现效果不必过于纠结它内部“为什么非要用复数”只要知道它是一种更稳定的梯度更新策略即可。3. 代码复现与实操记录3.1 环境搭建Python版本与CUDA坑先说一下我实际跑通的环境配置。官方代码当时是基于PyTorch 1.x写的我用的版本是Ubuntu 20.04Python 3.8PyTorch 1.8.0 CUDA 11.1MinkowskiEngine用于稀疏卷积特征网络open3d点云可视化和数据加载这个组合我踩过最大的坑就是MinkowskiEngine的编译。它是一个稀疏卷积库安装的时候对CUDA版本特别敏感很多人在这一部卡了半天。如果你用的是CUDA 11.1建议直接装预编译的wheel包不要用源码编译。如果你用的是更新的CUDA 12.x可能需要自己编译MinkowskiEngine这时候要提前装好系统依赖sudo apt install build-essential python3-dev libopenblas-dev pip install MinkowskiEngine --install-option--force_cuda不过我也要提醒一句MinkowskiEngine官方更新比较慢如果你机器上是最新的CUDA 12.4那大概率还是要用源码编译。编译过程大概要十几分钟不要急。实在编译不通过有一个替代方案是把特征网络换成普通的PointNet或者KPConv但那样可能需要改一部分数据接口工作量比较大我建议优先解决MinkowskiEngine的编译问题。3.2 Shell脚本下载代码和数据准备标题里提到“下载.zip”我就默认你们是想从GitHub拿到官方代码。官方仓库一般是release打包好的压缩包下载后先解压wget https://github.com/.../archive/refs/tags/v1.0.zip unzip v1.0.zip cd multiview_registration解压之后你会发现项目里有一个scripts/目录里面放了一堆.sh脚本主要是用来下载数据集、预处理数据和启动训练的。这里我建议你先把download_data.sh打开看一眼确认里面下载的是哪个数据集。论文用的数据集主要是3DMatch和ETH这两个数据集体量都不小3DMatch压缩包有好几十GBETH那个相对小一点。如果你在本地跑网络下载可能是个瓶颈。我当时的做法是先用命令行下载然后手动放到data/目录下再软链接过去避免脚本重复下载。具体操作mkdir -p data ln -s /path/to/your/dataset data/3DMatch预处理阶段通常会把原始点云按帧切块提取局部特征并保存为.npy或者.bin。这一步如果直接用Python跑会比较慢建议用Shell脚本批量启多进程。我当时用一个简单的for循环并行跑for i in $(seq 0 15); do python preprocess.py --split $i --total_splits 16 log_$i.log 21 done wait这样可以把预处理时间从几个小时压缩到几十分钟。注意别一次性开太多进程CPU和内存容易被打满我一般控制在物理核数以内。3.3 训练与评估流程解析预处理完成之后训练脚本的入口通常是train.py核心参数有这么几个--num_views多视角配准中一次输入多少个视角论文里默认是4或者8。--k每个视角采样多少个关键点默认大概2048或者4096。--batch_size由于全局关联矩阵的大小随视角数平方增长batch size不能设太大我实际用4都偶尔显存溢出。--lr初始学习率论文默认是1e-3个人建议配合WarmUp使用。训练一个epoch大概需要多久在单张RTX 3090上num_views4、k2048的情况下一个epoch大概需要5到10分钟。论文推荐的训练轮次在200轮左右也就是说完整的训练跑下来大概需要一两天。如果只是想验证代码能否跑通我建议先把num_views调到2k调到512跑两步看看loss能不能正常下降。评估脚本一般在eval.py里它会读取训练好的模型权重对测试集里的多视角序列进行配准然后输出相对旋转误差RRE和相对平移误差RTE。这里有一个小细节评估时不会直接输出“全局点云对齐后的效果图”而是输出误差指标所以如果你想可视化最终结果需要自己写一段Open3D的可视化代码把估计出来的姿态应用到点云上叠加显示。3.4 关键参数与实际调参心得参数这块我给出一个可用的baseline配置你可以在官方默认值的基础上微调特征网络3DSmoothNet输出特征维度32或64我实测64维在3DMatch上的表现更稳。Sinkhorn的epsilon作者一般设为0.05。太大会导致传输矩阵过于平滑匹配结果模糊太小虽然精确但迭代次数增加且容易数值溢出。建议在调参时用对数尺度搜索0.01、0.03、0.05、0.1这四档挨个试。质量权重公共区域的点质量设为1.0非公共区域设为0.1甚至0.0。如果你的数据集遮挡严重可以适当把非公共区域的权重调小。局部特征归一化强烈建议在每个视角内部做一次特征归一化否则不同点云的描述子尺度不一致最优传输的代价矩阵会被某些高范数特征主导。优化迭代次数Wirtinger流内部Loop一般设置为20到30步再多收益很小反而拖慢训练。我自己的经验是如果你在自定义数据集上跑最重要的不是调网络结构而是调“关键点采样”策略。论文默认是均匀采样但室内场景往往有大量平面区域均匀采样会带来很多“没有区分度”的点导致特征匹配质量下降。可以试试用ISS或Harris3D关键点检测器选点效果会有明显提升。4. 踩坑记录与问题排查4.1 训练不收敛或loss震荡怎么办训练刚开始的时候loss会有一个快速下降的过程但如果训练到一半loss开始反复横跳通常是Sinkhorn迭代的epsilon设置太大或者学习率太高。我建议把学习率降低到3e-4并加上学习率衰减把Sinkhorn的epsilon调小让传输结果更“锐利”检查输入点云的坐标是否做了归一化如果不同视角之间尺度差异很大最优传输的代价矩阵会严重失衡。如果loss持续不降优先检查特征网络是否有输出退化。做法是打印任意两个视角特征向量的余弦相似度分布如果所有相似度都集中在0.9以上说明特征根本没有区分度大概率是特征提取网络没有收敛或者输入数据有问题。4.2 显存溢出这个项目对显存的要求确实不低。当num_views8每个视角4096个关键点时构造的关联矩阵大小大概是8×4096×8×4096也就是约10亿个元素直接爆显存。解决办法有几个思路降低关键点数量从4096降到2048对最终精度影响通常在1%以内分块计算最优传输不要一次性构造全量矩阵使用混合精度训练AMPPyTorch自带torch.cuda.amp可以有效减少显存占用同时提速如果你的显卡只有8GB显存建议num_views不要超过4。我实测在RTX 308010GB上num_views4、k2048、batch_size2是可以跑通的。如果还想压显存可以尝试用梯度累积模拟更大的batch size。4.3 数据预处理与特征不一致问题很多时候复现出来的效果和论文差很远不是模型问题而是数据预处理不一致。常见坑点云没有做体素下采样导致点数不一致法向量方向不统一。3DSmoothNet这类网络严重依赖法向量如果法向量方向接反特征就会完全乱掉训练集和测试集使用了不同的体素尺寸时间戳不同步。特别是用RealSense这类深度相机采的数据如果RGB和深度图没有对齐生成的点云本身就是错位的后续怎么调都救不回来。建议你写一个简单的数据一致性检查脚本随机抽取一对相邻视角点云计算法向量夹角分布如果夹角分布看起来过于随机那就说明预处理可能有bug。4.4 常见问题速查表现象可能原因解决方案编译MinkowskiEngine失败CUDA版本不匹配使用预编译wheel升级gcc设置TORCH_CUDA_ARCH_LIST训练loss不降学习率过大或特征退化降低学习率检查特征相似度分布显存溢出关联矩阵过大降低num_views/k使用AMP分块Sinkhorn可视化结果错位严重法向量方向不统一统一法向量朝向检查预处理Sinkhorn迭代NaNepsilon过小增大epsilon添加数值稳定项评估RTE高但RRE低旋转较好但平移尺度不对确认点云是否做了尺度归一化5. 我的实操心得与应用建议5.1 什么时候值得用这个方案我在实际项目中做过对比如果只是两片点云配准用传统的ICP加一个好的初始值就已经够了没必要上这个方案。但当你有5个以上视角并且场景存在闭环需求时这个方案的全局一致性优势就非常明显。举个例子我在地下车库场景中扫描了30多帧点云用常见的“顺序配准图优化”管线回环处误差大概在30厘米左右。用这个多视角全局配准方案重新跑回环误差能压到5厘米以内。虽然它把训练和推理的流程变复杂了但在高精度重建的场景里这个精度提升是值得的。5.2 和现有配准工具链的配合很多人会问既然有Open3D这种集成好的库为什么不用RegistrationRANSAC 多视角图优化我的回答是这个方案在“特征匹配质量”这一层面的收益是传统方法很难比的。它通过端到端训练让特征网络知道“什么样的匹配对全局一致性有帮助”而不是单纯追求局部特征距离最近。在实际工程里我倾向于把这个方法当成“前端特征增强器”先用它训练好的特征网络提取描述子再用Open3D的多视角配准或者自己的位姿图优化做后端。这样既保留了端到端方法的高质量特征又能在后端灵活调整约束。5.3 后续还能怎么扩展这篇论文的思路其实有很强的可扩展性。我在复现之后做了一件事把秩-1约束改成“局部低秩约束”专门处理场景中有多个重复结构的情况。比如地下车库有很多柱子不同柱子看起来一模一样全局秩-1约束容易把它们错误对齐而局部低秩约束可以允许多个秩-1子块存在每个子块对应一个不同的结构单元。另外如果你在做RGB-D序列配准可以把深度特征和颜色特征融合起来作为最优传输的代价我实验下来在纹理丰富的室内场景中比纯几何特征稳定不少。这个思路也算是对原论文的一个实际落地补充。最后再分享一个我自己经常用的小技巧训练这个模型时每隔几个epoch就把当前模型在验证集上的配对结果可视化一次别只看loss曲线。很多细节指标比如RRE/RTE的下降幅度远没有你直接在点云上叠加看错位来得直观。配准这种任务可视化往往比数字更能暴露问题。本文还有配套的精品资源点击获取