李群数学推导及Opencv代码实现

本文主要是一些李群基本原理的整理和基于Opencv实现了一个李代数的接口,主要用于学习和不想配置Eigen的CVer。

1. 李群与李代数

1.1 李群的定义

  1. 三维空间中的旋转矩阵构成了特殊正交群(SO(3));而变换矩阵构成了特殊欧式群(SE(3))。通常会被表示成如下形式:
    S O ( 3 ) = { R ∈ R 3 × 3 ∣ R R T = I , d e t ( R ) = 1 } S E ( 3 ) = C = { [ R T 0 T 1 ] ∈ R 4 × 4 ∣ R ∈ S O ( 3 ) , T ∈ R 3 } SO(3)=\{R \in \mathbb{R^{3 \times 3}}|RR^T=I,det(R)=1\} \\ SE(3)=C=\{\begin{bmatrix}R & T \\ \mathbf{0}^T & 1 \end{bmatrix} \in \mathbb{R^{4 \times 4}}|R \in SO(3),T \in \mathbb{R}^3\} SO(3)={RR3×3RRT=I,det(R)=1}SE(3)=C={[R0TT1]R4×4RSO(3),TR3}
  2. 群(Group)是一种运算+一种集合的代数结构。我们把运算记为 ⋅ \cdot
    (1) 封闭性:集合中任意两个元素做运算得到的结果还属于这个集合。
    (2) 结合律:对于集合中任意三个元素A 、B、C;均满足 A ⋅ ( B ⋅ C ) = ( A ⋅ B ) ⋅ C A\cdot (B\cdot C)=(A\cdot B)\cdot C A(BC)=(AB)C
    (3) 幺元:集合中存在一个元素,任何元素跟他做运算都等于他本身(SE(3)和SO(3)的幺元就是单位矩阵)。
    (4) 可逆:集合中的元素和他的逆做运算等于幺元。
    上述可以记为 “凤姐咬你”
  3. 进一步的具有连续(光滑)性质的群就叫做李群,像整数群那样离散没有连续性质的群就不是李群。而SO(n)和SE(n)在实数空间上是连续的(可以直观的理解为一个刚体在空间上可以连续运动),所以它们都是李群

1.2 李代数

1.2.1 李代数的定义

每个李群都有与之对应的李代数。李代数描述了李群的局部性质,准确地说是单位元附近的正切空间。李代数由一个集合 V \mathbb{V} V,一个数域 F \mathbb{F} F和一个二元运算组成 [ , ] [,] [,]。如果他们满足一下几条性质,则称 ( V , F , [ , ] ) (\mathbb{V}, \mathbb{F}, [,]) (V,F,[,])为一个李代数。

  1. 封闭性: ∀ X , Y ∈ V ; [ X , Y ] ∈ V \forall X,Y \in\mathbb{V}; [X, Y] \in\mathbb{V} XYV;[X,Y]V

  2. 双线性: ∀ X , Y , Z ∈ V , a , b ∈ F \forall X, Y, Z \in\mathbb{V}, a, b \in\mathbb{F} X,Y,ZVa,bF 有:
    [ a X + b Y , Z ] = a [ X , Z ] + b [ Y , Z ] [aX+bY, Z]=a[X, Z]+b[Y,Z] [aX+bY,Z]=a[X,Z]+b[Y,Z], [ Z , a X + b Y ] = a [ Z , X ] + b [ Z , Y ] [Z, aX+bY]=a[Z, X]+b[Z,Y] [Z,aX+bY]=a[Z,X]+b[Z,Y]

  3. 自反性: ∀ X , [ X , X ] = 0 \forall X, [X, X]=0 X,[X,X]=0

  4. 雅可比等价: ∀ X , Y , Z ∈ V , [ X , [ Y , Z ] ] + [ Z , [ X , Y ] ] + [ Y , [ Z , X ] ] = 0 \forall X, Y, Z \in\mathbb{V},[X,[Y,Z]]+[Z, [X, Y]]+[Y,[Z, X]]=0 X,Y,ZV,[X,[Y,Z]]+[Z,[X,Y]]+[Y,[Z,X]]=0
    其中的二元运算被称作李括号。它不要求结合律,而要求元素和自己做运算为零的性质。一个简单的例子,三维向量 R 3 \mathbb{R}^3 R3和运算叉积构成了一李代数。

1.2.2 SO(3)对应的李代数

SO(3)对应的李代数是定义在 R 3 \mathbb{R}^3 R3上的向量 w w w,每一个 w w w都可以生成一个反对称矩阵 W W W其本质是向量叉乘得矩阵表达形式)。
W = w ∧ = [ 0 − w 3 w 2 w 3 0 − w 1 − w 2 w 1 0 ] W=w^{\wedge}= \begin{bmatrix} 0 & -w_3 & w_2 \\ w_3 & 0 & -w_1 \\ -w_2 & w_1 & 0 \\ \end{bmatrix} W=w= 0w3w2w30w1w2w10
在此种定义下,两个向量 w 1 w_1 w1 w 2 w_2 w2的李括号为( − ∧ {-\wedge} 代表 ∧ \wedge 逆运算):
[ w 1 , w 2 ] = ( W 1 W 2 − W 2 W 1 ) − ∧ [w_1, w_2]=(W_1W_2-W_2W_1)^{-\wedge} [w1,w2]=(W1W2W2W1)−∧
他跟SO(3)的对应关系由指数映射(Exponential Map)给出:
R = exp ⁡ ( w ∧ ) R=\exp(w^{\wedge}) R=exp(w)

1.2.3 SE(3)对应的李代数

对于SE(3),其同样也有对应的李代数,其是一个六维向量 ρ \rho ρ(其在机器人学领域被称作旋量(twist)):
ρ = [ v w ] \rho = \begin{bmatrix} v\\ w \end{bmatrix} ρ=[vw]
它的前三维是平移(但与SE(3)上对应的平移意义不一样),记为 v v v;后三维是旋转,记为 w w w,本质就是SO(3)所对应的李代数。同样我们使用 ∧ {\wedge} 表示为将六维向量 ρ \rho ρ转成4维矩阵 ρ m \rho^m ρm,用 − ∧ -{\wedge} 表示逆运算:
ρ m = ρ ∧ = [ w ∧ v 0 T 0 ] \rho^m=\rho^{\wedge}= \begin{bmatrix} w^{\wedge}& v \\ 0^T & 0\\ \end{bmatrix} ρm=ρ=[w0Tv0]
对应的李括号可以定义为:
[ ρ 1 m , ρ 2 m ] = ( ρ 1 m ρ 2 m − ρ 2 m ρ 1 m ) − ∧ [\rho^m_1, \rho^m_2]=(\rho^m_1\rho^m_2-\rho^m_2\rho^m_1)^{-\wedge} [ρ1m,ρ2m]=(ρ1mρ2mρ2mρ1m)−∧
他跟SE(3)的对应关系也可以由指数映射给出:
[ R T 0 T 1 ] = exp ⁡ ( ρ ∧ ) \begin{bmatrix} R& T\\ 0^T & 1\\ \end{bmatrix}=\exp(\rho^{\wedge}) [R0TT1]=exp(ρ)

1.3 SO(3)上的指数映射

接下来首先推导一下SO(3)所对应的指数映射,考虑一个旋转矩阵(SO(3))R,由于其为正交矩阵,则其满足如下:
R T R = I R^TR=I RTR=I
现在我们说R是某个相机的旋转,它会随时间而变化,即为时间的函数: R ( t ) R(t) R(t),由于其仍然为旋转矩阵,则依然满足:
R ( t ) T R ( t ) = I R(t)^TR(t)=I R(t)TR(t)=I
在等式两边同时对时间求导:
R ˙ ( t ) T R ( t ) + R ( t ) T R ˙ ( t ) = 0 \dot{R}(t)^TR(t)+R(t)^T\dot{R}(t)=0 R˙(t)TR(t)+R(t)TR˙(t)=0
整理得:
R ( t ) T R ˙ ( t ) = − ( R ( t ) T R ˙ ( t ) ) T R(t)^T\dot{R}(t)=-(R(t)^T\dot{R}(t))^T R(t)TR˙(t)=(R(t)TR˙(t))T
可以看到 R ˙ ( t ) T R ( t ) \dot{R}(t)^TR(t) R˙(t)TR(t)是一个反对称矩阵(反对称满足 A = − A T A=-A^T A=AT),根据前文的定义不妨取 R ( t ) T R ˙ ( t ) = w ∧ R(t)^T\dot{R}(t)=w^{\wedge} R(t)TR˙(t)=w,则:
R ˙ ( t ) = R ( t ) w ∧ \dot{R}(t)=R(t)w^{\wedge} R˙(t)=R(t)w
注意:也可以从 R R T = I RR^T=I RRT=I推导出 R ˙ ( t ) = w ∧ R ( t ) \dot{R}(t)=w^{\wedge}R(t) R˙(t)=wR(t)的结果,这两没有本质的区别,只是形式不同,左乘和由乘分别是全局坐标系和局部坐标系的不同表示(因为左乘是外旋,右乘是内旋?)。
根据高数中的定义, R ˙ ( t ) = R ( t ) w ∧ \dot{R}(t)=R(t)w^{\wedge} R˙(t)=R(t)w是一个关于 R R R的一阶常微分方程,其可以写成如下形式:
d R ( t ) d t = R ( t ) w ∧ \frac{dR(t)}{dt}=R(t)w^{\wedge} dtdR(t)=R(t)w
分离变量法:
1 R ( t ) d R ( t ) = w ∧ d t \frac{1}{R(t)}dR(t)=w^{\wedge}dt R(t)1dR(t)=wdt
对两侧进行积分:
ln ⁡ ( R ( t ) ) = w ∧ t \ln(R(t))=w^{\wedge}t ln(R(t))=wt
最后得:
R ( t ) = exp ⁡ ( w ∧ t ) R(t)=\exp(w^{\wedge}t) R(t)=exp(wt)
公式 R ( t ) = exp ⁡ ( w ∧ t ) R(t)=\exp(w^{\wedge}t) R(t)=exp(wt)便是指数映射。现在考虑下一个问题一个矩阵的指数 exp ⁡ ( w ∧ t ) \exp(w^{\wedge}t) exp(wt)如何计算?

首先我们可以对矩阵形式的指数 exp ⁡ ( w ∧ t ) \exp(w^{\wedge}t) exp(wt)进行泰勒展开:
exp ⁡ ( w ∧ ) = ∑ n = 0 ∞ 1 n ! ( w ∧ ) n \exp(w^{\wedge})=\sum_{n=0}^{\infty}\frac{1}{n!}(w^{\wedge})^n exp(w)=n=0n!1(w)n
上述公式没法直接计算,因为我们无法直接计算矩阵的无穷次幂。由于w是一个三维向量,因此我们定义它的模长和方向,分别记作 θ \theta θ a a a。这里 a a a是一个长度为1的方向向量,即 ∣ ∣ a ∣ ∣ = 1 ||a||=1 ∣∣a∣∣=1。首先对于 a ∧ a^{\wedge} a有如下两条性质:
a ∧ a ∧ = [ − a 2 2 − a 3 2 a 1 a 2 a 1 a 3 a 1 a 2 − a 1 2 − a 3 2 a 2 a 3 a 1 a 3 a 2 a 3 − a 1 2 − a 2 2 ] = a a T − I a^{\wedge}a^{\wedge}=\begin{bmatrix} -a_2^2-a_3^2 & a_1a_2 & a_1a_3 \\ a_1a_2 & -a_1^2-a_3^2 & a_2a_3 \\ a_1a_3 & a_2a_3 & -a_1^2-a_2^2 \\ \end{bmatrix}=aa^T-I aa= a22a32a1a2a1a3a1a2a12a32a2a3a1a3a2a3a12a22 =aaTI
以及:
a ∧ a ∧ a ∧ = a ∧ ( a a T − I ) = − a ∧ a^{\wedge}a^{\wedge}a^{\wedge}=a^{\wedge}(aa^T-I)=-a^{\wedge} aaa=a(aaTI)=a
注:回顾一下之前的定义 a ∧ a^{\wedge} a是向量叉乘的矩阵形式。因此 a ∧ a a^{\wedge}a aa相当于向量和自己做叉乘,结果为0。

以上两个公式提供了处理 a ∧ a^{\wedge} a高阶项的方法。基于此,我们可以把指数映射写成如下形式:
exp ⁡ ( w ∧ ) = exp ⁡ ( θ a ∧ ) = ∑ n = 0 ∞ 1 n ! ( θ a ∧ ) n = I + θ a ∧ + 1 2 ! ( θ a ∧ ) 2 + 1 3 ! ( θ a ∧ ) 3 + 1 4 ! ( θ a ∧ ) 4 + . . . . . . = a a T − a ∧ a ∧ + θ a ∧ + 1 2 ! ( θ a ∧ ) 2 − 1 3 ! θ 3 a ∧ − 1 4 ! θ 4 ( a ∧ ) 2 + . . . . . . = a a T + ( θ − 1 3 ! θ 3 + . . . . . . ) a ∧ − ( 1 − 1 2 ! θ 2 + . . . . . . ) a ∧ a ∧ = a ∧ a ∧ + I + sin ⁡ ( θ ) a ∧ − cos ⁡ ( θ ) a ∧ a ∧ = ( 1 − cos ⁡ ( θ ) ) a ∧ a ∧ + sin ⁡ ( θ ) a ∧ + I = cos ⁡ ( θ ) I + ( 1 − cos ⁡ ( θ ) ) a a T + sin ⁡ ( θ ) a ∧ \exp(w^{\wedge})=\exp(\theta a^{\wedge})=\sum_{n=0}^{\infty}\frac{1}{n!}(\theta a^{\wedge})^n \\[10pt] =I+\theta a^{\wedge}+\frac{1}{2!}(\theta a^{\wedge})^2+\frac{1}{3!}(\theta a^{\wedge})^3+\frac{1}{4!}(\theta a^{\wedge})^4+...... \\[10pt] =aa^T-a^{\wedge}a^{\wedge}+\theta a^{\wedge}+\frac{1}{2!}(\theta a^{\wedge})^2-\frac{1}{3!}\theta^3 a^{\wedge}-\frac{1}{4!}\theta^4 (a^{\wedge})^2+...... \\[10pt] = aa^T+(\theta -\frac{1}{3!}\theta^3+......)a^{\wedge}-(1-\frac{1}{2!}\theta^2+......)a^{\wedge}a^{\wedge} \\[10pt] =a^{\wedge}a^{\wedge}+I+\sin(\theta)a^{\wedge}-\cos(\theta)a^{\wedge}a^{\wedge} \\[10pt] =(1-\cos(\theta))a^{\wedge}a^{\wedge}+\sin(\theta)a^{\wedge}+I \\[10pt] = \cos(\theta)I+(1-\cos(\theta))aa^T+\sin(\theta)a^{\wedge} exp(w)=exp(θa)=n=0n!1(θa)n=I+θa+2!1(θa)2+3!1(θa)3+4!1(θa)4+......=aaTaa+θa+2!1(θa)23!1θ3a4!1θ4(a)2+......=aaT+(θ3!1θ3+......)a(12!1θ2+......)aa=aa+I+sin(θ)acos(θ)aa=(1cos(θ))aa+sin(θ)a+I=cos(θ)I+(1cos(θ))aaT+sin(θ)a
注: ( θ − 1 3 ! θ 3 + . . . . . . ) (\theta -\frac{1}{3!}\theta^3+......) (θ3!1θ3+......) ( 1 − 1 2 ! θ 2 + . . . . . . ) (1-\frac{1}{2!}\theta^2+......) (12!1θ2+......)分别为 cos ⁡ ( θ ) \cos(\theta) cos(θ) sin ⁡ ( θ ) \sin(\theta) sin(θ)的泰勒展开式。

最后我们得到了如下公式:
e x p ( w ∧ ) = cos ⁡ ( θ ) I + ( 1 − cos ⁡ ( θ ) ) a a T + sin ⁡ ( θ ) a ∧ exp(w^{\wedge})=\cos(\theta)I+(1-\cos(\theta))aa^T+\sin(\theta)a^{\wedge} exp(w)=cos(θ)I+(1cos(θ))aaT+sin(θ)a
这便是大名鼎鼎的罗德里格斯公式(Rodrigues),其用于将旋转向量转化为旋转矩阵。这表明SO(3)所对应的李代数的物理意义就是旋转向量,而指数映射就说罗德里格斯公式。 反之如果我们定义对数映射,也可以把李代数映射到李群。指数映射是一个满射而不是单射,这意味着每个SO(3)都有与之对应的李代数;但是可能存在多个李代数的元素对应同一个SO(3)。但是,如果我们把 θ \theta θ的范围定位为 ± π \pm\pi ±π的范围内,那么李群和李代数就是一一对应的。李群和李代数的关系与旋转矩阵和旋转向量的关系是相似的,他们的对应关系都由罗德里格斯公式给出,旋转矩阵的导数可以由旋转向量指定,指导着如何在旋转矩阵间进行微积分运算。

1.3 SE(3)上的指数映射

接下来,我们推导一下SE(3)上的指数映射,首先根据前文可知,有如下公式:
[ R T 0 T 1 ] = exp ⁡ ( ρ ∧ ) \begin{bmatrix} R& T\\ 0^T & 1\\ \end{bmatrix}=\exp(\rho^{\wedge}) [R0TT1]=exp(ρ)
我们同样对上述公式进行泰勒展开:
exp ⁡ ( ρ ∧ ) = exp ⁡ ( [ w ∧ v 0 T 0 ] ) = ∑ n = 0 ∞ 1 n ! ( [ w ∧ v 0 T 0 ] ) n = I + [ w ∧ v 0 T 0 ] + 1 2 ! [ w ∧ v 0 T 0 ] 2 + 1 3 ! [ w ∧ v 0 T 0 ] 3 + . . . . . . = I + [ w ∧ v 0 T 0 ] + 1 2 ! [ w ∧ 2 w ∧ v 0 T 0 ] + 1 3 ! [ w ∧ 3 w ∧ 2 v 0 T 0 ] + . . . . . . \exp(\rho^{\wedge})=\exp( \begin{bmatrix} w^{\wedge}& v \\ 0^T & 0\\ \end{bmatrix})=\sum_{n=0}^{\infty}\frac{1}{n!}(\begin{bmatrix} w^{\wedge}& v \\ 0^T & 0\\ \end{bmatrix})^n \\[10pt] = I + \begin{bmatrix} w^{\wedge}& v \\ 0^T & 0\\ \end{bmatrix}+\frac{1}{2!}\begin{bmatrix} w^{\wedge}& v \\ 0^T & 0\\ \end{bmatrix}^2+\frac{1}{3!}\begin{bmatrix} w^{\wedge}& v \\ 0^T & 0\\ \end{bmatrix}^3+...... \\[10pt] =I + \begin{bmatrix} w^{\wedge}& v \\ 0^T & 0\\ \end{bmatrix}+\frac{1}{2!}\begin{bmatrix} {w^{\wedge}}^2& w^{\wedge}v \\ 0^T & 0\\ \end{bmatrix}+\frac{1}{3!}\begin{bmatrix} {w^{\wedge}}^3& {w^{\wedge}}^2v \\ 0^T & 0\\ \end{bmatrix}+...... \\[10pt] exp(ρ)=exp([w0Tv0])=n=0n!1([w0Tv0])n=I+[w0Tv0]+2!1[w0Tv0]2+3!1[w0Tv0]3+......=I+[w0Tv0]+2!1[w20Twv0]+3!1[w30Tw2v0]+......
其可以被简化为如下形式:
exp ⁡ ( [ w ∧ v 0 T 0 ] ) = [ exp ⁡ ( w ∧ ) J v 0 T 1 ] J = I + 1 2 ! w ∧ + 1 3 ! ( w ∧ ) 2 + . . . . . . \exp( \begin{bmatrix} w^{\wedge}& v \\ 0^T & 0\\ \end{bmatrix}) =\begin{bmatrix} \exp(w^{\wedge})& Jv \\ 0^T & 1\\ \end{bmatrix} \\[10pt] J=I+\frac{1}{2!}w^{\wedge}+\frac{1}{3!}(w^{\wedge})^2+...... exp([w0Tv0])=[exp(w)0TJv1]J=I+2!1w+3!1(w)2+......
从上述公式看SE(3)的指数映射的旋转部分与SO(3)保持一致,而平移部分多了个系数 J J J。我们可使用前文推导SO(3)指数映射所用的几条性质( a ∧ a ∧ = a a T − I , a ∧ a ∧ a ∧ = − a ∧ a^{\wedge}a^{\wedge}=aa^T-I,a^{\wedge}a^{\wedge}a^{\wedge}=-a^{\wedge} aa=aaTIaaa=a),继续化简J。首先同样将 w ∧ w^{\wedge} w表达成一个模长乘一个单位向量的形式 θ a \theta a θa
J = I + 1 2 ! θ a + 1 3 ! ( θ a ) 2 + . . . . . . = I + 1 θ ( 1 2 ! θ 2 − 1 4 ! θ 4 + . . . ) a ∧ + 1 θ ( 1 3 ! θ 3 − 1 5 ! θ 5 + . . . ) a ∧ a ∧ = 1 θ ( 1 − cos ⁡ ( θ ) ) a ∧ + θ − sin ⁡ ( θ ) θ ( a a T − I ) + I = sin ⁡ ( θ ) θ I + ( 1 − sin ⁡ ( θ ) θ ) a a T + 1 − cos ⁡ ( θ ) θ a ∧ J=I+\frac{1}{2!}\theta a+\frac{1}{3!}(\theta a)^2+...... \\[10pt] = I+\frac{1}{\theta}(\frac{1}{2!}\theta^2-\frac{1}{4!}\theta^4+...)a^{\wedge}+\frac{1}{\theta}(\frac{1}{3!}\theta^3-\frac{1}{5!}\theta^5+...)a^{\wedge}a^{\wedge} \\[10pt] =\frac{1}{\theta}(1-\cos(\theta))a^{\wedge}+\frac{\theta-\sin(\theta)}{\theta}(aa^T-I)+I \\[10pt] =\frac{\sin(\theta)}{\theta}I+(1-\frac{\sin(\theta)}{\theta})aa^T+\frac{1-\cos(\theta)}{\theta}a^{\wedge} J=I+2!1θa+3!1(θa)2+......=I+θ1(2!1θ24!1θ4+...)a+θ1(3!1θ35!1θ5+...)aa=θ1(1cos(θ))a+θθsin(θ)(aaTI)+I=θsin(θ)I+(1θsin(θ))aaT+θ1cos(θ)a
不同的其他文献中也有采用 w ∧ w^{\wedge} w,采用如下表达式表示 J J J
J = I + 1 − cos ⁡ ( θ ) θ 2 w ∧ + θ − sin ⁡ ( θ ) θ 3 w ∧ w ∧ J=I+\frac{1-\cos(\theta)}{\theta^2}w^{\wedge}+\frac{\theta-\sin(\theta)}{\theta^3}w^{\wedge}w^{\wedge} J=I+θ21cos(θ)w+θ3θsin(θ)ww
以上就是SE(3)上完整的指数映射推导过程,同样该过程也有逆运算对数映射不过我们通常不采用对数映射求解李代数,SE(3)的旋转部分,可以通过计算他的旋转向量得到;而平移部分由于 t = J v t=Jv t=Jv,且 J J J可以由旋转向量得到,我们只需要直接求解 t = J v t=Jv t=Jv这个线性方程就能够得到 v v v。并且SE(3)上的李代数同样可以表征SE(3)导数性质(瞬时状态)

2. 李群下的伴随性质(Adjoint)

接下来通过一个简单的机器人学应用问题,引出李群下的伴随性质。考虑一个经典的眼在手上的手眼标定问题,其可以被定义为如下公式:
A X = X B AX=XB AX=XB
其中A的物理意义是机器人工具坐标系下单位时间内的 Δ t o o l \Delta tool Δtool X X X的物理意义是相机在机器人工具坐标系下的位置和姿态 B B B的物理意义是相机坐标系下单位时间内的 Δ c a m e r a \Delta camera Δcamera(手眼标定问题不是本文的重点,因此不对上述公式做详细推导)。因此上述公式可以写成如下的形式:
d T t o o l d t X = X d T c a m e r a d t \frac{dT_{tool}}{dt}X=X\frac{dT_{camera}}{dt} dtdTtoolX=XdtdTcamera
根据前文的介绍我们可以知道SE(3)上的李代数 ρ \rho ρ可以表示SE(3)的导数性质,因此上述公式可以被进一步的写成如下形式:
ρ t o o l ∧ X = X ρ c a m e r a ∧ ρ t o o l ∧ = X ρ c a m e r a ∧ X − 1 \rho^{\wedge}_{tool}X=X\rho^{\wedge}_{camera} \\ \rho^{\wedge}_{tool}=X\rho^{\wedge}_{camera}X^{-1} ρtoolX=Xρcameraρtool=XρcameraX1
将上述公式进一步的抽象为如下形式:
ρ 2 ∧ = C 12 ρ 1 ∧ C 12 − 1 \rho^{\wedge}_{2}=C_{12}\rho^{\wedge}_{1}C_{12}^{-1} ρ2=C12ρ1C121
其中 C 12 C_{12} C12代表坐标系1到坐标系2的刚体变换, ρ 1 ∧ \rho^{\wedge}_{1} ρ1 ρ 2 ∧ \rho^{\wedge}_{2} ρ2分别代表坐标系1和坐标系2下的6D速度(旋量)。这组公式也被称作李群上的共轭变换。
根据前文对刚体变换 C C C和李代数 ρ \rho ρ的定义,共轭变换可以进行如下化简:
[ w 2 ∧ v 2 0 T 0 ] = [ R T 0 T 1 ] [ w 1 ∧ v 1 0 T 0 ] [ R − 1 − R − 1 T 0 T 1 ] = [ R w 1 ∧ R v 1 0 T 0 ] [ R − 1 − R − 1 T 0 T 1 ] = [ R w 1 ∧ R − 1 − R w 1 ∧ R − 1 T + R v 1 0 T 0 ] \begin{bmatrix} w_2^{\wedge}& v_2 \\ 0^T & 0\\ \end{bmatrix}=\begin{bmatrix} R& T\\ 0^T & 1\\ \end{bmatrix}\begin{bmatrix} w_1^{\wedge}& v_1\\ 0^T & 0\\ \end{bmatrix}\begin{bmatrix} R^{-1}& -R^{-1}T\\ 0^T & 1\\ \end{bmatrix} \\[10pt] =\begin{bmatrix} Rw_1^{\wedge}& Rv_1 \\ 0^T & 0\\ \end{bmatrix}\begin{bmatrix} R^{-1}& -R^{-1}T\\ 0^T & 1\\ \end{bmatrix} \\[10pt] =\begin{bmatrix} Rw_1^{\wedge}R^{-1}& -Rw_1^{\wedge}R^{-1}T+Rv_1\\ 0^T & 0\\ \end{bmatrix} [w20Tv20]=[R0TT1][w10Tv10][R10TR1T1]=[Rw10TRv10][R10TR1T1]=[Rw1R10TRw1R1T+Rv10]
为了进一步化简上述公式,先推导一个小结论 R w ∧ = R w ∧ R − 1 Rw^{\wedge}=Rw^{\wedge}R^{-1} Rw=RwR1,考虑有一个向量 V V V,有:
R w ∧ v = R w ∧ R R − 1 v (正交矩阵满足 R R − 1 = I ) = ( R w ) × ( R R − 1 v ) ( w ∧ 的本质为向量叉乘的矩阵表达方式) = R ( w × R − 1 v ) (叉乘的分配律) = R w × R − 1 v (结合律) = R w ∧ R − 1 v Rw^{\wedge}v=Rw^{\wedge}RR^{-1}v (正交矩阵满足RR^{-1}=I)\\[5pt] =(Rw)\times(RR^{-1}v)(w^{\wedge}的本质为向量叉乘的矩阵表达方式)\\[5pt] =R(w\times R^{-1}v) (叉乘的分配律)\\[5pt] =Rw\times R^{-1}v(结合律)\\[5pt] =Rw^{\wedge}R^{-1}v Rwv=RwRR1v(正交矩阵满足RR1=I=(Rw)×(RR1v)w的本质为向量叉乘的矩阵表达方式)=R(w×R1v)(叉乘的分配律)=Rw×R1v(结合律)=RwR1v
因此:
[ w 2 ∧ v 2 0 T 0 ] = [ R w 1 ∧ − R w 1 ∧ T + R v 1 0 T 0 ] \begin{bmatrix} w_2^{\wedge}& v_2 \\ 0^T & 0\\ \end{bmatrix}=\begin{bmatrix} Rw_1^{\wedge}& -Rw_1^{\wedge}T+Rv_1\\ 0^T & 0\\ \end{bmatrix} [w20Tv20]=[Rw10TRw1T+Rv10]
由于有 − R w 1 ∧ T = − ( R w 1 ∧ ) T = − ( R w 1 ) × T = T × ( R w 1 ) -Rw_1^{\wedge}T= -(Rw_1^{\wedge})T=-(Rw_1)\times T=T\times(Rw_1) Rw1T=(Rw1)T=(Rw1)×T=T×(Rw1),将上述矩阵变换写成6x6的矩阵形式:
[ v 2 w 2 ] = [ R T ∧ R 0 R ] [ v 1 w 1 ] \begin{bmatrix} v_2\\ w_2\\ \end{bmatrix}=\begin{bmatrix} R& T^{\wedge}R \\ \mathbf 0 & R\\ \end{bmatrix} \begin{bmatrix} v_1\\ w_1\\ \end{bmatrix} [v2w2]=[R0TRR][v1w1]
上述6x6的矩阵就算是SE(3)上的伴随矩阵,可以用于将一个坐标系下的6D速度转换到另一个坐标系下。 并且从SE(3)的伴随矩阵可以很容易的得出如下结论:
w 2 = R w 1 w_2=Rw_1 w2=Rw1
这里面的 R R R就是SO(3)的伴随矩阵。

3. 李代数求导

3.1 BCH公式与近似形式

根据前文的推导我们已经清楚了SO(3)和SE(3)上李群与李代数关系。但是,当在SO(3)中完成两个矩阵乘法时,其对应的李代数发生了什么改变呢?反过来说,当两个李代数做加法时,SO(3)上是否对应这两个矩阵的乘积?换而言之,我们在研究下式是否成立:
ln ⁡ ( exp ⁡ ( A ) exp ⁡ ( B ) ) = A + B ? \ln(\exp(A)\exp(B))=A+B? ln(exp(A)exp(B))=A+B?
很遗憾,上述公式在对于李代数的时候并不成立。两个李代数指数映射乘积的完整形式,由 Baker-Campbell-Hausdorff(BCH) 公式(完整推导很复杂,当个结论记住就行)给出:
ln ⁡ ( exp ⁡ ( A ) exp ⁡ ( B ) ) = A + B + 1 2 [ A , B ] + 1 12 [ A , [ A , B ] ] − 1 12 [ B , [ A , B ] ] + . . . . . . \ln(\exp(A)\exp(B))=A+B+\frac{1}{2}[A,B]+\frac{1}{12}[A,[A,B]]-\frac{1}{12}[B,[A,B]]+...... ln(exp(A)exp(B))=A+B+21[A,B]+121[A,[A,B]]121[B,[A,B]]+......
其中 [ , ] [,] [,]为李括号,定义与前文相同( [ A , B ] = A B − B A [A,B]=AB-BA [A,B]=ABBA)。BCH公式告诉我们,当处理两个矩阵指数之积时,它们会产生一些由李括号组成的余项。当将其用于SO(3)上的李代数 ln ⁡ ( exp ⁡ ( w 1 ∧ ) exp ⁡ ( w 2 ∧ ) ) − ∧ \ln(\exp(w_1^{\wedge})\exp(w_2^{\wedge}))^{-\wedge} ln(exp(w1)exp(w2))−∧,当 w 1 w_1 w1 w 2 w_2 w2为一个小量的时候,小量二次以上的项都可以被忽略。此时用于SO(3)的BCH公式有近似表达:
ln ⁡ ( exp ⁡ ( w 1 ∧ ) exp ⁡ ( w 2 ∧ ) ) − ∧ ≈ J l ( w 2 ) − 1 w 1 + w 2 (左乘近似 w 1 为小量) ln ⁡ ( exp ⁡ ( w 1 ∧ ) exp ⁡ ( w 2 ∧ ) ) − ∧ ≈ J r ( w 1 ) − 1 w 2 + w 1 (右乘近似 w 2 为小量) \ln(\exp(w_1^{\wedge})\exp(w_2^{\wedge}))^{-\wedge} \approx J_l(w_2)^{-1}w_1+w_2(左乘近似w_1为小量)\\[5pt] \ln(\exp(w_1^{\wedge})\exp(w_2^{\wedge}))^{-\wedge} \approx J_r(w_1)^{-1}w_2+w_1(右乘近似w_2为小量) ln(exp(w1)exp(w2))−∧Jl(w2)1w1+w2(左乘近似w1为小量)ln(exp(w1)exp(w2))−∧Jr(w1)1w2+w1(右乘近似w2为小量)
以第一个公式为例,该式告诉我们,当对一个旋转矩阵 R 2 R_2 R2(李代数为 w 2 w_2 w2)左乘一个微小旋转矩阵 R 1 R_1 R1(李代数为 w 1 w_1 w1)时,可以近似地看作,在原有的李代数 w 2 w_2 w2上加上了一项 J l ( w 2 ) − 1 w 1 J_l(w_2)^{-1}w_1 Jl(w2)1w1,第二个也是同理(其中的 J l J_l Jl就是SE(3)所对应的李代数中的雅可比矩阵,右乘只需要将自变量取一个负号就行 J r ( w ) = J l ( − w ) J_r(w)=J_l(-w) Jr(w)=Jl(w))。最后左乘近似也可以简单的写成如下形式:
exp ⁡ ( Δ w ∧ ) exp ⁡ ( w ∧ ) = exp ⁡ ( ( w + J l ( w ) − 1 Δ w ) ∧ ) \exp(\Delta w^{\wedge})\exp(w^{\wedge})=\exp((w+J_l(w)^{-1}\Delta w)^{\wedge}) exp(Δw)exp(w)=exp((w+Jl(w)1Δw))
根据上述理论,使用李代数解决求导问题的思路分为两种:

  1. 用李代数表示姿态,然后根据李代数加法对李代数求导
  2. 对李群左乘右乘微小扰动,然后对该扰动求导,称为左扰动和右扰动模型。

第一种方法涉及到求解 J J J,较为复杂,且不常用。因此主要介绍第二种求导方法。

3.2 so(3)上的李代数求导

3.2.1 扰动模型(左乘)

首先考虑SO(3)上的情况,假设我们对一个空间点 l l l进行旋转,得到了 R p Rp Rp。现在要计算旋转之后点的坐标相对于旋转矩阵的导数。简单的表达为如下形式:
∂ ( R p ) ∂ R \frac{\partial (Rp)}{\partial R} R(Rp)
设有一个旋转矩阵 R R R的李代数为 w w w,现在对其施加一个左乘的扰动,看结果对于这个扰动的变化率。设左扰动 Δ R \Delta R ΔR对应的李代数为 Δ w \Delta w Δw。然后对 w w w求导,即:
∂ ( R p ) ∂ w = lim ⁡ w → 0 exp ⁡ ( Δ w ∧ ) exp ⁡ ( w ∧ ) p − exp ⁡ ( w ∧ ) p Δ w = lim ⁡ w → 0 ( I + Δ w ∧ ) exp ⁡ ( w ∧ ) p − exp ⁡ ( w ∧ ) p Δ w ( 1 阶泰勒展开 ) = lim ⁡ w → 0 Δ w ∧ exp ⁡ ( w ∧ ) p + exp ⁡ ( w ∧ ) p − exp ⁡ ( w ∧ ) p Δ w = lim ⁡ w → 0 Δ w ∧ exp ⁡ ( w ∧ ) p Δ w = lim ⁡ w → 0 Δ w ∧ ( R p ) Δ w = lim ⁡ w → 0 − ( R p ) ∧ Δ w Δ w ( 向量叉乘 ) = − ( R p ) ∧ \frac{\partial (Rp)}{\partial w}=\lim_{w\rightarrow0} \frac{\exp(\Delta w^{\wedge})\exp(w^{\wedge})p-\exp(w^{\wedge})p}{\Delta w} \\[10pt] = \lim_{w\rightarrow0} \frac{(I+\Delta w^{\wedge})\exp(w^{\wedge})p-\exp(w^{\wedge})p}{\Delta w}(1阶泰勒展开) \\[10pt] =\lim_{w\rightarrow0} \frac{\Delta w^{\wedge} \exp(w^{\wedge})p+\exp(w^{\wedge})p-\exp(w^{\wedge})p}{\Delta w}\\[10pt] =\lim_{w\rightarrow0} \frac{\Delta w^{\wedge} \exp(w^{\wedge})p}{\Delta w}\\[10pt] =\lim_{w\rightarrow0} \frac{\Delta w^{\wedge} (Rp)}{\Delta w}\\[10pt] =\lim_{w\rightarrow0} \frac{ -(Rp)^{\wedge} \Delta w}{\Delta w}(向量叉乘)= -(Rp)^{\wedge} w(Rp)=w0limΔwexp(Δw)exp(w)pexp(w)p=w0limΔw(I+Δw)exp(w)pexp(w)p(1阶泰勒展开)=w0limΔwΔwexp(w)p+exp(w)pexp(w)p=w0limΔwΔwexp(w)p=w0limΔwΔw(Rp)=w0limΔw(Rp)Δw(向量叉乘)=(Rp)
最后得到基于左乘扰动模型的雅克比矩阵就是 − ( R p ) ∧ -(Rp)^{\wedge} (Rp)

3.2.2 扰动模型(右乘)

右乘同理,这里直接给出推导过程
∂ ( R p ) ∂ w = lim ⁡ w → 0 exp ⁡ ( w ∧ ) exp ⁡ ( Δ w ∧ ) p − exp ⁡ ( w ∧ ) p Δ w = lim ⁡ w → 0 exp ⁡ ( w ∧ ) ( I + Δ w ∧ ) p − exp ⁡ ( w ∧ ) p Δ w = lim ⁡ w → 0 exp ⁡ ( w ∧ ) Δ w ∧ p + exp ⁡ ( w ∧ ) p − exp ⁡ ( w ∧ ) p Δ w = lim ⁡ w → 0 exp ⁡ ( w ∧ ) Δ w ∧ p Δ w = lim ⁡ w → 0 R Δ w ∧ p Δ w = lim ⁡ w → 0 − R p ∧ Δ w Δ w = − R p ∧ \frac{\partial (Rp)}{\partial w}=\lim_{w\rightarrow0} \frac{\exp(w^{\wedge})\exp(\Delta w^{\wedge})p-\exp(w^{\wedge})p}{\Delta w} \\[10pt] = \lim_{w\rightarrow0} \frac{\exp(w^{\wedge})(I+\Delta w^{\wedge})p-\exp(w^{\wedge})p}{\Delta w} \\[10pt] =\lim_{w\rightarrow0} \frac{ \exp(w^{\wedge})\Delta w^{\wedge}p+\exp(w^{\wedge})p-\exp(w^{\wedge})p}{\Delta w}\\[10pt] =\lim_{w\rightarrow0} \frac{ \exp(w^{\wedge})\Delta w^{\wedge}p}{\Delta w}\\[10pt] =\lim_{w\rightarrow0} \frac {R\Delta w^{\wedge}p}{\Delta w}\\[10pt] =\lim_{w\rightarrow0} \frac{ -Rp^{\wedge} \Delta w}{\Delta w}= -Rp^{\wedge} w(Rp)=w0limΔwexp(w)exp(Δw)pexp(w)p=w0limΔwexp(w)(I+Δw)pexp(w)p=w0limΔwexp(w)Δwp+exp(w)pexp(w)p=w0limΔwexp(w)Δwp=w0limΔwRΔwp=w0limΔwRpΔw=Rp

3.3 se(3)上的李代数求导

3.3.1 扰动模型(左乘)

之后给出SE(3)上的扰动模型。假设某空间点 p p p p h p_h ph p p p的齐次坐标)经过一次变换 C C C(对应的李代数为 ρ \rho ρ),得到 C p Cp Cp。现在给 C C C左乘一个扰动 Δ ρ ∧ = [ Δ v , Δ w ] T \Delta \rho^{\wedge}=[\Delta v, \Delta w]^T Δρ=[Δv,Δw]T,那么:
∂ C p ∂ Δ ρ = lim ⁡ w → 0 exp ⁡ ( Δ ρ ∧ ) exp ⁡ ( ρ ∧ ) p h − exp ⁡ ( ρ ∧ ) p h Δ ρ = lim ⁡ w → 0 ( I + Δ ρ ∧ ) exp ⁡ ( ρ ∧ ) p h − exp ⁡ ( ρ ∧ ) p h Δ ρ ( 1 阶泰勒展开 ) = lim ⁡ w → 0 Δ ρ ∧ exp ⁡ ( ρ ∧ ) p h + exp ⁡ ( ρ ∧ ) p h − exp ⁡ ( ρ ∧ ) p h Δ ρ = lim ⁡ w → 0 Δ ρ ∧ exp ⁡ ( ρ ∧ ) p h Δ ρ = lim ⁡ w → 0 [ Δ w ∧ Δ v 0 T 0 ] [ R p + T 1 ] Δ ρ = lim ⁡ w → 0 [ Δ w ∧ ( R p + T ) + Δ v 0 T ] [ Δ v Δ w ] T = lim ⁡ w → 0 [ − ( R p + T ) ∧ Δ w + Δ v 0 T ] [ Δ v Δ w ] T ( 向量叉乘 ) = lim ⁡ w → 0 [ I − ( R p + T ) ∧ 0 T 0 T ] [ Δ v Δ w ] [ Δ v Δ w ] T = [ I − ( R p + T ) ∧ 0 T 0 T ] \frac{\partial Cp}{\partial \Delta \rho}=\lim_{w\rightarrow0} \frac{\exp(\Delta \rho^{\wedge})\exp(\rho^{\wedge})p_h-\exp(\rho^{\wedge})p_h}{\Delta \rho} \\[10pt] =\lim_{w\rightarrow0} \frac{(I+\Delta \rho^{\wedge})\exp(\rho^{\wedge})p_h-\exp(\rho^{\wedge})p_h}{\Delta \rho}(1阶泰勒展开) \\[10pt] =\lim_{w\rightarrow0} \frac{\Delta \rho^{\wedge} \exp(\rho^{\wedge})p_h+\exp(\rho^{\wedge})p_h-\exp(\rho^{\wedge})p_h}{\Delta \rho}\\[10pt] =\lim_{w\rightarrow0} \frac{\Delta \rho^{\wedge} \exp(\rho^{\wedge})p_h}{\Delta \rho}\\[10pt] =\lim_{w\rightarrow0} \frac{\begin{bmatrix} \Delta w^{\wedge}& \Delta v \\ 0^T & 0\\ \end{bmatrix} \begin{bmatrix} Rp+T \\ 1 \end{bmatrix}}{\Delta \rho} \\[10pt] =\lim_{w\rightarrow0} \frac{\begin{bmatrix} \Delta w^{\wedge}(Rp+T)+\Delta v \\ \mathbf{0}^T \end{bmatrix}}{\begin{bmatrix}\Delta v & \Delta w \end{bmatrix}^T}\\[10pt] =\lim_{w\rightarrow0} \frac{\begin{bmatrix}-(Rp+T)^{\wedge} \Delta w+\Delta v \\ \mathbf{0}^T \end{bmatrix}}{\begin{bmatrix}\Delta v & \Delta w \end{bmatrix}^T}(向量叉乘)\\[10pt] =\lim_{w\rightarrow0} \frac{\begin{bmatrix}I &-(Rp+T)^{\wedge} \\ \mathbf{0}^T &\mathbf{0}^T \end{bmatrix}\begin{bmatrix} \Delta v \\ \Delta w \end{bmatrix}}{\begin{bmatrix}\Delta v & \Delta w \end{bmatrix}^T}\\[10pt] =\begin{bmatrix}I &-(Rp+T)^{\wedge} \\ \mathbf{0}^T &\mathbf{0}^T \end{bmatrix} ΔρCp=w0limΔρexp(Δρ)exp(ρ)phexp(ρ)ph=w0limΔρ(I+Δρ)exp(ρ)phexp(ρ)ph(1阶泰勒展开)=w0limΔρΔρexp(ρ)ph+exp(ρ)phexp(ρ)ph=w0limΔρΔρexp(ρ)ph=w0limΔρ[Δw0TΔv0][Rp+T1]=w0lim[ΔvΔw]T[Δw(Rp+T)+Δv0T]=w0lim[ΔvΔw]T[(Rp+T)Δw+Δv0T](向量叉乘)=w0lim[ΔvΔw]T[I0T(Rp+T)0T][ΔvΔw]=[I0T(Rp+T)0T]
最后我们得到了一个4x6的雅克比矩阵。

3.3.2 扰动模型(右乘)

同理给出SE(3)右乘扰动模型的雅克比矩阵推导过程:
∂ C p ∂ Δ ρ = lim ⁡ w → 0 exp ⁡ ( ρ ∧ ) exp ⁡ ( Δ ρ ∧ ) p h − exp ⁡ ( ρ ∧ ) p h Δ ρ = lim ⁡ w → 0 exp ⁡ ( ρ ∧ ) ( I + Δ ρ ∧ ) p h − exp ⁡ ( ρ ∧ ) p h Δ ρ ( 1 阶泰勒展开 ) = lim ⁡ w → 0 exp ⁡ ( ρ ∧ ) Δ ρ ∧ p h + exp ⁡ ( ρ ∧ ) p h − exp ⁡ ( ρ ∧ ) p h Δ ρ = lim ⁡ w → 0 exp ⁡ ( ρ ∧ ) Δ ρ ∧ p h Δ ρ = lim ⁡ w → 0 [ R T 0 T 1 ] [ Δ w ∧ Δ v 0 T 0 ] p h Δ ρ = lim ⁡ w → 0 [ R T 0 T 1 ] [ Δ w ∧ p + Δ v 0 ] Δ ρ = lim ⁡ w → 0 [ R ( Δ w ∧ p + Δ v ) 0 T ] [ Δ v Δ w ] T = lim ⁡ w → 0 [ R ( Δ v − p ∧ Δ w ) 0 T ] [ Δ v Δ w ] T ( 向量叉乘 ) = lim ⁡ w → 0 [ R − R p ∧ 0 T 0 T ] [ Δ v Δ w ] [ Δ v Δ w ] T = [ R − R p ∧ 0 T 0 T ] \frac{\partial Cp}{\partial \Delta \rho}=\lim_{w\rightarrow0} \frac{\exp(\rho^{\wedge})\exp(\Delta \rho^{\wedge})p_h-\exp(\rho^{\wedge})p_h}{\Delta \rho} \\[10pt] =\lim_{w\rightarrow0} \frac{\exp(\rho^{\wedge})(I+\Delta \rho^{\wedge})p_h-\exp(\rho^{\wedge})p_h}{\Delta \rho}(1阶泰勒展开) \\[10pt] =\lim_{w\rightarrow0} \frac{ \exp(\rho^{\wedge})\Delta \rho^{\wedge}p_h+\exp(\rho^{\wedge})p_h-\exp(\rho^{\wedge})p_h}{\Delta \rho}\\[10pt] =\lim_{w\rightarrow0} \frac{ \exp(\rho^{\wedge})\Delta \rho^{\wedge}p_h}{\Delta \rho}\\[10pt] =\lim_{w\rightarrow0} \frac{\begin{bmatrix} R &T \\ \mathbf{0}^T & 1 \end{bmatrix}\begin{bmatrix} \Delta w^{\wedge}& \Delta v \\ 0^T & 0\\ \end{bmatrix}p_h }{\Delta \rho} \\[10pt] =\lim_{w\rightarrow0} \frac{\begin{bmatrix} R &T \\ \mathbf{0}^T & 1 \end{bmatrix}\begin{bmatrix} \Delta w^{\wedge}p+\Delta v \\ 0 \end{bmatrix} }{\Delta \rho} \\[10pt] =\lim_{w\rightarrow0} \frac{\begin{bmatrix} R(\Delta w^{\wedge}p+\Delta v)\\ \mathbf{0}^T \end{bmatrix}}{\begin{bmatrix}\Delta v & \Delta w \end{bmatrix}^T}\\[10pt] =\lim_{w\rightarrow0} \frac{\begin{bmatrix}R(\Delta v-p^{\wedge} \Delta w)\\ \mathbf{0}^T \end{bmatrix}}{\begin{bmatrix}\Delta v & \Delta w \end{bmatrix}^T}(向量叉乘)\\[10pt] =\lim_{w\rightarrow0} \frac{\begin{bmatrix} R &-Rp^{\wedge} \\ \mathbf{0}^T &\mathbf{0}^T \end{bmatrix}\begin{bmatrix} \Delta v \\ \Delta w \end{bmatrix}}{\begin{bmatrix}\Delta v & \Delta w \end{bmatrix}^T}\\[10pt] =\begin{bmatrix}R &-Rp^{\wedge} \\ \mathbf{0}^T &\mathbf{0}^T \end{bmatrix} ΔρCp=w0limΔρexp(ρ)exp(Δρ)phexp(ρ)ph=w0limΔρexp(ρ)(I+Δρ)phexp(ρ)ph(1阶泰勒展开)=w0limΔρexp(ρ)Δρph+exp(ρ)phexp(ρ)ph=w0limΔρexp(ρ)Δρph=w0limΔρ[R0TT1][Δw0TΔv0]ph=w0limΔρ[R0TT1][Δwp+Δv0]=w0lim[ΔvΔw]T[R(Δwp+Δv)0T]=w0lim[ΔvΔw]T[R(ΔvpΔw)0T](向量叉乘)=w0lim[ΔvΔw]T[R0TRp0T][ΔvΔw]=[R0TRp0T]
注:求解雅克比和更新雅克比用的扰动模型一定要一致

4. 代码实现

本文基于上述原理结合opencv的cv::Matx、完成对cv::Affine3和cv::Quat的李代数功能扩展.。

4.1 SO3

/// @file
/// Special Euclidean group SO(3) - rotation and translation in 3d.

#pragma once

#include <opencv2/opencv.hpp>
#include <opencv2/core/quaternion.hpp>

namespace cvSophus
{
    template <typename T>
    class SO3
    {
    public:
        /// @brief non-parametric constructor
        SO3() = default;

        /// @brief constructor from quaternion
        /// @param q : quaternion
        SO3(const cv::Quat<T> &q) : data_(q) {}

        /// @brief constructor from rotation matrix
        /// @param R : rotation matrix
        SO3(const cv::Matx<T, 3, 3> &R) { data_ = data_.createFromRotMat(R); }


        /// @brief constructor from rotation vector
        /// @param rvec : rotation vector
        SO3(const cv::Vec<T, 3> &rvec)
        {
            // if Quaternions are directly converted rotation vector, rotation vector will be singular.
            cv::Matx<T, 3, 3> R;
            cv::Rodrigues(rvec, R);
            data_ = data_.createFromRotMat(R);
        }


        /// @brief constructor from euler angle
        /// @param euler : euler angle
        /// @param euler_type : euler angel type
        SO3(const cv::Vec<T, 3> &euler, const cv::QuatEnum::EulerAnglesType &euler_type)
        {
            data_ = data_.createFromEulerAngles(euler, euler_type);
        }

        SO3<T> operator*(const SO3<T> &rhs)
        {
            return SO3<T>(data_.toRotMat3x3() * rhs.matrix());
        }

        /// @brief generate the adjoint matrix of SO(3)
        /// @return adjoint matrix 
        cv::Matx<T, 3, 3> adjoint() { return data_; }


        /// @brief generate the Derivative by left perturbation
        /// @param p : the point of 3D space
        /// @return jacobian matrix
        cv::Matx<T, 3, 3> left_jacobian(const cv::Vec<T, 3> &p)
        {
            return -skew(data_.toRotMat3x3() * p);
        }

        /// @brief generate the Derivative by right perturbation
        /// @param p : the point of 3D space
        /// @return jacobian matrix
        cv::Matx<T, 3, 3> right_jacobian(const cv::Vec<T, 3> &p)
        {
            return -data_.toRotMat3x3() * skew(p);
        }

        /// @brief log map(to rotation vector)
        /// @return the Lie Algebra of SO(3) (rotation vector)
        cv::Vec<T, 3> log() const
        {
            cv::Matx<T, 3, 3> R = matrix();

            cv::Vec<T, 3> rvec;
            cv::Rodrigues(R, rvec);

            return rvec;
        }

        /// @brief get rotation matrix
        /// @return rotation matrix
        cv::Matx<T, 3, 3> matrix() const { return data_.toRotMat3x3(); }


        /// @brief get euler angle
        /// @param euler_type : euler angle type
        /// @return euler angle by rad
        cv::Vec<T, 3> to_euler_angle(const cv::QuatEnum::EulerAnglesType &euler_type) const
        {
            return data_.toEulerAngles(euler_type);
        }

        /// @brief get quterntion
        /// @return quterntion
        cv::Quat<T> to_querntion() const { return data_ };

        /// @brief Exponential Map
        /// @param w : the Lie Algebra of SO(3) (rotation vector)
        /// @return SO(3)
        static SO3 exp(const cv::Vec<T, 3> &w) { return SO3(w); }

        /// @brief get skew matrix
        /// @param w : the Lie Algebra of SO(3) (rotation vector)
        /// @return skew matrix
        static cv::Matx<T, 3, 3> skew(const cv::Vec<T, 3> &w)
        {
            cv::Matx<T, 3, 3> skew_matrix;
            skew_matrix << 0, -w(2), w(1),
                w(2), 0, -w(0),
                -w(1), w(0), 0;

            return skew_matrix;
        }

    private:
        cv::Quat<T> data_;
    };

    template<typename T>
    std::ostream& operator<<(std::ostream& out, const SO3<T> &data)
    {
        out << data.matrix();
        return out;
    }

    using SO3d = SO3<double>;
    using SO3f = SO3<float>;
}

4.2 SE(3)

/// @file
/// Special Euclidean group SE(3) - rotation and translation in 3d.

#pragma once

#include <opencv2/opencv.hpp>
#include "so3.hpp"


namespace cvSophus
{
    template <typename datatype>
    class SE3
    {
    public:
        /// @brief non-parametric constructor
        SE3() = default;

        /// @brief constructor from rotation matrix and translation
        /// @param R : rotation matrix
        /// @param T : translation
        SE3(const cv::Matx<datatype, 3, 3> &R, const cv::Vec<datatype, 3> &T) { data_ = cv::Affine3<datatype>(R, T); }

        /// @brief constructor from SO(3) and translation
        /// @param R : SO(3)
        /// @param T : translation
        SE3(const SO3<datatype> &R, const cv::Vec<datatype, 3> &T) { data_ = cv::Affine3<datatype>(R.log(), T); }

        /// @brief constructor from rotation vector and translation
        /// @param rvec : rotation vector
        /// @param T : translation
        SE3(const cv::Vec<datatype, 3> &rvec, const cv::Vec<datatype, 3> &T) { data_ = cv::Affine3<datatype>(rvec, T); }


        /// @brief constructor from rotation vector and translation
        /// @param t_rvec : the std::vector of translation and rotation vector
        SE3(const std::vector<datatype> &t_rvec)
        {
            assert(t_rvec.size() == 6);
            cv::Vec<datatype, 3> t(t_rvec[0], t_rvec[1], t_rvec[2]);
            cv::Vec<datatype, 3> rvec(t_rvec[3], t_rvec[4], t_rvec[5]);

            data_ = cv::Affine3<datatype>(rvec, t);
        }

        /// @brief  constructor from euler angle and translation
        /// @param euler : euler angle
        /// @param t : translation
        /// @param euler_type : the type of euler angle
        SE3(const cv::Vec<datatype, 3> &euler, const cv::Vec<datatype, 3> &t,
            const cv::QuatEnum::EulerAnglesType &euler_type)
        {
            SO3<datatype> tmp(euler, euler_type);
            data_ = cv::Affine3<datatype>(tmp.log(), t);
        }


        /// @brief constructor from the euler angle and translation of std::vector
        /// @param t_euler : the euler angle and translation of std::vector
        /// @param euler_type : the type fo euler angle
        SE3(const std::vector<datatype> &t_euler, const cv::QuatEnum::EulerAnglesType &euler_type)
        {
            cv::Vec<datatype, 3> t(t_euler[0], t_euler[1], t_euler[2]);
            cv::Vec<datatype, 3> euler(t_euler[3], t_euler[4], t_euler[5]);

            SO3<datatype> tmp(euler, euler_type);
            data_ = cv::Affine3<datatype>(tmp.log(), t);
        }

        SE3 operator*(const SE3 &rhs)
        {
            cv::Affine3<datatype> rhs_affine(rhs.rvec(), rhs.translation());
            cv::Affine3<datatype> result = data_ * rhs_affine;

            return SE3<datatype>(result.rvec(), result.translation());
        }


        /// @brief get translaton
        /// @return translaton
        cv::Vec<datatype, 3> translation() const { return data_.translation(); }


        /// @brief get rotation vector
        /// @return  rotation vector
        cv::Vec<datatype, 3> rvec() const { return data_.rvec(); }


        /// @brief generate the adjoint matrix of SE(3)
        /// @return adjoint matrix
        cv::Matx<datatype, 6, 6> adjoint() const
        {
            cv::Matx<datatype, 3, 3> R = data_.rotation();
            cv::Matx<datatype, 3, 3> zero = cv::Matx<T, 3, 3>::zeros();

            cv::Matx<datatype, 3, 6> upper;
            cv::hconcat(R, SO3<T>::skew(data_.translation() * R), upper);
            cv::Matx<datatype, 3, 6> lower;
            cv::hconcat(zero, R, lower);

            cv::Matx<datatype, 6, 6> result;
            cv::vconcat(upper, lower, result);

            return result;
        }

        /// @brief generate the Derivative by left perturbation
        /// @param p : the point of 3D space
        /// @return jacobian matrix
        cv::Matx<datatype, 4, 6> left_jacobian(const cv::Vec<datatype, 3> &p)
        {
            cv::Matx<datatype, 3, 3> left = cv::Matx<datatype, 3, 3>::eye();
            cv::Matx<datatype, 3, 3> right = -SO3<datatype>::skew(data_.rotation() * p + data_.translation());

            cv::Matx<datatype, 3, 6> upper;
            cv::hconcat(left, right, upper);

            cv::Matx<datatype, 1, 6> lower = cv::Matx<datatype, 1, 6>::zeros();

            cv::Matx<datatype, 4, 6> result;
            cv::vconcat(upper, lower, result);

            return result;
        }

        /// @brief generate the Derivative by right perturbation
        /// @param p : the point of 3D space
        /// @return jacobian matrix
        cv::Matx<datatype, 4, 6> right_jacobian(const cv::Vec<datatype, 3> &p)
        {
            cv::Matx<datatype, 3, 3> left = data_.rotation();
            cv::Matx<datatype, 3, 3> right = -left * SO3<datatype>::skew(p);

            cv::Matx<datatype, 3, 6> upper;
            cv::hconcat(left, right, upper);

            cv::Matx<datatype, 1, 6> lower = cv::Matx<datatype, 1, 6>::zeros();

            cv::Matx<datatype, 4, 6> result;
            cv::vconcat(upper, lower, result);

            return result;
        }

        /// @brief log map(to twist)
        /// @return the Lie Algebra of SE(3) (twist)
        cv::Vec<datatype, 6> log() const
        {
            cv::Vec<datatype, 3> rvec = data_.rvec();

            cv::Vec<datatype, 3> t = data_.translation();
            cv::Matx<datatype, 3, 3> J = jacobian(rvec, 1e-8);
            cv::Vec<datatype, 3> v = J.solve(t, cv::DECOMP_SVD);

            return {v(0), v(1), v(2), rvec(0), rvec(1), rvec(2)};
        }

        cv::Matx<datatype, 4, 4> matrix() const
        {
            return data_.matrix();
        }

        /// @brief to the std::vector of rotation vector and translation
        /// @return the std::vector of rotation vector and translation
        std::vector<datatype> to_stdvector_t_rvec()
        {
            cv::Vec<datatype, 3> rvec = data_.rvec();
            cv::Vec<datatype, 3> t = data_.translation();

            return {t(0), t(1), t(2), rvec(0), rvec(1), rvec(2)};
        }

        /// @brief Exponential Map
        /// @param w : the Lie Algebra of SE(3) (twist)
        /// @return SE(3)
        static SE3 exp(const cv::Vec<datatype, 6> &rho)
        {
            cv::Vec<datatype, 3> v(rho(0), rho(1), rho(2));
            cv::Vec<datatype, 3> w(rho(3), rho(4), rho(5));

            cv::Vec<datatype, 3> t = jacobian(w, 1e-8) * v;

            return SE3<T>(w, t);
        }

        /// @brief generate jacobian matrix(to generate the Lie Algerba of SE(3))
        /// @param w : the Lie Algebra of SO(3) (rotaton vector)
        /// @return jacobian matrix
        static cv::Matx<datatype, 3, 3> jacobian(const cv::Vec<datatype, 3> &w, const double &eps)
        {
            double theta_ = cv::norm(w);

            cv::Matx<datatype, 3, 3> J = cv::Matx<datatype, 3, 3>::eye() + ((1 - std::cos(theta_)) / (theta_ * theta_)) * SO3<datatype>::skew(w) +
                                  ((theta_ - std::sin(theta_)) / (theta_ * theta_ * theta_)) * SO3<datatype>::skew(w) * SO3<datatype>::skew(w);

            // To numerical stability
            if (theta_ < eps)
            {
                J = cv::Matx<datatype, 3, 3>::eye() + 0.5 * SO3<datatype>::skew(w) +
                    (1.f + 1.f / 6.0) * SO3<datatype>::skew(w) * SO3<datatype>::skew(w);
            }

            return J;
        }

    private:
        cv::Affine3<datatype> data_;
    };

    template <typename datatype>
    std::ostream& operator<<(std::ostream& out, const SE3<datatype> &data)
    {
        out << data.matrix();
        return out;
    }

    using SE3f = SE3<float>;
    using SE3d = SE3<double>;
}
  • 20
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值