@
这篇文章以介绍为主。
ORB-SLAM2是一个用于单目、双目、RGB-D相机的完整SLAM系统,包括地图复用、回环检测和重定位功能。无论是室内的小型手持设备,还是工厂中的无人机和城市中行驶的汽车,ORB-SLAM2都能够在标准CPU上实时工作。ORB-SLAM2后端采用基于BA的优化,可以得到公制尺度的精确轨迹估计。此外,ORB-SLAM2包含了一个轻量级的定位模式,该模式利用视觉里程计来追踪未建图区域,以及匹配地图点实现零漂移定位。29个公共数据集测试结果显示,在大多数情况下ORB-SLAM2精度更高。
contributions:
ORB-SLAM2的总体概述如上,它具有3个主要的并行线程,
另外,该系统嵌入了基于DBoW2的位置识别模块,用于在跟踪失败(例如遮挡)的情况下重新定位或在已建图的场景中重新初始化,以及用于回环检测。
接下来,文章只介绍了ORB-SLAM2对初代ORB-SLAM的改进,譬如,如何利用深度信息,ORB-SLAM2的哪些部分与ORB-SLAM不同。如果需要知道系统的各个模块的详细描述,需要查看初代ORB-SLAM文章。
ORB-SLAM2系统对双目或RGB-D相机的图像进行预处理,并将特征点分成单目特征点、远双目特征点和近双目特征点,保证其系统的运行都是基于输入图像的特征点展开而不是图像。
双目特征点由三个坐标定义:
$$
begin{array}{c}
mathbf{x}_s=(u_L,v_L,u_R),u_L,v_L是左图像的像素坐标,
u_R是右图像的水平像素坐标。
end{array}
$$
如果是由RGB-D相机拍摄的,使用如下式子转化为双目特征点:
$$
begin{array}{c}
u_R= u_L - frac{f_x b}{d},f_x为水平焦距,
b为结构光投影器和红外相机之间的基线。
end{array}
$$
如果双目特征点的深度小于双目/RGB-D基线的40倍,这个特征点就被视为近双目特征点。近特征点能从单一帧中进行三角化得到精确估计的深度,并能提供尺度、平移和旋转信息。远点提供精确的旋转信息,但是提供较弱的尺度和平移信息,并且需要多个视角支持远点才可以三角化。
单目特征点通过左图坐标(uL,vL)定义,即,无法找到双目匹配的ORB特征点,和,在RGB-D图上具有无效深度值的ORB特征点。这些点需要多视角才可以三角化,不能提供尺度信息,但是可以提供旋转和平移的估计信息。
使用双目/RGB-D相机的主要优势之一为,通过仅从一帧获得深度信息,不需要像单目相机那样从运动初始化获得特定的结构。在系统启动时,它使用第一帧创建关键帧,设置它的位姿为原点,并且从所有双目特征点创建一张初始地图。
ORB-SLAM2系统中的存在三种BA,使用g2o实现的Levenberg–Marquardt方法去优化:
由于双目/深度信息使得尺度可观,几何校验和位姿图优化中不再处理尺度漂移问题,并且是基于刚体变换而不是基于相似性。
全局BA将在完成位姿图优化后进行,这种优化的成本可能非常高,因此以单独的线程执行,允许系统同时建图和回环检测。然而,如何将BA的输出结果与地图的当前状态融合是一个问题。如果在全局BA运行时检测到新的闭环,将中止全局BA并进行闭环,然后再次启动完整的全局BA优化。当全局BA结束时,将全局BA优化的关键帧和点的子集进行更新,并与全局BA运行时未更新的关键帧和插入的点进行融合(通过生成树将更新关键帧的校正传递到未更新的关键帧中,未更新点根据参考关键帧的矫正进行变换)。
ORB-SLAM2基本遵循单目ORB-SLAM的方法:宽松的关键帧插入策略并筛选剔除冗余的关键帧。
另外,由于引入了双目和RGB-D相机,还需要根据远近特征点的差异,设置新的关键帧插入条件,即:当跟踪的近特征点小于τt=100且该帧创造大于τc=70个新的近特征点时,插入新的关键帧。(因为需要大量的近特征点用于精确估计平移)
适用于地图已知的长期定位。此模式下停用局部BA(即停止建图),回环检测线程也停用,始终通过追踪线程进行重定位。在这种模式下,跟踪线程利用视觉里程计,将当前帧的特征点和已经生成的3D点云(由局部BA优化),进行匹配。这些匹配使得定位在未建图区域较为鲁棒,但是会累积漂移。在已知地图中,可以通过地图点匹配,实现零漂移定位。
现有基于NeRF的SLAM系统具有两个缺陷:1.因为多层感知机倾向于学习低频数据,从而忽略高频数据,导致场景重建过于平滑;2.多层感知机在不断接收新的数据进行训练,面临“遗忘”的问题,导致难以应用于大型场景。
NICE-SLAM是基于神经隐式表达NeRF进行建图的SLAM系统,针对以上问题,NICE-SLAM引入了多分辨率的网格模型来分别表示粗尺度、中尺度、细尺度下的场景,并且不同分辨率下的MLP会经过预训练,从而实现了在大型室内场景的稠密重建。这种利用grid的思想,在笔者看来,非常类似于Plenoxels和InstantNGP。
从左到右,NICE-SLAM使用RGB-D图像作为输入,使用多分辨率的分层特征网络表示场景,并使用基于pixel和depth的loss去优化grid和pose(BA),即Mapping过程。从右到左,NICE-SLAM使用已经训练好的MLP去进行一个Novel View Synthesis的任务,然后同样会使用一个基于pixel和depth的loss反传优化相机pose,即tracking过程。Novel View Synthesis的过程就是我们再熟悉不过的NeRF的体渲染。
前三个MLP用于输出体渲染需要的体素密度数值,后一个MLP用于输出这个点的颜色值。这里的MLP起到一个特征解码器的作用。
$$
begin{array}{c}
f0表示粗糙级别的网格,f1表示中级别的网格,f^2表示精细级别的网格。
end{array}
$$
网络的分辨率是根据物理尺寸决定的,譬如,Mid级别的grid的格子边长为32cm(TUM RGBD数据集中为16cm),Fine级别的grid的格子边长为16cm(TUM RGBD数据集中为8cm)。
体渲染所用到的体素密度是通过Mid Level和Fine Level联合计算得出,因此,在Mapping过程中,也只会优化这两个级别的特征网格。这些MLP都是预训练好的,不会进行任何更改。以下就是体素密度的计算过程:
$$
begin{array}{c}
o_mathbf{p}1=f1(mathbf{p},phi_theta^1(mathbf{p})) ,
Delta o_{mathbf{p}}1=f2(mathbf{p},phi_theta1(mathbf{p}),phi_theta2(mathbf{p})),
o_{mathbf{p}}=o_{mathbf{p}}^1+Delta o_{mathbf{p}}^1 ,
phi_theta(mathbf{p})表示特征网络在点mathbf{p}处进行三线性插值。
end{array}
$$
中分辨率能够有效地优化网格特征以适应观测结果。为了在场景几何图形中捕捉更小的高频细节,NICE-SLAM以残差方式添加精细级别的特征。精细级的特征解码器将相应的中级特征和精细级特征作为输入,并输出一个偏移量,把这个偏移量加到中级特征解码器的输出上,就是最终的体素密度了。
粗级特征网格旨在捕获场景中大尺度的几何形状(例如墙壁、地板等),它的优化过程独立于中级和精细级进行。对于那些未被mid-level和fine-level估计的点,就由coarse-level所估计。这些大尺度的几何在图片中通常仅被部分观察到(例如墙壁、地板等)。为此,coarse-level grid在实现中使用了非常低的分辨率,边长为2m。它输出体素密度的过程如出一辙。
$$
o_mathbf{p}0=f0(mathbf{p},phi_theta^0(mathbf{p})).
$$
在Tracking期间,coarse-level输出的体素密度同样仅用于预测先前未观察到的场景部分。
NICE-SLAM使用三种不同的固定的多层感知机来将网格特征解码为体素密度。解码器作为ConvONet的一部分进行预训练,它由一个CNN编码器和一个MLP解码器组成,并使用预测值和真值之间的交叉熵损失(binary cross-entropy)来训练编码器和解码器。经过训练后,只使用MLP解码器,因为NICE-SLAM将直接优化特征。通过这种方式,预先训练好的解码器可以在解码我们优化后的特征时,利用从训练集学习到的特定于分辨率(resolution-specific)的先验。
颜色估计和体素密度的估计过程不太一样,color-level的解码器是可以进行优化的,因此,color-level解码器和color-level grid将进行联合优化。NICE-SLAM称,这样可以提高跟踪性能。但是,与iMAP类似,这可能会导致“遗忘”问题,并且颜色只在局部保持一致。如果想可视化整个场景的颜色,可能需要进行全局优化作为后处理步骤。
$$
mathbf{c_p}=mathbf{g_omega}(mathbf{p},psi_omega(mathbf{p})).
$$
体渲染导出pixel的过程我们已经很熟练了,这里还要在讲一下计算depth的过程,实际上跟计算pixel也如出一辙。
$$
begin{array}{c}
mathbf{p}i=mathbf{o}+d_i mathbf{r},i in {1,...,N} ,
omega_i = oi} prod^{i-1}(1-o_{mathbf{p}j}) ,
深度:hat{D}=sum^Nw_id_i ,
颜色:hat{I}=sum_{i=1}^Nw_imathbf{c}_i .
end{array}
$$
此外,还计算了沿光线的深度的方差:
$$
hat{D}{var}c=sum_{i=1}Nw_ic(hat{D}c-d_i)^2 ,
hat{D}f=sum_{i=1}Nw_if(hat{D}f-d_i)^2,f in {c(coarse),f(fine) }.
$$
在当前帧和关键帧中均匀采样M个像素,定义几何损失(geometric loss)和光度损失(photometric loss)如下:
$$
mathcal{L}_gl=frac{1}{M}sum_{m=1}Mleft|D_m-hat{D}_m^lright|,quad lin{c,f} .
mathcal{L}p=frac{1}{M}sum^Mleft|I_m-hat{I}_mright|.
$$
然后,这个联合优化问题就是:
$$
min_{theta,omega,{mathbf{R}_i,mathbf{t}_i}}(mathcal{L}_gc+mathcal{L}_gf+lambda_pmathcal{L}_p).
$$
在当前帧中采样Mt个像素,修改几何损失为:
$$
mathcal{L}{g _ var}=frac{1}{M_t}sum{M_t}frac{left|D_m-hat{D}_mcright|}{sqrt{hat{D}{var}c}}+frac{left|D_m-hat{D}_mfright|}{sqrt{hat{D}^f}}.
$$
然后,优化相机pose的问题就是:
$$
min_{mathbf{R},mathbf{t}}(mathcal{L}{g _ var}+lambdamathcal{L}_p).
$$
SplaTAM是首次利用3DGS的SLAM系统,实验表明,SplaTAM在相机姿态估计、地图构建和新视角生产方面的性能提高了2倍。作者认为,使用3D高斯来进行SLAM的渲染、跟踪、建图会带来以下好处
3DGS的体渲染过程我们已经很熟悉了,这里再提及一下SplaTAM所用到的深度图和轮廓图如何计算的。
$$
begin{array}{c}
与视角无关的颜色参数:mathbf{c},高斯点的中心坐标:mathbf{mu},
球半径:r,着色度:o,空间中一点的坐标:mathbf{x}
那么,这个点对于这个gaussian的值可以计算为:
f(mathbf{x})=oexpleft(-frac{|mathbf{x}-boldsymbol{mu}|2}{2r2}right).
end{array}
$$
然后,将一个pixel对应方向上的所有gaussian由深度从小到大进行体渲染即可:
$$
begin{array}{c}
T_{cw}这篇文章中记作E_t,K为相机内参,f为焦距。
d=(E_tboldsymbol{mu})z,quad boldsymbol{mu}^{2mathrm{D}}=Kfrac{E_tboldsymbol{mu}}{d},quad r^{2mathrm{D}}=frac{fr}{d}.
颜色:C(mathbf{p})=sumnmathbf{c}_if_i(mathbf{p})prod_{j=1}(1-f_j(mathbf{p})),
深度:D(mathbf{p})=sum_{i=1}nd_if_i(mathbf{p})prod_{i=1}(1-f_j(mathbf{p})),
轮廓:S(mathbf{p})=sum_{i=1}nf_i(mathbf{p})prod_{i=1}(1-f_j(mathbf{p})).
end{array}
$$
假设有一个现有的地图(通过一组3D高斯函数表示),该地图已根据一组相机帧1到t进行拟合。给定一个新的RGB-D帧t+1,splaTAM执行以下步骤:
对于第一帧,跳过跟踪步骤,并将相机姿势设置为单位阵。在致密化步骤中,由于渲染的轮廓是空的,因此所有像素都用于初始化新的高斯。具体来说,对于每个像素,添加一个新的高斯,其颜色为该像素的颜色,中心位于该像素深度的反投影位置,不透明度为0.5,半径如下初始化,保证了一个gaussian投影到图像后仅覆盖一个像素:
$$
r=frac{D_{GT}}{f}.
$$
首先,确定第t+1帧的相机位姿的初值:
$$
E_{t+1}=E_{t}+(E_{t}-E_{t-1}).
$$
然后,计算损失并使用梯度下降算法优化:
$$
L_mathfrak{t}=sum_{mathbf{p}}left(S(mathbf{p})>0.99right)left(mathcal{L}_1left(D(mathbf{p})right)+0.5mathcal{L}_1left(C(mathbf{p})right)right).
$$
通过使用轮廓图,splaTAM仅从地图优化良好的部分计算损失,这对于跟踪新的相机姿势非常重要,因为新帧通常包含尚未良好建模的新gaussian,如果计算了这部分gaussian,那么带来的loss是非常大的。
splaTAM会根据每个传入的在线帧,在地图中初始化新的gaussian。但是,我们并不想在重建良好的区域再添加新的gaussian,因此,splaTAM是根据轮廓图,在建图不良好区域重新初始化。初始化gaussian的策略如上所述。具体会往哪些区域添加gaussian由下式决定:
$$
begin{array}{c}
M(mathbf{p}) =left(S(mathbf{p})
left(D_{mathrm{GT}}(mathbf{p})
mathrm{MDE}表示medianquad depth quad error,即这张图片上深度误差的均值。
lambda是超参数,经验上设置为50。
end{array}
$$
以上这个掩码指示,在未建图区域S(p)和重建质量差的区域去初始化新的gaussian。
更新gaussian的过程我们同样已经很熟悉了,就是3DGS的优化过程。具体而言,splaTAM系统利用给定迄今为止相机pose的集合,更新3D高斯地图的参数。这是通过可微渲染和基于梯度的优化完成的,但是与tracking不同,在mapping中,相机pose是固定的,gaussian的参数被更新。
除此之外,splaTAM做了两个重要的修改。第一,优化不是从头开始,而是从最近构建的地图开始优化。第二,也不直接选择所有先前的(关键)帧,而是选择尽可能包含新gaussian的帧。具体选择方案是:第n帧为关键帧,一共选择k帧进行优化,包括当前帧、最近的关键帧以及与当前帧重叠度最高的k-2个先前关键帧。重叠度计算方式为:一,获取当前帧深度图的点云;二,确定关键帧的视锥体内的包含了多少个上述点,包含的点越多的关键帧优先被选择。
此阶段使用与跟踪期间类似的损失,但不使用轮廓图进行筛选,因为需要利用所有像素进行优化。此外,splaTAM在体渲染中添加了结构相似性(SSIM)损失,并剔除不透明度接近0或太大的无用高斯,类似3DGS的剪枝策略。
GS-SLAM同样是使用3DGS进行场景重建的SLAM方法,与splaTAM不同的是,GS-SLAM完成了单目SLAM的任务,而不是splaTAM的RGB-D输入,因此,GS-SLAM需要来自SFM估计的准确位姿。GS-SLAM也可以无缝拓展到RGB-D的SLAM。此外,通过利用gaussian的显式性质,GS-SLAM引入几何验证和正则化来处理gaussian在densify中出现的歧义。
GS-SLAM的贡献为:
GS-SLAM用的是原始的计算方案,没有使用像splaTAM那样的球形高斯。
$$
begin{array}{c}
第i个gaussian:mathcal{G}{i},颜色:ci,着色度:alpha^i,
中心坐标:boldsymbol{mu}{W}{i},协方差矩阵:boldsymbol{Sigma}_{W}。
mathcal{C}=sum_{i in mathcal{N}} c_{i} alpha_{i} prod_{j=1}^{i-1}left(1-alpha_{j}right)
end{array}
$$
投影到像素坐标系:
$$
begin{array}{c}
boldsymbol{mu}{I}=pi left(mathbf{T} cdot boldsymbol{mu}{W}right),boldsymbol{Sigma}=boldsymbol{J}boldsymbol{W}boldsymbol{Sigma}{W}boldsymbol{W}{T}boldsymbol{J},
pi 表示相机坐标系到像素坐标系的投影操作,
boldsymbol{J}是投影变换的线性近似的雅克比矩阵,
boldsymbol{W}是mathbf{T}的旋转部分。
end{array}
$$
为了实现准确的跟踪,GS-SLAM通常需要每帧至少50次梯度下降迭代。为了避免自动微分的开销,同样使用CUDA实现光栅化,并为所有显式计算的参数提供导数。由于光栅化对性能至关重要,因此GS-SLAM明确地推导了相机位姿的雅可比矩阵。
$$
begin{aligned}
& frac{partialboldsymbol{mu}I}{partialboldsymbol{T}{CW}}=frac{partialboldsymbol{mu}_I}{partialboldsymbol{mu}C}frac{mathcal{D}boldsymbol{mu}C}{mathcal{D}boldsymbol{T}{CW}},
& frac{partialboldsymbol{Sigma}I}{partialboldsymbol{T}{CW}}=frac{partialboldsymbol{Sigma}I}{partialmathbf{J}}frac{partialmathbf{J}}{partialboldsymbol{mu}C}frac{mathcal{D}boldsymbol{mu}C}{mathcal{D}boldsymbol{T}{CW}}+frac{partialboldsymbol{Sigma}I}{partialmathbf{W}}frac{mathcal{D}mathbf{W}}{mathcal{D}boldsymbol{T}{CW}},
& frac{mathcal{D}boldsymbol{mu}C}{mathcal{D}boldsymbol{T}{CW}}=
begin{bmatrix}
boldsymbol{I} & -boldsymbol{mu}C^times
end{bmatrix},frac{mathcal{D}mathbf{W}}{mathcal{D}boldsymbol{T}{CW}}=
begin{bmatrix}
boldsymbol{0} & -mathbf{W}^times
boldsymbol{0} & -mathbf{W}^times
boldsymbol{0} & -mathbf{W}^times
end{bmatrix}.
end{aligned}
$$
跟踪时,所用的loss如下:
$$
begin{array}{c}
深度图渲染:mathcal{D}p=sum{iinmathcal{N}}z_ialpha_iprod_{j=1}^{i-1}(1-alpha_j) ,
光度损失:E_{pho}=left|I(mathcal{G},boldsymbol{T}{CW})-bar{I}right|1 ,
几何损失:E=left|D(mathcal{G},boldsymbol{T})-bar{D}right|1 .
loss = lambdaE_{pho}+(1-lambda_{pho})E_{geo} .
几何损失只在RGB-D输入时启用。
z_i表示boldsymbol{mu}_{W}^{i}的z坐标,I就是渲染得出的RGB图。
end{array}
$$
直接使用视频流中的所有图像来在线联合优化高斯和相机姿势是不可行的,因此需要维护一组基于帧间共视性精心选择的关键帧。理想的一组关键帧将选择观察同一区域的不同视角,以提供更好的多视图约束。
根据以上这个标准,只需通过测量当前帧i和最后一个关键帧j之间观察到的gaussian的并集来衡量共可见性。如果共可见性下降到阈值以下,亦或者如果gaussian的相对平移大于深度的中值,则这一帧i就被标记为关键帧。有添加就有删除,为了提高效率,系统仅维护少量关键帧。如果某一关键帧与最新关键帧的共可见性低于阈值,则删除这一关键帧。
衡量一个gaussian的可见性非常容易,只需要计算在体渲染中沿着光线累计的不透明度,只要不透明度小于0.5,那么此前的gaussian都视为可见;如果超过0.5,那么此后的gaussian视为不可见。
如果使用RGB-D的图像,那么新增的gaussian可以直接使用深度进行反投影从而初始化。在单目情况下,对于具有深度估计的像素,则根据低方差的深度值进行初始化;对于没有深度估计的像素,则在渲染图中使用这些gaussian深度的中值的邻近值初始化新的gaussian。
在单目情况下,许多新插入的gaussian的位置是不正确的。大多数在优化过程中会很快消失,因为它们违反了多视图一致性,系统还通过检查关键帧集合中的可见性来进一步修剪多余的gaussian。如果最后3个关键帧中插入的gaussian未被至少3个其他帧观察到,就将它们修剪掉,因为它们在几何上不稳定。
建图的目的是保持一致的3D结构并优化新插入的gaussian。在建图过程中,关键帧集合用于重建当前可见区域。此外,每次迭代都会选择两个随机的过去关键帧以避免忘记全局地图。
splaTAM直接使用了球形gaussian,而GS-SLAM也有类似的做法,就是引入各向同性正则化,促进gaussian的球形度,从而避免了gaussian因拉长而产生伪影的问题:
$$
E_{text {iso }}=sum_{i=1}^{|mathcal{G}|}left|mathbf{s}{i}-tilde{mathbf{s}{i}} cdot mathbf{1}right|{1} ,
min{C W}^{k} in boldsymbol{S E}(3), mathcal{G}, forall k in mathcal{W}}} sum{forall k in mathcal{W}} E_{p h o}^{k}+lambda_{i s o} E_{i s o}
$$
如果深度值可用,那么深度损失也会加入总loss。
本文由博客一文多发平台 OpenWrite 发布!
参与评论
手机查看
返回顶部