DROID-SLAM和Raft(ECCV2020 Best Paper)的通讯都是ImageNet的一作,给跪了。
从dense mapping的角度来看,DROID-SLAM采用 ”缝合预测光流 + DBA + Upsample“的情况,极大的提高了一个预训练模型在各个场景的泛化性(相比于估深度的网络)。
从Localization的角度来看,与特征点法VSLAM的区别是:信息来源上完整的使用了1/8降采样后的RGB信息,使用预训练模型预测光流从而丢掉了特征匹配过程;与直接法VSLAM的区别是:预测光流的模块可以支持全局BA,直接法VSLAM时间距离较大的两帧之间没法Global BA;
正经的特征点法的BA流程是根据估计的特征点深度和相机位姿计算相应颜色的warping loss然后反过来优化特征点的深度和相机位姿。DROID-SLAM的流程是不求RGB的损失,而是根据RGB+RAFT求出光流偏差r+像素权重w,然后根据权重求光流损失优化降采样光流图的损失,从而优化降采样深度图和相机位姿。
文章目录
一、高斯牛顿法二、BA与图优化(后端,非线性优化)2.1 Schur消元
三、DROID-SLAM,NIPS20213.1 PVFDM(Derivate from DROID-SLAM)3.2 DROID-SLAM正文3.3 DROID-SLAM代码3.4 DROID-SLAM在TartanAir_AbandonFactory和KITTI
DROID-SLAM论文链接:https://arxiv.org/abs/2108.10869DROID-SLAM代码repo:https://github.com/princeton-vl/DROID-SLAM
一、高斯牛顿法
对于最小二乘问题:
m
i
n
(
1
2
∣
∣
f
(
x
)
∣
∣
2
2
)
m
i
n
(
1
2
∣
∣
f
(
x
+
Δ
x
)
∣
∣
2
2
)
=
m
i
n
(
1
2
∣
∣
f
(
x
)
+
J
(
x
)
T
Δ
x
∣
∣
2
2
)
=
[
f
(
x
)
+
J
(
x
)
T
Δ
x
]
T
⋅
[
f
(
x
)
+
J
(
x
)
T
Δ
x
]
=
∣
∣
f
(
x
)
∣
∣
2
2
+
f
(
x
)
T
J
(
x
)
T
Δ
x
+
Δ
x
T
J
(
x
)
f
(
x
)
+
Δ
x
T
J
(
x
)
J
(
x
)
T
Δ
x
min( \frac{1}{2}||f(x)||_2^2 )\\ min( \frac{1}{2}||f(x+\Delta x)||_2^2 ) \\ = min(\frac{1}{2}||f(x) + J(x)^T \Delta x||_2^2) \\ = [f(x) + J(x)^T \Delta x]^T \cdot [f(x) + J(x)^T \Delta x] \\ = ||f(x)||_2^2 + f(x)^T J(x)^T \Delta x + \Delta x^T J(x)f(x) + \Delta x^TJ(x)J(x)^T\Delta x
min(21∣∣f(x)∣∣22)min(21∣∣f(x+Δx)∣∣22)=min(21∣∣f(x)+J(x)TΔx∣∣22)=[f(x)+J(x)TΔx]T⋅[f(x)+J(x)TΔx]=∣∣f(x)∣∣22+f(x)TJ(x)TΔx+ΔxTJ(x)f(x)+ΔxTJ(x)J(x)TΔx
对
Δ
x
\Delta x
Δx求导:
f
(
x
)
J
(
x
)
+
J
(
x
)
J
(
x
)
T
Δ
x
=
0
f(x)J(x) + J(x)J(x)^T \Delta x = 0
f(x)J(x)+J(x)J(x)TΔx=0
记作:
H
(
x
)
:
=
J
(
x
)
J
(
x
)
T
g
(
x
)
:
=
−
f
(
x
)
J
(
x
)
H
(
x
)
Δ
x
=
g
(
x
)
H(x) := J(x) J(x)^T\\ g(x) := -f(x)J(x)\\ H(x) \Delta x = g(x)
H(x):=J(x)J(x)Tg(x):=−f(x)J(x)H(x)Δx=g(x)
即高斯牛顿法把
J
J
T
J J^T
JJT当作海森矩阵的近似,我们把上面的方程称作增量方程或者高斯牛顿方程。
高斯牛顿法的步骤:
给定初始值
x
0
x_0
x0。 对于第
k
k
k次迭代,求出当前的雅可比矩阵
J
(
x
k
)
J(x_k)
J(xk)和误差
f
(
x
k
)
f(x_k)
f(xk)。 求解增量方程:
H
Δ
x
k
=
g
H \Delta x_k = g
HΔxk=g。 如果
Δ
x
k
\Delta x_k
Δxk足够小,则停止,否则返回第2步。
二、BA与图优化(后端,非线性优化)
参考下:https://max.book118.com/html/2019/0307/5201144303002014.shtm
假设有路标点
p
p
p,
T
T
T代表从世界坐标系到像素坐标系的投影,
h
(
T
i
,
p
j
)
h(T_i,p_j)
h(Ti,pj)代表观测模型,即把第
j
j
j个路标点投影到第
i
i
i个像素坐标系下,
z
i
j
z_{ij}
zij代表第
i
i
i个像素坐标系下拍到的第
j
j
j个路标点。
那么对于
m
m
m张图观测
n
n
n个路标点并进行BA的目标函数可以写成:
1
2
∑
i
=
1
m
∑
j
=
1
n
∣
∣
e
i
j
(
x
)
∣
∣
2
2
=
1
2
∑
i
=
1
m
∑
j
=
1
n
∣
∣
z
i
j
−
h
(
T
i
,
p
j
)
∣
∣
2
2
\frac{1}{2} \sum_{i=1}^m \sum_{j=1}^n||e_{ij}(x)||_2^2 \\ = \frac{1}{2} \sum_{i=1}^m \sum_{j=1}^n ||z_{ij} - h(T_i,p_j)||_2^2
21i=1∑mj=1∑n∣∣eij(x)∣∣22=21i=1∑mj=1∑n∣∣zij−h(Ti,pj)∣∣22
对于上面的问题,只有
z
i
j
z_{ij}
zij是观测到的数据,我们把其他的全都当作待优化的变量:
x
=
[
T
1
,
.
.
.
,
T
m
,
p
1
,
.
.
.
,
p
n
]
T
=
[
x
c
T
,
x
p
T
]
x = [T_1,...,T_m,p_1,...,p_n]^T = [x_c^T,x_p^T]
x=[T1,...,Tm,p1,...,pn]T=[xcT,xpT]
1
2
∑
i
=
1
m
∑
j
=
1
n
∣
∣
e
i
j
(
x
+
Δ
x
)
∣
∣
2
2
=
1
2
∑
i
=
1
m
∑
j
=
1
n
∣
∣
e
i
j
+
J
T
Δ
x
∣
∣
2
=
1
2
∑
i
=
1
m
∑
j
=
1
n
∣
∣
e
i
j
+
F
Δ
x
c
+
E
Δ
x
p
∣
∣
2
\frac{1}{2} \sum_{i=1}^m \sum_{j=1}^n||e_{ij}(x+\Delta x)||_2^2 \\ = \frac{1}{2} \sum_{i=1}^m \sum_{j=1}^n ||e_{ij} + J^T \Delta x||^2 \\ = \frac{1}{2} \sum_{i=1}^m \sum_{j=1}^n || e_{ij} + F \Delta x_c + E \Delta x_p||^2
21i=1∑mj=1∑n∣∣eij(x+Δx)∣∣22=21i=1∑mj=1∑n∣∣eij+JTΔx∣∣2=21i=1∑mj=1∑n∣∣eij+FΔxc+EΔxp∣∣2
记
e
i
j
e_{ij}
eij对于路标点
p
j
p_j
pj的偏导为
E
i
j
E_{ij}
Eij,记
e
i
j
e_{ij}
eij对相机位姿
T
i
T_i
Ti的偏导为
F
i
j
F_{ij}
Fij,
J
T
=
[
F
,
E
]
J^T=[F,E]
JT=[F,E]。
求一下增量方程:
H
=
J
J
T
=
[
F
T
F
F
T
E
E
T
F
E
T
E
]
H = J J^T = \begin{bmatrix} F^TF & F^T E \\ E^T F & E^T E \end{bmatrix} \\
H=JJT=[FTFETFFTEETE]
Δ
x
=
H
−
1
g
\Delta x = H^{-1}g
Δx=H−1g,如果路标点很多观测位姿也很多,计算
H
H
H矩阵的逆需要巨大的计算量,21世纪的VSLAM的一个重要进步是意识到了
H
H
H矩阵的稀疏结构,并发现该结构可以自然,显示的用图优化来表示。
J
i
j
(
x
)
=
[
0
,
.
.
.
0
,
∂
e
i
j
∂
T
i
,
0
,
.
.
.
0
∣
0
,
.
.
.
,
0
,
∂
e
i
j
∂
p
j
,
0
,
.
.
.
,
0
]
J_{ij}(x) = [0,...0,\frac{\partial e_{ij}}{\partial T_{i}},0,...0 |0,...,0,\frac{\partial e_{ij}}{\partial p_{j}},0,...,0]
Jij(x)=[0,...0,∂Ti∂eij,0,...0∣0,...,0,∂pj∂eij,0,...,0]
一般情况下,路标点的数量
n
n
n远大于相机数量
m
m
m,因此左上角非常小,整个矩阵看起来很像箭头,又叫箭头形矩阵。对于这种稀疏矩阵求逆的问题,常用 Schur消元,这个过程也被叫做 Marginalization(边缘化)。
2.1 Schur消元
我们把
H
H
H 矩阵的左上角,右上角,左下角,右下角的四个矩阵依次记作:
(
B
,
E
,
E
T
,
C
)
(B, E, E^T, C)
(B,E,ET,C)
因此增量方程写作:
H
(
x
)
:
=
J
(
x
)
J
(
x
)
T
[
v
w
]
=
g
(
x
)
:
=
−
f
(
x
)
J
(
x
)
H(x) := J(x) J(x)^T\\ \begin{bmatrix} v \\ w \end{bmatrix} = g(x) := -f(x)J(x)\\
H(x):=J(x)J(x)T[vw]=g(x):=−f(x)J(x)
[
B
E
E
T
C
]
[
Δ
x
c
Δ
x
p
]
=
[
v
w
]
\begin{bmatrix} B & E \\ E^T & C \end{bmatrix} \begin{bmatrix} \Delta x_c \\ \Delta x_p \end{bmatrix} = \begin{bmatrix} v \\ w \end{bmatrix}
[BETEC][ΔxcΔxp]=[vw]
B
B
B是对角块矩阵,每个块的维度是
c
c
c的维度,
C
C
C也是对角块矩阵,每个块的维度是
3
×
3
3 \times 3
3×3 (
p
p
p的维度)。对角块矩阵求逆的难度远小于一般的矩阵,只是需要每个对角块矩阵分别求逆即可。通过消元将
H
H
H矩阵变成对角块矩阵先:
[
I
−
E
C
−
1
0
I
]
[
B
E
E
T
C
]
[
Δ
x
c
Δ
x
p
]
=
[
I
−
E
C
−
1
0
I
]
[
v
w
]
⇒
[
B
−
E
C
−
1
E
T
0
E
T
C
]
[
Δ
x
c
Δ
x
p
]
=
[
v
−
E
C
−
1
w
w
]
\begin{bmatrix} I & -EC^{-1} \\ 0 & I \end{bmatrix} \begin{bmatrix} B & E \\ E^T & C \end{bmatrix} \begin{bmatrix} \Delta x_c \\ \Delta x_p \end{bmatrix} = \begin{bmatrix} I & -EC^{-1} \\ 0 & I \end{bmatrix} \begin{bmatrix} v \\ w \end{bmatrix} \\ \Rightarrow \\ \begin{bmatrix} B-EC^{-1}E^T & 0 \\ E^T & C \end{bmatrix} \begin{bmatrix} \Delta x_c \\ \Delta x_p \end{bmatrix} = \begin{bmatrix} v -EC^{-1}w \\ w \end{bmatrix}
[I0−EC−1I][BETEC][ΔxcΔxp]=[I0−EC−1I][vw]⇒[B−EC−1ETET0C][ΔxcΔxp]=[v−EC−1ww]
解出
Δ
x
c
\Delta x_c
Δxc之后代到下面,下面就是新的对角块矩阵,然后解出
Δ
x
p
\Delta x_p
Δxp就完事了。
[
B
−
E
C
−
1
E
T
]
Δ
x
c
=
v
−
E
C
−
1
w
[B-EC^{-1}E^T ] \Delta x_c = v - E C^{-1}w
[B−EC−1ET]Δxc=v−EC−1w
定义:
S
:
=
[
B
−
E
C
−
1
E
T
]
S := [B-EC^{-1}E^T ]
S:=[B−EC−1ET]
三、DROID-SLAM,NIPS2021
3.1 PVFDM(Derivate from DROID-SLAM)
按照 “PVFDM,Probabilistic Volumetric Fusion for Dense Monocular SLAM, WACV2023” 的说法,先用上面的BA + 高斯牛顿法 拿到
1
8
\frac{1}{8}
81 resolution 的深度图(对于Euroc数据集是
69
×
44
512
×
384
\frac{69 \times 44}{512 \times 384}
512×38469×44),然后用一个 learnable upsample operation 来 recover full resolution 的深度图。
(这个上采样方法是抄作业的 “Raft,ECCV2020 Best Paper” )
传统方法是 “选择特征点 + sparse-depth BA”, 但是PVFDM发现 “降采样版本depth + full-depth BA + Raft做UpSample”
3.2 DROID-SLAM正文
传统的那一套基于BA优化的formulation的好处在于可能拓展到多模态传感器。
DROID-SLAM的评价是Learning-Based的SLAM系统鲁棒性较好,但是它的精度还远远不如传统算法。
RAFT是输入两帧RGB迭代的优化光流,DROID-SLAM是输入arbitrary数量的RGB,迭代的优化深度和位姿。(Joint global refinement of all camera poses and depth maps对于消除累计飘移和回环检测至关重要)
Each update of camera poses and depth maps in DROID-SLAM is produced by a differentiable Dense Bundle Adjustment (DBA) layer, which computes a Gauss-Newton update to camera poses and dense per-pixel depth so as to maximize their compatibility with the current estimate of optical flow.
DROID-SLAM中可学习的参数有光流预测部分(包括特征提取和ConvGRU)+ 上采样部分,本身只需要光流真值做监督,但是加了DBA嵌入SLAM后又用预测的pose做了额外监督。训练出的来网络仍然只是做光流预测,为后面的DBA优化提供观测值。可以认为Droid-SLAM就是用RAFT光流代替传统直接法SLAM(如DSO)中的传统光流的SLAM。
DROID-SLAM是在四卡3090上TartanAir上训练了一周然后拿得到的 “Extractor+ConvGRU” 在各种数据集上infer,泛化能力强的主要原因还是在DBA这套传统方法上我感觉。
我们的方法在双3090上实时,第一张卡跑Tracking和Local BA,第二张卡跑global BA和Loop Closure。后端由于要存储feature map,所以比较花费显存。
消溶试验证明full BA很有用捏。
GRU输入的光流是啥? 前端work之前都用零,前端work了(凑够八张)之后,用估的光流。 相比于输入RGB,输入为RGBD时候,DBA是怎么样的形式? 原文是这样说的:“In the case of RGB-D, we still treat depth as a variable, since sensor depth can be noisy and have missing observations, and simply add a term to the optimization objective.”
3.3 DROID-SLAM代码
Python和Pytorch基础知识
__setitem__(index, value): 索引赋值重写,即调用 Obj[index]=value等价于Obj.__setitem__(index, value) torch.autograd.Function.apply 是 PyTorch 中一个用于定义自定义自动微分函数的方法。在 PyTorch 中,所有神经网络的核心是 torch.autograd.Function。这是一个抽象类,定义了所有向用户定义的函数应如何工作的基本方法。 torch.autograd.Function 的 apply 方法是一个实现自动微分的关键部分。它允许你定义自己的函数,并指定如何计算函数的正向和反向传播。 class CorrSampler(torch.autograd.Function):
@staticmethod
def forward(ctx, volume, coords, radius):
ctx.save_for_backward(volume,coords)
ctx.radius = radius
corr, = droid_backends.corr_index_forward(volume, coords, radius)
return corr
@staticmethod
def backward(ctx, grad_output):
volume, coords = ctx.saved_tensors
grad_output = grad_output.contiguous()
grad_volume, = droid_backends.corr_index_backward(volume, coords, grad_output, ctx.radius)
return grad_volume, None, None
python的setuptools,setup函数负责将自己写好的cpp或者cu代码封装好,以便python脚本直接调用。 (具体教程见:https://blog.csdn.net/ygf666/article/details/127797494) 如果想查看某个API到底做啥操作,一旦您用 C++ 和 ATen 编写了计算,可以使用 pybind11 以非常简单的方式将 C++ 函数或类衔接到 Python 中。 from setuptools import setup
from torch.utils.cpp_extension import BuildExtension, CUDAExtension
import os.path as osp
ROOT = osp.dirname(osp.abspath(__file__))
setup(
name='droid_backends',
ext_modules=[
CUDAExtension('droid_backends',
include_dirs=[osp.join(ROOT, 'thirdparty/eigen')],
sources=[
'src/droid.cpp',
'src/droid_kernels.cu',
'src/correlation_kernels.cu',
'src/altcorr_kernel.cu',
],
extra_compile_args={
'cxx': ['-O3'],
'nvcc': ['-O3',
'-gencode=arch=compute_60,code=sm_60',
'-gencode=arch=compute_61,code=sm_61',
'-gencode=arch=compute_70,code=sm_70',
'-gencode=arch=compute_75,code=sm_75',
'-gencode=arch=compute_80,code=sm_80',
'-gencode=arch=compute_86,code=sm_86',
]
}),
],
cmdclass={ 'build_ext' : BuildExtension }
)
pybind找到cpp函数和python函数的对应关系 PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
// bundle adjustment kernels
m.def("ba", &ba, "bundle adjustment");
m.def("frame_distance", &frame_distance, "frame_distance");
m.def("projmap", &projmap, "projmap");
m.def("depth_filter", &depth_filter, "depth_filter");
m.def("iproj", &iproj, "back projection");
// correlation volume kernels
m.def("altcorr_forward", &altcorr_forward, "ALTCORR forward");
m.def("altcorr_backward", &altcorr_backward, "ALTCORR backward");
m.def("corr_index_forward", &corr_index_forward, "INDEX forward");
m.def("corr_index_backward", &corr_index_backward, "INDEX backward");
}
⭐代码结构图: DROIDSLAM的DBA全都是CUDA写的,封装到droid_backend.ba函数中。 图片太大了,没法上传,URL:https://github.com/Promethe-us/URL_Album/blob/main/202309/DROID-SLAM.png
3.4 DROID-SLAM在TartanAir_AbandonFactory和KITTI
推荐文章
发表评论