个人学习笔记,翻译自原始论文
g2o: a general framework for graph optimization
摘要
在机器人和计算机视觉领域的许多流行问题如SLAM和BA,都可以转化为对于误差函数的最小二乘优化问题,其误差函数可以通过图这种数据结构进行表达。本文描述了面对该问题的一种通用的结构,并且提供了开源的c++库g2o用于优化基于图的非线性误差函数。我们的系统被设计成为能够轻易的扩展到其他问题上并且对于一个新的问题,仅仅需要自定义几行代码去描述该问题即可。当前的实现提供了几种不同的SLAM和BA问题的解决方案。我们也提供了大量在真实世界和仿真环境中的数据集用以评估算法性能。这些评估表明,即使g2o提供了一个通用的解决方案,其在特定问题上性能依旧可以和最先进的专用算法相提并论。
简介
大量的机器人学和计算机视觉问题都牵涉到最小化一个可以用图来表达的非线性误差函数,如SLAM和BA。总的来说,这些问题都希望尽可能的从一系列受到高斯白噪声影响测量数据中估计真实的状态参数。例如对于基于图的slam问题来说,其状态变量是机器人在环境中的位置和机器人能观测到的地标点的位置。因此一个传感器测量数据仅仅依赖于和它相关联的两个状态变量中的位置信息。里程计测量信息也仅仅和其前后两次的状态变量中的位置信息相关。同样,对于一个地图点的位置也仅仅和能够看到它的那几帧对应的相机位姿相关。
所有的这些问题都可以用图来表达。其中每一个节点都代表了一个待优化的变量,连接两个顶点的每一条边都代表顶点之间的一次观测。在文献中,对于这样一个经典的问题提出了许多的方法去解决,例如有高斯-牛顿法、L-M算法,或者梯度下降的方法也能够在大多数情况下解决该问题。然而需要实现这些算法并达到良好的效果需要大量的知识储备。
在本篇文章中,我们针对这种可以用图表达的非线性最小二乘问题提出了一种通用的解决方案。我们命名为g2o。图1中给出了能够采用g2o作为后端解决的不同问题的一个概述。该系统能够和最先进的专用系统的性能所媲美,并且可以用于一般的通用系统。我们通过如下的算法实现高效性:
- 利用了图连接的稀疏性
- 利用了在上述问题中图的特殊性
- 利用先进的方法求解稀疏的线性系统
- 利用现代处理器的特性如SIMD指令优化内存的使用。
除了高效性,g2o非常的通用且易于扩展:
一个2D SLAM算法可以用少于30行的c++代码描述。用户只需要制定误差函数和他的参数即可。
在这篇文章中,我们将g2o应用于不同类别的最小二乘问题的求解并和对应的专用求解算法进行对比。我们展示了大量的真实和仿真数据用以评估算法,并和对应领域最先进算法进行对比,在一些情况下甚至超过了专用算法的性能。
论文接下来的结构如下:我们首先讨论了特别针对SLAM问题或者BA问题的求解方法。其次在第三节我们描述了我们提出的系统是如何处理基于图的最小二乘问题,并讨论通过LM算法或者GN算法的实现。在第四节中,我们讨论了系统的特性,最后在第五节我们将g2o系统和最先进的专用的方法进行对比。
相关工作
略(参考意义不大)
使用最小二乘的非线性图优化
许多在机器人和计算机视觉中的问题可以通过求解如下函数的最小值来解决:
F ( x ) = ∑ ? i , j ? ∈ C e ( x i , x j , z i j ) ? Ω i j e ( x i , x j , z i j ) ? F i j \mathbf{F}(\mathbf{x})=\sum_{\langle i, j\rangle \in \mathcal{C}} \underbrace{\mathbf{e}\left(\mathbf{x}_{i}, \mathbf{x}_{j}, \mathbf{z}_{i j}\right)^{\top} \mathbf{\Omega}_{i j} \mathbf{e}\left(\mathbf{x}_{i}, \mathbf{x}_{j}, \mathbf{z}_{i j}\right)}_{\mathbf{F}_{i j}} F(x)=?i,j?∈C∑?Fij?
e(xi?,xj?,zij?)?Ωij?e(xi?,xj?,zij?)??
x ? = argmin ? x F ( x ) \mathbf{x}^{*}=\underset{\mathbf{x}}{\operatorname{argmin}} \mathbf{F}(\mathbf{x}) x?=xargmin?F(x)
这里的 x = ( x 1 T , ? , x n T ) T x=(x_1^T,\cdots ,x_n^T)^T x=(x1T?,?,xnT?)T是一个参数向量,其中每一个 x i x_i xi?代表着一个通用的参数块, z i j z_{ij} zij?和 Ω i j \Omega _{ij} Ωij?代表观测的均值和 x i x_i xi?和 x j x_j xj?之间的信息矩阵。 e ( x i , x j , z i j ) e(x_i,x_j,z_{ij}) e(xi?,xj?,zij?)是一个向量误差函数描述了 x i x_i xi?和 x j x_j xj?符合约束条件 z i j z_{ij} zij?的程度。当其为零时代表完美的符合了约束条件。
为了简化记号,在论文的接下来部分将使用如下的简化符号代表误差函数:
e ( x i , x j , z i j ) = def. e i j ( x i , x j ) = def. e i j ( x ) \mathbf{e}\left(\mathbf{x}_{i}, \mathbf{x}_{j}, \mathbf{z}_{i j}\right) \stackrel{\text { def. }}{=} \mathbf{e}_{i j}\left(\mathbf{x}_{i}, \mathbf{x}_{j}\right) \stackrel{\text { def. }}{=} \mathbf{e}_{i j}(\mathbf{x}) e(xi?,xj?,zij?)= def. eij?(xi?,xj?)= def. eij?(x)
注意到每一个误差函数,每一个参数块都可以跨越不同的空间。一个这样的问题能够通过有向图结构高效的表达。图中一个节点代表着一个参数块 x i x_i xi?,节点 i i i和 j j j之间的一条边代表着在两个参数块之间的有序约束。图二中是对建图过程的图描述。
A.最小二乘优化
如果已知参数的一个良好的初始值 x ? \breve{x} x?,便可以用流行的LM算法或G-N算法对其进行数值求解。其主要的思想在于使用误差函数的在初始位置的泰勒展开来近似误差函数。
e i j ( x ? i + Δ x i , x ? j + Δ x j ) = e i j ( x ? + Δ x ) ? e i j + J i j Δ x \begin{aligned} \mathbf{e}_{i j}\left(\breve{\mathbf{x}}_{i}+\Delta \mathbf{x}_{i}, \breve{\mathbf{x}}_{j}+\Delta \mathbf{x}_{j}\right) &=\mathbf{e}_{i j}(\breve{\mathbf{x}}+\Delta \mathbf{x}) \\ & \simeq \mathbf{e}_{i j}+\mathbf{J}_{i j} \Delta \mathbf{x} \end{aligned} eij?(x?i?+Δxi?,x?j?+Δxj?)?=eij?(x?+Δx)?eij?+Jij?Δx?
在这里, J i j J_{ij} Jij?是对 e i j ( x ) e_{ij}(x) eij?(x)计算得到的雅克比矩阵。
定义残差项如下:
F i j ( x ? + Δ x ) = e i j ( x ? + Δ x ) ? Ω i j e i j ( x ? + Δ x ) ? ( e i j + J i j Δ x ) ? Ω i j ( e i j + J i j Δ x ) = e i j ? Ω i j e i j ? c i j + 2 e i j ? Ω i j J i j ? b i j Δ x + Δ x ? J i j ? Ω i j J i j ? H i j Δ x = c i j + 2 b i j Δ x + Δ x ? H i j Δ x \begin{array}{l} \mathbf{F}_{i j}(\breve{\mathbf{x}}+\Delta \mathbf{x}) \\ =\mathbf{e}_{i j}(\breve{\mathbf{x}}+\Delta \mathbf{x})^{\top} \mathbf{\Omega}_{i j} \mathbf{e}_{i j}(\breve{\mathbf{x}}+\Delta \mathbf{x}) \\ \simeq\left(\mathbf{e}_{i j}+\mathbf{J}_{i j} \Delta \mathbf{x}\right)^{\top} \mathbf{\Omega}_{i j}\left(\mathbf{e}_{i j}+\mathbf{J}_{i j} \Delta \mathbf{x}\right) \\ =\underbrace{\mathbf{e}_{i j}^{\top} \mathbf{\Omega}_{i j} \mathbf{e}_{i j}}_{\mathrm{c}_{i j}}+2 \underbrace{\mathbf{e}_{i j}^{\top} \mathbf{\Omega}_{i j} \mathbf{J}_{i j}}_{\mathbf{b}_{i j}} \mathbf{\Delta} \mathbf{x}+\mathbf{\Delta} \mathbf{x}^{\top} \underbrace{\mathbf{J}_{i j}^{\top} \mathbf{\Omega}_{i j} \mathbf{J}_{i j}}_{\mathbf{H}_{i j}} \mathbf{\Delta} \mathbf{x} \\ =\mathbf{c}_{i j}+2 \mathbf{b}_{i j} \mathbf{\Delta} \mathbf{x}+\mathbf{\Delta} \mathbf{x}^{\top} \mathbf{H}_{i j} \mathbf{\Delta} \mathbf{x} \end{array} Fij?(x?+Δx)=eij?(x?+Δx)?Ωij?eij?(x?+Δx)?(eij?+Jij?Δx)?Ωij?(eij?+Jij?Δx)=cij?
eij??Ωij?eij???+2bij?
eij??Ωij?Jij???Δx+Δx?Hij?
Jij??Ωij?Jij???Δx=cij?+2bij?Δx+Δx?Hij?Δx?
这只是对其中一个约束的残差项,对图中所有约束的残差项需要将所有的残差求和。
F ( x ? + Δ x ) = ∑ ? i , j ? ∈ C F i j ( x ? + Δ x ) ? ∑ ? i , j ? ∈ C c i j + 2 b i j Δ x + Δ x ? H i j Δ x = c + 2 b ? Δ x + Δ x ? H Δ x \begin{aligned} \mathbf{F}(\breve{\mathbf{x}}+\Delta \mathbf{x}) &=\sum_{\langle i, j\rangle \in \mathcal{C}} \mathbf{F}_{i j}(\breve{\mathbf{x}}+\Delta \mathbf{x}) \\ & \simeq \sum_{\langle i, j\rangle \in \mathcal{C}} \mathrm{c}_{i j}+2 \mathbf{b}_{i j} \Delta \mathbf{x}+\Delta \mathbf{x}^{\top} \mathbf{H}_{i j} \Delta \mathbf{x} \\ &=\mathrm{c}+2 \mathbf{b}^{\top} \mathbf{\Delta} \mathbf{x}+\mathbf{\Delta} \mathbf{x}^{\top} \mathbf{H} \mathbf{\Delta} \mathbf{x} \end{aligned} F(x?+Δx)?=?i,j?∈C∑?Fij?(x?+Δx)??i,j?∈C∑?cij?+2bij?Δx+Δx?Hij?Δx=c+2b?Δx+Δx?HΔx?
其中, c = ∑ c i j c= \sum{c_{ij}} c=∑cij?, b = ∑ b i j b= \sum{b_{ij}} b=∑bij?, H = ∑ H i j H= \sum{H_{ij}} H=∑Hij?.因此可以通过求解如下线性方程得到使误差最小的 Δ x \Delta x Δx。
H Δ x ? = ? b \mathbf{H} \boldsymbol{\Delta} \mathbf{x}^{*}=-\mathbf{b} HΔx?=?b
H \mathbf{H} H矩阵是系统的信息矩阵,在初始值的基础上加上求解的增量即可得到下一次的迭代初始值。
x ? = x ? + Δ x ? \mathbf{x}^{*}=\breve{\mathbf{x}}+\Delta \mathbf{x}^{*} x?=x?+Δx?
G-N法就是通过不断在当前点求解 Δ x ? \Delta x^* Δx?来迭代 x ? x^* x?从而收敛到局部最优值。
LM算法在G-N算法的基础上引入了一个阻尼系统用来控制算法的收敛速度。
( H + λ I ) Δ x ? = ? b (\mathbf{H}+\lambda \mathbf{I}) \boldsymbol{\Delta} \mathbf{x}^{*}=-\mathbf{b} (H+λI)Δx?=?b
这里的 λ \lambda λ是一个阻尼系数,其中 λ \lambda λ越大前进的距离 Δ x \Delta x Δx越小。这在强非线性表面的情况下能够能够有效的控制步长。(避免步长太长,线性化后的误差太大)LM算法的主要思想就是如何动态的根据需要调节阻尼系数。在每一次迭代过程中会监视线性化误差函数与真实误差函数的误差。如果当前迭代比上一次的误差要小,说明模型能够较好的近似,于是在下一次迭代中减小 λ \lambda λ的值以便快速搜索。否则将会减小 λ \lambda λ的值并带回原式重新计算。
B.可选的参数化
程序中系统希望在面对不同函数最小化问题时能够采用一种通用的方法。他们假设参数空间是欧几里德的,但是这并不满足SLAM或则BA等问题的求解。为了处理状态变量在非欧几里德空间中的情况,一种通用的做法是在不同的参数空间中表达不同的参数。
例如,在上文中提到的SLAM问题中,每一个参数块中包含平移向量 t i t_i ti?和旋转参数 α i \alpha_i αi?。其中平移向量是在欧几里德空间中的,而旋转参数是在非欧几里德空间中,如 S O ( 3 ) , S O ( 2 ) SO(3),SO(2) SO(3),SO(2)空间中。为了避免奇异性,这些空间常常采用超参数的方法描述,如四元数或旋转矩阵。如果直接将超参数带入原算法中,会使得计算结果不满足超参数自带的约束条件。为了避免这个问题,可以采用旋转的最小表达如欧拉角,但是欧拉角又会有奇异点。
现在的一个思路是,在当前位置对参数 x ? \breve{x} x?进行一个扰动,来求得增量 Δ x \Delta x Δx。其增量 Δ x \Delta x Δx采用最小参数的表达方式,而当前的位置 x ? \breve{x} x?依旧采用超参数的方法描述。由于增量一般都很小,离奇异点非常的远,因此不用考虑奇异点的问题。新的状态参数 x ? x^* x?可以通过一种算子田 : Dom ? ( x i ) × Dom ? ( Δ x i ) → Dom ? ( x i ) : \operatorname{Dom}\left(\mathbf{x}_{i}\right) \times \operatorname{Dom}\left(\boldsymbol{\Delta} \mathbf{x}_{i}\right) \rightarrow \operatorname{Dom}\left(\mathbf{x}_{i}\right) :Dom(xi?)×Dom(Δxi?)→Dom(xi?)的非线性方式进行更新(就是SO(3)的左\右扰动求导方法):
x i ? = x ? i 田 Δ x i ? \mathbf{x}_{i}^{*}=\breve{\mathbf{x}}_{i} \text { 田 } \boldsymbol{\Delta} \mathbf{x}_{i}^{*} xi??=x?i? 田 Δxi??
例如在3D SLAM中可以采用四元数来表达位姿,但是更新量却是通过角速度向量来表达。然后通过四元数的跟新规则跟新当前状态(复杂公式太难打了,参考李群、李代数中如何通过对旋转矩阵采用扰动的方法求导即可理解。就是解决非欧空间内参数不能直接加,一个旋转矩阵加上一个旋转矩阵肯定不是旋转矩阵,那么如何求对应的雅可比,如何对参数跟新的问题)
C.线性系统的结构
对于 H \textbf{H} H矩阵和向量 b \textbf{b} b是所有约束的一系列矩阵和向量的和。其中, b i j = J i j ? Ω i j e i j \mathbf{b}_{i j}=\mathbf{J}_{i j}^{\top} \mathbf{\Omega}_{i j} \mathbf{e}_{i j} bij?=Jij??Ωij?eij?, H i j = J i j ? Ω i j J i j \mathbf{H}_{i j}=\mathbf{J}_{i j}^{\top} \mathbf{\Omega}_{i j} \mathbf{J}_{i j} Hij?=Jij??Ωij?Jij?,然后定义 H \textbf{H} H矩阵和向量 b \textbf{b} b如下:
b = ∑ ? i , j ? ∈ C b i j H = ∑ ? i , j ? ∈ C H i j \mathbf{b}=\sum_{\langle i, j\rangle \in \mathcal{C}} \mathbf{b}_{i j} \quad \mathbf{H}=\sum_{\langle i, j\rangle \in \mathcal{C}} \mathbf{H}_{i j} b=?i,j?∈C∑?bij?H=?i,j?∈C∑?Hij?
对于每一个约束的误差都会用加法的形式构成系统整体的约束。经过累加之后的矩阵或者向量的结构取决于误差函数的雅可比结构。因为每一个约束仅仅和它连接的两个节点有关系,因此雅可比的大部分地方都是零,只有相关的两个节点对应的地方才不为零,表现为如下的结构。
J i j = ( 0 ? 0 A i j ? i 0 ? 0 B i j ? j 0 ? 0 ) \mathbf{J}_{i j}=(\mathbf{0} \cdots \mathbf{0} \underbrace{\mathbf{A}_{i j}}_{i} \mathbf{0} \cdots \mathbf{0} \underbrace{\mathbf{B}_{i j}}_{j} \mathbf{0} \cdots \mathbf{0}) Jij?=(0?0i
Aij???0?0j
Bij???0?0)
这里的 A i j \textbf{A}_{ij} Aij?和 B i j \textbf{B}_{ij} Bij?分别代表误差函数 e i j e_{ij} eij?分别对节点i和节点j进行求导结果,可以表达为 Δ x i \Delta x_i Δxi?和 Δ x j \Delta x_j Δxj?.然后根据雅可比只有两项非零的特点得到矩阵 H i j H_{ij} Hij?和向量 b i j b_{ij} bij?的表达式。
H i j = ( ? A i j ? Ω i j A i j ? A i j ? Ω i j B i j ? ? B i j ? Ω i j A i j ? B i j ? Ω i j B i j ? ) b i j = ( ? A i j ? Ω i j e i j ? B i j ? Ω i j e i j ? ) \mathbf{H}_{i j}=\left(\begin{array}{cccc} \ddots & & & \\ & \mathbf{A}_{i j}^{\top} \mathbf{\Omega}_{i j} \mathbf{A}_{i j} & \cdots & \mathbf{A}_{i j}^{\top} \mathbf{\Omega}_{i j} \mathbf{B}_{i j} & \\ & \vdots & & \vdots & \\ & \mathbf{B}_{i j}^{\top} \mathbf{\Omega}_{i j} \mathbf{A}_{i j} & \cdots & \mathbf{B}_{i j}^{\top} \mathbf{\Omega}_{i j} \mathbf{B}_{i j} & \\ & & & &\ddots \end{array}\right) \mathbf{b}_{i j}=\left(\begin{array}{c} \vdots \\ \mathbf{A}_{i j}^{\top} \mathbf{\Omega}_{i j} \mathbf{e}_{i j} \\ \vdots \\ \mathbf{B}_{i j}^{\top} \mathbf{\Omega}_{i j} \mathbf{e}_{i j} \\ \vdots \end{array}\right) Hij?=??????????Aij??Ωij?Aij??Bij??Ωij?Aij?????Aij??Ωij?Bij??Bij??Ωij?Bij????????????bij?=???????????Aij??Ωij?eij??Bij??Ωij?eij?????????????
除了矩阵中给出的四项和向量中给出的两项,其他都为零,对于单个约束来说是非常稀疏的结构。即使在累加后,H矩阵依旧是稀疏的,因此将会利用其稀疏特性进行简化计算。
D.稀疏矩阵的处理
对于特定的应用如BA问题等,H矩阵拥有一些更加特殊的结构性质,我们将利用这些矩阵结构特性提高算法的性能。对于BA问题,需要求解的状态参数分为两类相机的位姿p和地图点的位置l。将参数分为两个部分看,然后对求解方程进行分块
( H p p H p l H p l ? H l l ) ( Δ x p ? Δ x l ? ) = ( ? b p ? b l ) \left(\begin{array}{cc} \mathbf{H}_{\mathbf{p p}} & \mathbf{H}_{\mathbf{p l}} \\ \mathbf{H}_{\mathbf{p l}}^{\top} & \mathbf{H}_{\mathrm{ll}} \end{array}\right)\left(\begin{array}{l} \mathbf{\Delta} \mathbf{x}_{\mathbf{p}}^{*} \\ \mathbf{\Delta} \mathbf{x}_{\mathbf{l}}^{*} \end{array}\right)=\left(\begin{array}{l} -\mathbf{b}_{\mathbf{p}} \\ -\mathbf{b}_{\mathbf{l}} \end{array}\right) (Hpp?Hpl???Hpl?Hll??)(Δxp??Δxl???)=(?bp??bl??)
对这样一个分块系统去舒克补的方式可以简化计算,通过舒克补操作得到两个方程
( H p p ? H p l H l l ? 1 H p l ? ) Δ x p ? = ? b p + H p l H l 1 ? 1 b l \left(\mathbf{H}_{\mathbf{p p}}-\mathbf{H}_{\mathbf{p} \mathbf{l}} \mathbf{H}_{\mathbf{l} \mathbf{l}}^{-1} \mathbf{H}_{\mathbf{p l}}^{\top}\right) \Delta \mathbf{x}_{\mathbf{p}}^{*}=-\mathbf{b}_{\mathbf{p}}+\mathbf{H}_{\mathbf{p} \mathbf{l}} \mathbf{H}_{\mathbf{l} 1}^{-1} \mathbf{b}_{\mathbf{l}} (Hpp??Hpl?Hll?1?Hpl??)Δxp??=?bp?+Hpl?Hl1?1?bl?
H l l Δ x 1 ? = ? b 1 ? H p l ? Δ x p ? \mathbf{H}_{\mathrm{ll}} \Delta \mathbf{x}_{1}^{*}=-\mathbf{b}_{1}-\mathbf{H}_{\mathrm{pl}}^{\top} \Delta \mathbf{x}_{\mathbf{p}}^{*} Hll?Δx1??=?b1??Hpl??Δxp??
第一个方程可以求得 x p ? \mathbf{x}_{\mathbf{p}}^{*} xp??,然后带入到第二个方程就可以求出 x 1 ? \mathbf{x}_{1}^{*} x1??。这样求解无需对整个大矩阵进行求逆的操作,而是转换成对分块小矩阵求逆,在稀疏矩阵的条件下能极大的降低运算复杂度。
实现
有关实现问题在g2o源码中给出的一份文档介绍的更为详细
其他
g2o中为了提高对outlier的鲁棒性,添加了一个Huber鲁棒核函数,但是没有在论文里面提及。