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

21​i=1∑m​j=1∑n​∣∣eij​(x)∣∣22​=21​i=1∑m​j=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

21​i=1∑m​j=1∑n​∣∣eij​(x+Δx)∣∣22​=21​i=1∑m​j=1∑n​∣∣eij​+JTΔx∣∣2=21​i=1∑m​j=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=[FTFETF​FTEETE​]

Δ

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}

[BET​EC​][Δ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​][BET​EC​][Δxc​Δxp​​]=[I0​−EC−1I​][vw​]⇒[B−EC−1ETET​0C​][Δ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

推荐文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: