1 概述

在上一篇文章《详解SLAM中的李群和李代数(上)》中,我们已经通过对李群求导引出了李代数。在这篇文章中,我们就系统总结一下李代数的相关知识。

2 李代数

2.1 定义

李代数是一个向量空间\(\mathfrak{g}\)与一个二元运算的组合。其中的二元运算称为李括号(Lie bracket),记为\([\cdot,\cdot]: \mathfrak{g} \times \mathfrak{g} \rightarrow \mathfrak{g}\)。在这个公式中:

  • \([\cdot,\cdot]\)是一个二元运算符,表示两个元素之间的某种“乘法”操作。当然它不是普通的乘法,常见的运算操作是向量叉乘 \(\times\) 或矩阵的换位子\([A,B] = AB - BA\)。
  • \(\mathfrak{g}\) 是李代数的集合(通常是一个向量空间),\(\mathfrak{g} \times\mathfrak{g}\) 就是说我们要从这个集合里取两个元素做运算,\(\rightarrow \mathfrak{g}\)表示运算的结果仍然属于同一个集合 \(\mathfrak{g}\)。也就是说李括号运算把两个李代数中的元素变成另一个李代数中的元素。其实就是封闭性的意思。

李代数需要满足以下三个公理:

  1. 双线性:对于所有\(x, y, z \in \mathfrak{g}\)和所有标量\(a, b\),有:

    \[[ax + by, z] = a[x, z] + b[y, z],\quad [z, ax + by] = a[z, x] + b[z, y]
    \]
  2. 反对称性:对于所有\(x, y \in \mathfrak{g}\),有:

    \[[x, y] = -[y, x]
    \]

    特别地,这意味着\([x,x]=0\)。

  3. 雅可比恒等式:对于所有\(x, y, z \in \mathfrak{g}\),有:

    \[[x,[y,z]] + [y,[z,x]] + [z,[x,y]] = 0
    \]

如果是第一次接触到李代数这个概念,这三个特性公理并不太好理解,因为缺少实际的使用场景。但是这里还是举一个例子来加深印象。例如,三维向量空间\(\mathbb{R}^3\)和其上定义的叉乘\(\times\)构成了一个李代数\((\mathbb{R}^3, \times)\),李括号为\([\boldsymbol{u}, \boldsymbol{v}] = \boldsymbol{u} \times \boldsymbol{v}\)。它符合李代数的三个公理:

  1. 双线性:假设有向量\(\boldsymbol{u} = (1, 0, 0)\),\(\boldsymbol{v} = (0, 1, 0)\),\(\boldsymbol{w} = (0, 0, 1)\),以及标量\(a=2\),\(b=3\)。

    • 计算\([a\boldsymbol{u} + b\boldsymbol{v}, \boldsymbol{w}]\):
    \[[(2, 0, 0) + (0, 3, 0), (0, 0, 1)] = [(2, 3, 0), (0, 0, 1)] = (3, -2, 0)
    \]
    • 计算\(a[\boldsymbol{u}, \boldsymbol{w}] + b[\boldsymbol{v}, \boldsymbol{w}]\):
    \[2[(1, 0, 0), (0, 0, 1)] + 3[(0, 1, 0), (0, 0, 1)] = 2(0, 1, 0) + 3(-1, 0, 0) = (3, -2, 0)
    \]

    两式结果相等,符合双线性公理。

  2. 反对称性:假设\(\boldsymbol{u} = (1, 0, 0)\)和\(\boldsymbol{v} = (0, 1, 0)\)。

    • 计算\([\boldsymbol{u}, \boldsymbol{v}]\):
      \[(1, 0, 0) \times (0, 1, 0) = (0, 0, 1)
      \]
    • 计算 \([\boldsymbol{v}, \boldsymbol{u}]\):
      \[(0, 1, 0) \times (1, 0, 0) = (0, 0, -1)
      \]

    可以看到\([\boldsymbol{u}, \boldsymbol{v}] = -( [\boldsymbol{v}, \boldsymbol{u}] )\),这符合反对称性。

  3. 雅可比恒等式:假设\(\boldsymbol{u} = (1, 0, 0)\), \(\boldsymbol{v} = (0, 1, 0)\), \(\boldsymbol{w} = (0, 0, 1)\)。

    • 计算\([\boldsymbol{u}, [\boldsymbol{v}, \boldsymbol{w}]]\):
      \[[\boldsymbol{u}, (0, 1, 0) \times (0, 0, 1)] = [(1, 0, 0), (1, 0, 0)] = (0, 0, 0)
      \]
    • 计算 \([\boldsymbol{v}, [\boldsymbol{w}, \boldsymbol{u}]]\):
      \[[\boldsymbol{v}, (0, 0, 1) \times (1, 0, 0)] = [(0, 1, 0), (0, 1, 0)] = (0, 0, 0)
      \]
    • 计算 \([\boldsymbol{w}, [\boldsymbol{u}, \boldsymbol{v}]]\):
      \[[\boldsymbol{w}, (1, 0, 0) \times (0, 1, 0)] = [(0, 0, 1), (0, 0, 1)] = (0, 0, 0)
      \]

    因此,

    \[[\boldsymbol{u}, [\boldsymbol{v}, \boldsymbol{w}]] + [\boldsymbol{v}, [\boldsymbol{w}, \boldsymbol{u}]] + [\boldsymbol{w}, [\boldsymbol{u}, \boldsymbol{v}]] = (0, 0, 0) + (0, 0, 0) + (0, 0, 0) = (0, 0, 0)
    \]

    这验证了雅可比恒等式成立。

注意,笔者这里对于李代数的定义与《视觉SLAM十四讲》上的定义略有不同,但是表达的实际的含义是一样的,读者可以自行体会。

2.2 李代数\(\mathfrak{so}(3)\)

在上一篇文章《详解SLAM中的李群和李代数(上)》中笔者实现了对特殊正交群\(SO(3)\)的求导:

\[R(t) = \exp([\boldsymbol{\omega}(t)]_\times t)
\]

其中\(\exp\)表示矩阵指数运算,\(\boldsymbol{\omega}(t)\)是表达旋转矩阵的旋转向量,\([\boldsymbol{\omega}(t)]_{\times}\)是其对应的反对称矩阵。去掉用于求导的自变量\(t\),那么可以定义\(SO(3)\)对应的李代数是:

\[\mathfrak{so}(3) = \left\{ [\boldsymbol{\omega}]_\times \in \mathbb{R}^{3 \times 3} \mid {\omega} \in \mathbb{R}^3 \right\}
\]

也就是说,\(\mathfrak{so}(3)\)是所有实\(3\times 3\)反对称矩阵构成的集合,对应的李括号是两个元素之间的换位子(commutator)。

2.3 \(\mathfrak{so}(3)\)的李括号

这里的李括号也就是换位子运算具体定义是:对于任意\(X, Y \in \mathfrak{so}(3)\),有:

\[[X, Y] = XY - YX
\]

例如,在\(\mathfrak{so}(3)\)中,如果\(X = [\boldsymbol{\omega}_1]_\times\)和\(Y = [\boldsymbol{\omega}_2]_\times\),则:

\[[[\boldsymbol{\omega}_1]_\times, [\boldsymbol{\omega}_2]_\times] = [\boldsymbol{\omega}_1]_\times [\boldsymbol{\omega}_2]_\times - [\boldsymbol{\omega}_2]_\times [\boldsymbol{\omega}_1]_\times
\]

这个表达式的结果仍然是一个反对称矩阵,并且它对应于向量\(\boldsymbol{\omega}_1 \times \boldsymbol{\omega}_2\)的反对称矩阵,即:

\[[[\boldsymbol{\omega}_1]_\times, [\boldsymbol{\omega}_2]_\times] = [\boldsymbol{\omega}_1 \times \boldsymbol{\omega}_2]_\times
\]

这意味着在\(\mathfrak{so}(3)\)中,李括号运算等价于三维向量空间中的叉积运算,\(\mathfrak{so}(3)\)与上一小节中介绍的李代数\((\mathbb{R}^3, \times)\)的含义基本相同。

举例验证一下,假设有两个向量\(\boldsymbol{\omega}_1 = (1, 0, 0)^T\)和\(\boldsymbol{\omega}_2 = (0, 1, 0)^T\),它们对应的反对称矩阵分别是:

\[[\boldsymbol{\omega}_1]_\times = \begin{bmatrix}
0 & 0 & 0 \\
0 & 0 & -1 \\
0 & 1 & 0
\end{bmatrix}, \quad [\boldsymbol{\omega}_2]_\times = \begin{bmatrix}
0 & 0 & 1 \\
0 & 0 & 0 \\
-1 & 0 & 0
\end{bmatrix} \]

计算它们的李括号:

\[[[\boldsymbol{\omega}_1]_\times, [\boldsymbol{\omega}_2]_\times] = \begin{bmatrix}
0 & 0 & 0 \\
0 & 0 & -1 \\
0 & 1 & 0
\end{bmatrix} \begin{bmatrix}
0 & 0 & 1 \\
0 & 0 & 0 \\
-1 & 0 & 0
\end{bmatrix} - \begin{bmatrix}
0 & 0 & 1 \\
0 & 0 & 0 \\
-1 & 0 & 0
\end{bmatrix} \begin{bmatrix}
0 & 0 & 0 \\
0 & 0 & -1 \\
0 & 1 & 0
\end{bmatrix}\]
\[= \begin{bmatrix}
0 & 0 & 0 \\
1 & 0 & 0 \\
0 & 0 & -1
\end{bmatrix} - \begin{bmatrix}
0 & 0 & -1 \\
0 & 0 & 0 \\
1 & 0 & 0
\end{bmatrix} = \begin{bmatrix}
0 & 0 & 1 \\
-1 & 0 & 0 \\
0 & -1 & 0
\end{bmatrix}\]

这正是\([0, 0, 1]^T\)向量对应的反对称矩阵,也就是\(\boldsymbol{\omega}_1 \times \boldsymbol{\omega}_2 = (0, 0, 1)^T\)。

2.4 李代数\(\mathfrak{se}(3)\)

与引出特殊正交群\(SO(3)\)的李代数\(\mathfrak{so}(3)\)的推导一样,也可以对特殊欧式群 \(SE(3)\)对时间\(t\)微分来引出对应的李代数\(\mathfrak{se}(3)\)。假设一个刚体在三维空间随时间做欧式运动(即刚体运动,同时包括旋转和平移),那么欧式变换矩阵可以表示为:

\[T(t) =
\begin{bmatrix}
R(t) & \boldsymbol{p}(t) \\
\mathbf{0}^T & 1
\end{bmatrix}
\in SE(3)
\]

其中:

  • \(R(t) \in SO(3)\) 表示旋转;
  • \(\boldsymbol{p}(t) \in \mathbb{R}^3\) 表示平移;
  • 整个矩阵是一个\(4 \times 4\)的齐次变换矩阵。

对\(T(t)\)的自变量时间\(t\)进行微分,即:

\[\frac{d}{dt} T(t) = \frac{d}{dt}
\begin{bmatrix}
R(t) & \boldsymbol{p}(t) \\
\mathbf{0}^T & 1
\end{bmatrix}=
\begin{bmatrix}
\frac{d}{dt} R(t) & \frac{d}{dt} \boldsymbol{p}(t) \\
\mathbf{0}^T & 0
\end{bmatrix}
\]

其中\(\frac{d}{dt} R(t) = [\boldsymbol{\omega}(t)]_\times R(t)\),这一点我们在前面引出李代数\(\mathfrak{so}(3)\)的时候推导过。\(\boldsymbol{p}(t)\)表示位移,那么\(\boldsymbol{p}(t)\)对时间\(t\)求导,即\(\frac{d}{dt} \boldsymbol{p}(t) = \boldsymbol{v}(t)\)是瞬时线速度。

因此:

\[\frac{d}{dt} T(t) =
\begin{bmatrix}
[\boldsymbol{\omega}(t)]_\times R(t) & \boldsymbol{v}(t) \\
\mathbf{0}^T & 0
\end{bmatrix}
\tag{1}
\]

需要进一步说明的是,\(\boldsymbol{v}(t)\) 是空间坐标系下的线速度。我们知道速度是一种矢量,而欧式变换包含旋转和平移两种变换,那么空间坐标系下线速度\(\boldsymbol{v}(t)\)可以分解成:

  1. 由于旋转引起的“附加速度”:即使没有自身的平移,只要它在旋转,它上面的点也会因为旋转而产生一个速度。
  2. 由于自身平移产生的速度:这是物体本身在自己坐标系下的移动速度。

数学表达如下:

\[\boldsymbol{v}(t) = [\boldsymbol{\omega}(t)]_\times \boldsymbol{p}(t) + \boldsymbol{v}_{\text{body}}(t) \tag{2}
\]

这个公式其实就是刚体运动学中的速度合成公式:

  • \(\boldsymbol{v}_{\text{body}}\)就是刚体自身平移产生的速度,即体坐标系下的线速度;
  • \(\boldsymbol{\omega}(t)\)是刚体的角速度,通常也在体坐标系下表示;
  • \(\boldsymbol{p}(t)\)表示相对于刚体原点的位置向量。
  • \([\boldsymbol{\omega}(t)]_\times \boldsymbol{p}(t)\)就是由于旋转引起的附加速度,其是一种典型的圆周运动的速度:\(\boldsymbol{\omega}(t)\)方向垂直于圆周面,\(\boldsymbol{p}(t)\)定义了到旋转中心的距离半径,两者叉乘,即得因刚体旋转而具有的切向速度。

将式(2)代入式(1),有:

\[\begin{align*}
\frac{d}{dt} T(t)
&= \begin{bmatrix}
[\boldsymbol{\omega}(t)]_\times R(t) & [\boldsymbol{\omega}(t)]_\times \boldsymbol{p}(t) + \boldsymbol{v}_{\text{body}}(t) \\
\mathbf{0}^T & 0
\end{bmatrix} \\
&= \begin{bmatrix}
[\boldsymbol{\omega}(t)]_\times & \boldsymbol{v}_{\text{body}}(t) \\
\mathbf{0}^T & 0
\end{bmatrix}
\begin{bmatrix}
R(t) & \boldsymbol{p}(t) \\
\mathbf{0}^T & 1
\end{bmatrix}
\\
\end{align*}
\]

如果令:

\[\xi(t) =
\begin{bmatrix}
[\boldsymbol{\omega}(t)]_\times & \boldsymbol{v}_{\text{body}}(t) \\
\mathbf{0}^T & 0
\end{bmatrix}
\]

那么有:

\[\frac{d}{dt} T(t) = \xi(t) T(t)
\]

显然,在初始时刻欧式变换矩阵为单位矩阵,即\(T(0) = I_4\),那么有微分方程:

\[\frac{d}{dt} T(t) = \xi(t) T(t), \quad T(0) = I_4
\]

这就又回到了上一篇文章《详解SLAM中的李群和李代数(上)》中介绍的一阶线性微分方程,其解为:

\[T(t) = \exp(\xi(t) t)
\]

这就是从李代数\(\mathfrak{se}(3)\)到李群\(SE(3)\)的指数映射,矩阵\(\xi(t)\)就是\(SE(3)\)的李代数中集合的元素。

进一步的,去掉自变量\(t\),将\(\boldsymbol{v}_{\text{body}}\)重命名为\(\boldsymbol{v}\),李代数 \(\mathfrak{se}(3)\) 是所有形如:

\[\xi =
\begin{bmatrix}
[\boldsymbol{\omega}]_\times & \boldsymbol{v} \\
\mathbf{0}^T & 0
\end{bmatrix}
\in \mathbb{R}^{4 \times 4}
\]

的矩阵构成的集合,其中 \(\boldsymbol{\omega}, \boldsymbol{v} \in \mathbb{R}^3\)。

将其简记为:

\[\mathfrak{se}(3) = \left\{
\begin{bmatrix}
[\boldsymbol{\omega}]_\times & \boldsymbol{v} \\
\mathbf{0}^T & 0
\end{bmatrix}
\,\middle|\, \boldsymbol{\omega}, \boldsymbol{v} \in \mathbb{R}^3 \right\}
\]

如果,\(\mathfrak{se}(3)\)也可以用一个六维向量来表示:

\[\boldsymbol{\eta} =
\begin{bmatrix}
\boldsymbol{\omega} \\
\boldsymbol{v}
\end{bmatrix}
\in \mathbb{R}^6
\]

并定义对应的映射:

\[\hat{\boldsymbol{\eta}} =
\begin{bmatrix}
[\boldsymbol{\omega}]_\times & \boldsymbol{v} \\
\mathbf{0}^T & 0
\end{bmatrix}
\]

所以\(\mathfrak{se}(3)\)也可以表示为:

\[\mathfrak{se}(3) = \left\{ \hat{\boldsymbol{\eta}} \,\middle|\, \boldsymbol{\eta} \in \mathbb{R}^6 \right\}
\]

2.5 \(\mathfrak{se}(3)\)的李括号

\(\mathfrak{se}(3)\) 中的李括号仍然是矩阵换位子。对于任意两个 \(\xi_1, \xi_2 \in \mathfrak{se}(3)\),有:

\[[\xi_1, \xi_2] = \xi_1 \xi_2 - \xi_2 \xi_1
\]

如果写成向量形式 \(\boldsymbol{\eta}_1 = (\boldsymbol{\omega}_1, \boldsymbol{v}_1)^T\), \(\boldsymbol{\eta}_2 = (\boldsymbol{\omega}_2, \boldsymbol{v}_2)^T\),则可以推出:

\[[\boldsymbol{\eta}_1, \boldsymbol{\eta}_2] =
\begin{bmatrix}
\boldsymbol{\omega}_1 \times \boldsymbol{\omega}_2 \\
\boldsymbol{\omega}_1 \times \boldsymbol{v}_2 - \boldsymbol{\omega}_2 \times \boldsymbol{v}_1
\end{bmatrix}
\]

这是通过直接计算矩阵乘积利用叉积与反对称矩阵的关系推导出来的,估计也不是很常用,因此略掉推导过程。

3 指数映射

3.1 预备知识

3.1.1 泰勒公式

设一个函数 \(f(x)\) 在点 \(x = a\) 处无穷可导,那么它可以展开为如下形式的泰勒级数

\[f(x) = \sum_{n=0}^\infty \frac{f^{(n)}(a)}{n!}(x - a)^n
\]

这个式子的意思是:用无穷多个多项式项来逼近原函数 \(f(x)\)。

如果取 \(a = 0\),则称为麦克劳林级数(Maclaurin series)

\[f(x) = \sum_{n=0}^\infty \frac{f^{(n)}(0)}{n!}x^n
\]

根据这个定义,有:

\[e^x = \sum_{n=0}^\infty \frac{1}{n!}x^n = 1 + x + \frac{x^2}{2!} + \frac{x^3}{3!} + \cdots \\
\sin(x) = x - \frac{x^3}{3!} + \frac{x^5}{5!} - \frac{x^7}{7!} + \cdots = \sum_{n=0}^\infty (-1)^n \frac{x^{2n+1}}{(2n+1)!}\\
\cos(x) = 1 - \frac{x^2}{2!} + \frac{x^4}{4!} - \frac{x^6}{6!} + \cdots = \sum_{n=0}^\infty (-1)^n \frac{x^{2n}}{(2n)!}
\]

3.1.2 反对称矩阵的幂次规律

给定一个单位三维向量 \(\boldsymbol{\omega} = (\omega_1, \omega_2, \omega_3)^T\),其对应的反对称矩阵 \([\boldsymbol{\omega}]_\times\) 为:

\[[\boldsymbol{\omega}]_\times =
\begin{bmatrix}
0 & -\omega_3 & \omega_2 \\
\omega_3 & 0 & -\omega_1 \\
-\omega_2 & \omega_1 & 0
\end{bmatrix}
\]

假设 \(A = [\boldsymbol{\omega}]_\times\),我们首先计算 \(A^2\):

\[A^2 = [\boldsymbol{\omega}]_\times^2 =
\begin{bmatrix}
0 & -\omega_3 & \omega_2 \\
\omega_3 & 0 & -\omega_1 \\
-\omega_2 & \omega_1 & 0
\end{bmatrix}
\begin{bmatrix}
0 & -\omega_3 & \omega_2 \\
\omega_3 & 0 & -\omega_1 \\
-\omega_2 & \omega_1 & 0
\end{bmatrix}
\]

进行矩阵乘法运算后得到:

\[A^2 =
\begin{bmatrix}
-\omega_2^2 - \omega_3^2 & \omega_1 \omega_2 & \omega_1 \omega_3 \\
\omega_1 \omega_2 & -\omega_1^2 - \omega_3^2 & \omega_2 \omega_3 \\
\omega_1 \omega_3 & \omega_2 \omega_3 & -\omega_1^2 - \omega_2^2
\end{bmatrix}
\]

因为这里\(\boldsymbol{\omega}\) 是单位向量(即 \(\omega_1^2 + \omega_2^2 + \omega_3^2 = 1\)),则:

\[A^2 =
\begin{bmatrix}
-(1 - \omega_1^2) & \omega_1 \omega_2 & \omega_1 \omega_3 \\
\omega_1 \omega_2 & -(1 - \omega_2^2) & \omega_2 \omega_3 \\
\omega_1 \omega_3 & \omega_2 \omega_3 & -(1 - \omega_3^2)
\end{bmatrix}
\]

进一步简化为:

\[A^2 =
\begin{bmatrix}
-\omega_1^2 & \omega_1 \omega_2 & \omega_1 \omega_3 \\
\omega_1 \omega_2 & -\omega_2^2 & \omega_2 \omega_3 \\
\omega_1 \omega_3 & \omega_2 \omega_3 & -\omega_3^2
\end{bmatrix}
= \boldsymbol{\omega} \boldsymbol{\omega}^T - \mathbf{I}
\]

这里 \(\mathbf{I}\) 是单位矩阵,\(\boldsymbol{\omega} \boldsymbol{\omega}^T\) 是外积矩阵。

接下来计算 \(A^3\):

\[A^3 = A \cdot A^2 = [\boldsymbol{\omega}]_\times \cdot (\boldsymbol{\omega} \boldsymbol{\omega}^T - \mathbf{I})
\]

注意到 \([\boldsymbol{\omega}]_\times \boldsymbol{\omega} = \mathbf{0}\) (因为反对称矩阵作用于自身方向的向量结果为零向量),因此:

\[A^3 = -[\boldsymbol{\omega}]_\times + [\boldsymbol{\omega}]_\times (\boldsymbol{\omega} \boldsymbol{\omega}^T) = -[\boldsymbol{\omega}]_\times = -A
\]

所以:

\[A^3 = -A
\]

再来看计算\(A^4\):

\[A^4 = A \cdot A^3 = A \cdot (-A) = -A^2
\]

根据前面的结果 \(A^2 = \boldsymbol{\omega} \boldsymbol{\omega}^T - \mathbf{I}\),于是:

\[A^4 = -(\boldsymbol{\omega} \boldsymbol{\omega}^T - \mathbf{I}) = -A^2
\]

因此:

\[A^4 = -A^2
\]

最后计算 \(A^5\):

\[A^5 = A \cdot A^4 = A \cdot (-A^2) = -A^3 = -(-A) = A
\]

所以:

\[A^5 = A
\]

通过上述推导,我们可以总结出反对称矩阵 \(A = [\boldsymbol{\omega}]_\times\) 的一些重要幂次规律:

  • \(A = [\boldsymbol{\omega}]_\times\)
  • \(A^2 = \boldsymbol{\omega} \boldsymbol{\omega}^T-\mathbf{I}\)
  • \(A^3 = -A\)
  • \(A^4 = -A^2\)
  • \(A^5 = A\)
  • \(A^6 = A^2\)
  • \(A^7 = -A\)
  • \(A^8 = -A^2\)

这些规律表明,对于反对称矩阵 \(A = [\boldsymbol{\omega}]_\times\),其高次幂具有周期性,并且可以归约为 \(A\)、\(A^2\) 和 \(-A\) 的线性组合。

3.1.3 Chasles定理

从\(SO(3)\)李代数的推导可以知道,任何旋转变换可以等效为绕某个固定轴的旋转来表示,那么欧式变换(刚体变换)是不是也有类似的性质呢?确实如此,刚体运动学的Chasles定理指出:

任何一个刚体的位移都可以视为绕某条直线(称为螺旋轴或瞬时转动轴)的旋转加上沿这条直线的平移。

换句话说,任何刚体的位移可以分解为绕一个特定方向的旋转加上沿着这个方向的平移。这种组合运动被称为螺旋运动,这条特定方向确定的直线就是螺旋轴。在刚体运动过程中,这条直线是保持不变的。

根据前面\(SE(3)\)李代数推导可知,欧式变换可以表示为:

\[T = \exp(\xi)
\]

如同旋转变换的旋转向量可以分解成单位轴向量和旋转角度一样,螺旋运动也可以由以下两个量来确定:

  1. 单位螺旋轴 \(\hat{s}\):描述运动的方向(包括旋转轴和平移方向)。
  2. 标量 \(\theta\):描述沿这个螺旋轴运动了多远。

即:

\[\xi = \hat{s} \theta
\]

于是整个运动就可以写成:

\[T = \exp(\hat{s} \theta)
\]

又因为:

\[\xi =
\begin{bmatrix}
[\boldsymbol{\omega}]_\times & \boldsymbol{v} \\
\mathbf{0}^T & 0
\end{bmatrix}
\in \mathbb{R}^{4 \times 4}
\]

对这个矩阵提取\(\theta\),很显然:

  • \([\boldsymbol{\omega}]_\times = \theta \cdot [\boldsymbol{\omega}_{\text{unit}}]_\times\)
  • \(\mathbf{0}^T = \theta \cdot \mathbf{0}^T\)
  • \(0 = \theta \cdot 0\)

那么必然存在单位螺旋轴 \(\hat{s}\) 的矩阵:

\[\hat{s} =
\begin{bmatrix}
[\boldsymbol{\omega}_{\text{unit}}]_\times & \boldsymbol{v}_{\text{unit}} \\
\mathbf{0}^T & 0
\end{bmatrix}
\]

能满足\(\boldsymbol{v} = \theta \cdot \boldsymbol{v}_{\text{unit}}\)。这里的\(\boldsymbol{\omega}_{\text{unit}}\) 是单位旋转轴,\(\boldsymbol{v}_{\text{unit}}\) 是单位平移速度向量。

可能在这里有的读者会觉得有点难以理解,平移和旋转是两个独立的运动,为什么会共享同一个参数\(\theta\)?其实这里把螺旋运动理解成向一个木板拧螺丝就行了,拧螺丝拧的角度越大,打进木板的距离就越远。这里的\(\theta\)是一个统一的“运动参数”,它决定了旋转了多少、平移了多少,就像是“沿某个固定运动方向走多远”的度量。

数学上的表达总结如下:

\[{\xi} = \theta \cdot \hat{s}
= \theta \cdot
\begin{bmatrix}
[\boldsymbol{\omega}_{\text{unit}}]_\times & \boldsymbol{v}_{\text{unit}} \\
\mathbf{0}^T & 0
\end{bmatrix}=
\begin{bmatrix}
[\boldsymbol{\omega}]_\times & \boldsymbol{v} \\
\mathbf{0}^T & 0
\end{bmatrix}
\]

3.2 \(SO(3)\)上的指数映射

根据前面的推导,已知旋转矩阵 \(R \in SO(3)\) 可以通过李代数 \(\mathfrak{so}(3)\) 的指数映射来表示:

\[R = \exp([\boldsymbol{\omega}]_\times)
\]

这里的\(\boldsymbol{\omega}\)就是旋转向量,我们知道向量都等于自身的单位向量与模长的乘积,那么可以令\(a\)为\(\boldsymbol{\omega}\)的单位向量,\(\theta\) 为 \(\boldsymbol{\omega}\)的模长。也即是:

\[\boldsymbol{\omega} = {\theta}a
\]

取反对称矩阵,有:

\[[\boldsymbol{\omega}]_\times = [{\theta}a]_\times = {\theta}[a]_\times
\]

令\(A = [a]_\times\),根据预备知识介绍的泰勒公式,将标量指数函数推广到矩阵指数函数上,有:

\[R = \exp({\theta}A) = \sum_{n=0}^\infty \frac{({\theta}A)^n}{n!}
\]

将 \(\exp(\theta A)\) 展开为泰勒级数:

\[\exp(\theta A) = I + \theta A + \frac{\theta^2}{2!} A^2 + \frac{\theta^3}{3!} A^3 + \frac{\theta^4}{4!} A^4 + \cdots
\]

利用预备知识中提到的反对称矩阵的(如 \(A^3 = -A\), \(A^4 = -A^2\) 等),我们可以把所有项都写成 \(I, A, A^2\) 的线性组合:

\[\begin{aligned}
\exp(\theta A) &= I + \theta A + \frac{\theta^2}{2!} A^2
-\frac{\theta^3}{3!} A
-\frac{\theta^4}{4!} A^2
+\frac{\theta^5}{5!} A
+\cdots \\
&= I + \left(\theta - \frac{\theta^3}{3!} + \frac{\theta^5}{5!} - \cdots\right) A \\
&\quad + \left(\frac{\theta^2}{2!} - \frac{\theta^4}{4!} + \cdots\right) A^2
\end{aligned}
\]

再利用预备知识中的正弦和余弦的泰勒公式展开:

  • \(\sin\theta = \theta - \frac{\theta^3}{3!} + \frac{\theta^5}{5!} - \cdots\)
  • \(1 - \cos\theta = \frac{\theta^2}{2!} - \frac{\theta^4}{4!} + \cdots\)

所以:

\[\exp(\theta A) = I + \sin\theta \cdot A + (1 - \cos\theta) \cdot A^2
\]

也可以表达为:

\[\exp(\theta [a]_\times) = \cos\theta I + (1 - \cos\theta) a{a}^T +\sin\theta [a]_\times
\]

其中,\(a\)为\(\mathfrak{so}(3)\)集合中元素\(\boldsymbol{\omega}\)的单位向量,\(A\)为\(a\)的反对称矩阵,\(\theta\) 为\(\boldsymbol{\omega}\)的模长。其实这里的\(\boldsymbol{\omega}\)就是旋转矩阵的旋转向量,这个公式也就是著名的罗德里格公式。

通过以上推导表明,\(\mathfrak{so}(3)\)其实就是旋转向量组成的向量空间,指数映射就是罗德里格公式。通过罗德里格公式,任意一个向量都可以对应到\(SO(3)\)中的旋转矩阵。反之,将\(SO(3)\)对应到\(\mathfrak{so}(3)\)就是对数映射,当然这里就是不用列出对数表达式,在《视觉SLAM十四讲》的第3讲中介绍了更简便的计算方法:

\[\theta = \arccos\left( \frac{\operatorname{tr}(R) - 1}{2} \right),Ra = a
\]

在书上没有给出\(Ra = a\)的具体解的过程,因为可能有多个解的情况,表述起来比较麻烦,大概的求解过程是:

步骤 内容
1. 计算旋转角度:\(\theta = \arccos\left( \frac{\operatorname{tr}(R) - 1}{2} \right)\)
2. 若 \(\theta = 0\):返回 \(\boldsymbol{\omega} = \mathbf{0}\) 或任意方向
3. 若 \(\theta = \pi\):构造 \(\boldsymbol{\omega} \boldsymbol{\omega}^T = \frac{1}{2}(R + I)\),取最大特征值对应的单位特征向量
4. 否则:计算反对称部分 \(S = \frac{1}{2}(R - R^T)\),提取 \(\boldsymbol{\omega} = \frac{1}{2\sin\theta} \begin{bmatrix} S_{32} \\ S_{13} \\ S_{21} \end{bmatrix}\),然后归一化

其实从求解对数映射的解就可以看出,指数映射是一个满射,而不是单射。也就是任意\(SO(3)\)集合中的元素,都能在\(\mathfrak{so}(3)\)中找到一个元素与之对应;但是可能存在多个\(\mathfrak{so}(3)\)中的元素对应于一个\(SO(3)\)集合中的元素。其实这个不难理解,旋转是有周期性的,旋转360度和不转是一样的,只有范围限定在\((-\pi, +\pi]\)之间,两者才可以一一对应。

3.3 \(SE(3)\)上的指数映射

根据前面的推导,特殊欧式群\(SE(3)\)的李代数\(\mathfrak{se}(3)\),可以用一个4维矩阵表示,包含旋转和平移信息:

\[\xi =
\begin{bmatrix}
[\boldsymbol{\omega}]_\times & \boldsymbol{v} \\
\mathbf{0}^T & 0
\end{bmatrix}
\in \mathbb{R}^{4 \times 4}
\]

其中 \(\boldsymbol{\omega}\) 就是特殊正交群\(SO(3)\)的李代数的向量表示,\(\boldsymbol{v}\) 表示平移速度。

根据准备知识中介绍的Chasles定理,有:

\[T = \exp(\xi) = \exp(\hat{s} \theta)
\]

其中\(\hat{s}\)是单位螺旋轴矩阵:

\[\hat{s} =
\begin{bmatrix}
[\boldsymbol{\omega}_{\text{unit}}]_\times & \boldsymbol{v}_{\text{unit}} \\
\mathbf{0}^T & 0
\end{bmatrix}
\]

\(\theta\)则描述沿这个螺旋轴运动了多远。使用泰勒公式展开指数函数,有:

\[\exp({\xi}) = \sum_{n=0}^\infty \frac{(\hat{s}\theta)^n}{n!} = I + \hat{s} \theta + \frac{(\hat{s} \theta)^2}{2!} + \frac{(\hat{s} \theta)^3}{3!} + \cdots
\]

因此关键在于计算每一项\((\hat{s}\theta)^n\)。对于第0项:

\[I =
\begin{bmatrix}
I & \mathbf{0} \\
\mathbf{0}^T & 1
\end{bmatrix}
\]

对于第1项,前面求\(SO(3)\)的时候已经定义\(A = [\boldsymbol{\omega}_{\text{unit}}]_\times\),则:

\[\hat{s}\theta =
\begin{bmatrix}
\theta A & \theta \boldsymbol{v}_{\text{unit}} \\
\mathbf{0}^T & 0
\end{bmatrix} =
\begin{bmatrix}
\theta A & \boldsymbol{v} \\
\mathbf{0}^T & 0
\end{bmatrix}
\]

对于第2项:

\[\frac{(\hat{s} \theta)^2}{2!} = \frac{1}{2!}
\begin{bmatrix}
(\theta A)^2 & \theta A \boldsymbol{v} \\
\mathbf{0}^T & 0
\end{bmatrix}=
\begin{bmatrix}
\frac{(\theta A)^2}{2!} & \frac{(\theta A) \boldsymbol{v}}{2!} \\
\mathbf{0}^T & 0
\end{bmatrix}
\]

对于第3项:

\[\frac{(\hat{s} \theta)^3}{3!} = \frac{1}{3!}
\begin{bmatrix}
(\theta A)^3 & (\theta A)^2 \boldsymbol{v} \\
\mathbf{0}^T & 0
\end{bmatrix}=
\begin{bmatrix}
\frac{(\theta A)^3}{3!} & \frac{(\theta A)^2 \boldsymbol{v}}{3!} \\
\mathbf{0}^T & 0
\end{bmatrix}
\]

以此类推,把所有项加起来,分块相加,有:

\[\begin{align*}
\exp({\xi}) &= I + \hat{s} \theta + \frac{(\hat{s} \theta)^2}{2!} + \frac{(\hat{s} \theta)^3}{3!} + \cdots \\
&= \begin{bmatrix}
I & \mathbf{0} \\
\mathbf{0}^T & 1
\end{bmatrix} +
\begin{bmatrix}
\theta A & \boldsymbol{v} \\
\mathbf{0}^T & 0
\end{bmatrix} +
\begin{bmatrix}
\frac{(\theta A)^2}{2!} & \frac{(\theta A) \boldsymbol{v}}{2!} \\
\mathbf{0}^T & 0
\end{bmatrix}+
\begin{bmatrix}
\frac{(\theta A)^3}{3!} & \frac{(\theta A)^2 \boldsymbol{v}}{3!} \\
\mathbf{0}^T & 0
\end{bmatrix} + \cdots\\
&=\begin{bmatrix}
\sum_{n=0}^\infty \frac{({\theta}A)^n}{n!} & \sum_{n=1}^\infty \frac{(\theta A)^{n-1} \boldsymbol{v}}{n!} \\
\mathbf{0}^T & 1 \\
\end{bmatrix}
\end{align*}
\]

对于这个结果的左上角元素,其实就是\(SO(3)\)的指数映射:

\[\exp(\theta A) = \sum_{n=0}^\infty \frac{({\theta}A)^n}{n!}
\]

而对于右上角元素,可以将索引平移一下,令 \(k = n - 1\),则:

\[\sum_{k=0}^\infty \frac{(\theta A)^k \boldsymbol{v}}{(k+1)!}
\]

因此,\(SE(3)\)指数映射可以写成:

\[\exp({\xi}) =
\begin{bmatrix}
\exp(\theta A) & \sum_{n=0}^\infty \frac{(\theta A)^n \boldsymbol{v}}{(n+1)!} \\
\mathbf{0}^T & 1
\end{bmatrix}
\]

但是为了进一步方便表达右上角元素的表达式,引入左不变雅可比矩阵\(J\):

\[J = \sum_{n=0}^\infty \frac{(\theta A)^n}{(n+1)!}
\]

接下来利用预备知识中介绍的反对称矩阵的周期性规律计算\(J\):

\[\begin{align*}
J &= I + \frac{\theta}{2!}{A} + \frac{{\theta}^{2}}{3!}{A^2} + \frac{{\theta}^{3}}{4!}{A^3} + \cdots \\
&= I + \frac{1}{\theta}(\frac{\theta^{2}}{2!} - \frac{{\theta}^{4}}{4!} + \cdots) {A} + \frac{1}{\theta}(\frac{{\theta}^{3}}{3!}-\frac{{\theta}^{5}}{5!} + \cdots) {A^2}
\end{align*}
\]

利用预备知识中的正弦和余弦的泰勒公式展开:

  • \(\theta - \sin\theta = \frac{\theta^3}{3!} + \frac{\theta^5}{5!} - \cdots\)
  • \(1 - \cos\theta = \frac{\theta^2}{2!} - \frac{\theta^4}{4!} + \cdots\)

可以得到:

\[J = I + \frac{1 - \cos\theta}{\theta} A + \frac{\theta - \sin\theta}{\theta} A^2
\]

由于\(A = [a]_\times\),因此\(J\)也可以表达为:

\[\begin{align*}
J &= I + \frac{1 - \cos\theta}{\theta} [a]_\times + \frac{\theta - \sin\theta}{\theta} ({a}{a^T}-\mathbf{I})\\
&= \frac{\sin\theta}{\theta}I + (1 - \frac{\sin\theta}{\theta}){a}{a^T} + \frac{1 - \cos\theta}{\theta} [a]_\times\\
\end{align*}
\]

\(SE(3)\)的指数映射最终结果如下:

\[\exp({\xi}) =
\begin{bmatrix}
\exp([\boldsymbol{\omega}]_\times) & J\boldsymbol{v} \\
\mathbf{0}^T & 1
\end{bmatrix}
\]

其中:

  • 旋转部分 \(R = \exp([\boldsymbol{\omega}]_\times)\)即\(SO(3)\)的指数映射。
  • 平移部分 \(\boldsymbol{t} = J\boldsymbol{v}\), 其中J是左不变雅可比矩阵:
    \[J = I + \frac{1 - \cos\theta}{\theta} A + \frac{\theta - \sin\theta}{\theta}
    \]

    \[J=\frac{\sin\theta}{\theta}I + (1 - \frac{\sin\theta}{\theta}){a}{a^T} + \frac{1 - \cos\theta}{\theta} [a]_\times
    \]

与\(SO(3)\)类似,也可以推导出\(SE(3)\)到\(\mathfrak{se}(3)\)的对数映射。旋转部分可以按照之前介绍的\(SO(3)\)到\(\mathfrak{so}(3)\)的对数映射求出;平移部分由于\(J\)已知(通过左不变雅可比矩阵求得),可以解\(\boldsymbol{t} = J\boldsymbol{v}\)这个线性方程组来解得。具体数学表达式如下:

\[\theta = \arccos\left( \frac{\operatorname{tr}(R) - 1}{2} \right),Ra = a,\boldsymbol{t} = J\boldsymbol{v}
\]

4 总结

作为本篇的结束,整理了一张特殊正交群的内容的表格,如下所示:

内容 特殊正交群
李群 \(SO(3) = \{ R \in \mathbb{R}^{3\times3} \mid R^T R = I, \det(R) = 1\}\)
李代数 \(\mathfrak{so}(3) = \left\{ [\boldsymbol{\omega}]_\times \in \mathbb{R}^{3 \times 3} \mid {\omega} \in \mathbb{R}^3 \right\}\)
向量表示 \({\omega} \in \mathbb{R}^3\)
李括号 \([[\boldsymbol{\omega}_1]_\times, [\boldsymbol{\omega}_2]_\times] = [\boldsymbol{\omega}_1]_\times [\boldsymbol{\omega}_2]_\times - [\boldsymbol{\omega}_2]_\times [\boldsymbol{\omega}_1]_\times\) 或者 叉乘\(\times\)
指数映射 \(\exp(\theta [a]_\times) = \cos\theta I + (1 - \cos\theta) a{a}^T +\sin\theta [a]_\times\)
对数映射 \(\theta = \arccos\left( \frac{\operatorname{tr}(R) - 1}{2} \right),Ra = a\)

特殊欧式群的内容则如下表所示:

内容 特殊欧式群
李群 \(SE(3)=\bigg\{ T = \begin{bmatrix} R & t \\ 0 & 1 \end{bmatrix} \in \mathbb{R}^{4\times4} \mid R \in SO(3) ,t \in \mathbb{R}^3 \bigg\}\)
李代数 \(\mathfrak{se}(3) = \left\{\xi = \begin{bmatrix}[\boldsymbol{\omega}]_\times & \boldsymbol{v} \\ \mathbf{0}^T & 0 \\ \end{bmatrix} \in \mathbb{R}^{4 \times 4} \mid \boldsymbol{\omega}, \boldsymbol{v} \in \mathbb{R}^3 \right\}\)
向量表示 \(\boldsymbol{\eta} = \begin{bmatrix} \boldsymbol{\omega} \\ \boldsymbol{v} \end{bmatrix} \in \mathbb{R}^6\)
李括号 \([\xi_1, \xi_2] = \xi_1 \xi_2 - \xi_2 \xi_1\) 或者 \([\boldsymbol{\eta}_1, \boldsymbol{\eta}_2] = \begin{bmatrix} \boldsymbol{\omega}_1 \times \boldsymbol{\omega}_2 \\ \boldsymbol{\omega}_1 \times \boldsymbol{v}_2 - \boldsymbol{\omega}_2 \times \boldsymbol{v}_1 \end{bmatrix}\)
指数映射 \(\exp({\xi}) = \begin{bmatrix} \exp([\boldsymbol{\omega}]_\times) & J\boldsymbol{v} \\ \mathbf{0}^T & 1 \\ \end{bmatrix}\\J=\frac{\sin\theta}{\theta}I + (1 - \frac{\sin\theta}{\theta}){a}{a^T} + \frac{1 - \cos\theta}{\theta} [a]_\times\)
对数映射 \(\theta = \arccos\left( \frac{\operatorname{tr}(R) - 1}{2} \right),Ra = a,\boldsymbol{t} = J\boldsymbol{v}\)

注意可能符号与书上的符号略有差异,读者可以自行对照参考。

另外,笔者也想说一点内容之外的感受。除非具有相当的抽象能力,第一次接触到像李群和李代数这样的知识是很难理解到位的,因为你完全没有应用这些知识的场景,再怎么认真看书,也不过是泛泛而过。在这种情况下,不如好好推导一遍其中的原理,举一些实际的例子来加深映像。另外,这些数学知识既高深又抽象(笔者大概翻阅了一下《李理论》的相关知识,本文的这些推导在行家面前估计不值一哂),我们也不用过多去纠结,主打一个能自己说服自己即可。如果读者只是像我一样的普通人,可能需要在以后的应用和实践中来更深入地理解这些知识了;目前我们需要做的,就是不要排斥这个新的工具,然后理解它,利用它——真理之道,就在其中。

详解SLAM中的李群和李代数(中)的更多相关文章

  1. 从零开始一起学习SLAM | 为啥需要李群与李代数?

    很多刚刚接触SLAM的小伙伴在看到李群和李代数这部分的时候,都有点蒙蒙哒,感觉突然到了另外一个世界,很多都不自觉的跳过了,但是这里必须强调一点,这部分在后续SLAM的学习中其实是非常重要的基础,不信你 ...

  2. 《HTML5网页开发实例详解》连载(四)HTML5中的FileSystem接口

    HTML 5除了提供用于获取文件信息的File对象外,还添加了FileSystem相关的应用接口.FileSystem对于不同的处理功能做了细致的分类,如用于文件读取和处理的FileReader和Fi ...

  3. MSScriptControl详解(可实现在C#等语言中调用JAVASCRIPT代码)

    ScriptControl接口 属性名称 类型 备注 AllowUI BOOL 检测是否允许运行用户的接口元素.如果为False,则诸如消息框之类的界面元素不可见. CodeObject Object ...

  4. 共享池之八:软解析、硬解析、软软解析 详解一条SQL在library cache中解析涉及的锁

    先来张大图: 结合上图来说明一下解析的各个步骤涉及的锁. 软解析.硬解析.软软解析区别的简单说明: 为了将用户写的sql文本转化为oracle认识的且可执行的语句,这个过程就叫做解析过程. 解析分为硬 ...

  5. 共享内存shared pool (5):详解一条SQL在library cache中解析

    前面介绍的 shared pool,library cache结构,都是为了说明一条SQL是如何被解析的.先看下面的图: 图中涉及的各结构简单介绍 父HANDLE,里面有父游标堆0的地址.. 父游标堆 ...

  6. python3多线程应用详解(第四卷:图解多线程中LOCK)

    先来看下图形对比: 发现没有这种密集型计算的任务中,多线程没有穿行的速率快,原因就是多线程在线程切换间也是要耗时的而密集型计算任务执行时几乎没以偶IO阻塞,这样你说谁快

  7. 3dTiles 数据规范详解[3] 内嵌在瓦片文件中的两大数据表

    转载请声明出处:全网@秋意正寒 零.本篇前言 说实话,我很纠结是先介绍瓦片的二进制数据文件结构,还是先介绍这两个重要的表.思前想后,我决定还是先介绍这两个数据表. 因为这两个表不先给读者灌输,那么介绍 ...

  8. 详解线程池的作用及Java中如何使用线程池

    服务端应用程序(如数据库和 Web 服务器)需要处理来自客户端的高并发.耗时较短的请求任务,所以频繁的创建处理这些请求的所需要的线程就是一个非常消耗资源的操作.常规的方法是针对一个新的请求创建一个新线 ...

  9. 视觉SLAM基础-李群和李代数

    李群和李代数 目录 李群和李代数 引言 1.0 李群 1.1 群 1.2 李群基础定义 2.0李代数 2.1 引出 2.2 李代数的定义 2.3 李代数 \(so(3)\) 2.4 李代数 \(se( ...

  10. mysql中event的用法详解

    一.基本概念mysql5.1版本开始引进event概念.event既“时间触发器”,与triggers的事件触发不同,event类似与linux crontab计划任务,用于时间触发.通过单独或调用存 ...

随机推荐

  1. 在使用HOperatorSet.Draw忘记点击右键结束方法无法关闭窗体问题如何规避

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/17270056.html 可以在离开窗体或者关闭窗体事件中调用HOperatorSet.HIOC ...

  2. Android应用禁止屏幕休眠的3种方法

    做android应用开发时,有时需要在应用前台运行时,禁止休眠,以下几种方法供参考. 方法一:持有wakelock 添加休眠锁,休眠锁必须成对出现. private wakelock mwakeloc ...

  3. java 8 lamdba 表达式list集合的BigDecimal求和操作

  4. 企业付款到零钱(微信小程序提现,用户提现到零钱)

    pom 增加 <dependency> <groupId>com.github.binarywang</groupId> <artifactId>wei ...

  5. 腾讯解禁 QQ 极速版,且看我收集的最全 QQ 各类版本

    因为利益关系,腾讯早就限制QQ极速版的登录了,近日居然解除限制了,面对越来越臃肿的QQ,我给大伙准备了几十个版本的QQ,总有一个适合你. QQ版本合集 给大伙们收集了QQ版本合集,分别有历史版本.精简 ...

  6. 赶快检查,木马可能已经植入服务器,Redis未授权访问漏洞记录,redis的key值出现backup要谨慎

    问题描述:为图省事,很多时候我们在使用redis的时候会使用默认空密码,这就增加了安全隐患,如果有下属情况,那赶快去检查下redis,木马或许已经植入服务器,应尽快处理: 1.redis绑定在 0.0 ...

  7. 关于我第二周学习kotlin这门语言

    有关kotlin的知识点: 在学习lambda之前,我们先了解一下什么是lambda,简答来说就是一小段代码块,并且我们可以将这个代码块在函数之间传递,这是函数式编程的一个重要特性. 通常我们会需要一 ...

  8. 搭建个人多机器ssh连接平台

    最近新配了个主机,有了多个设备,ssh连接的功能可以优化很多体验,便又开始鼓捣.以前都是windows连各种linux,比较方便:这次是在windows之间,还是小查了好一会儿,留个记录 SSH连接的 ...

  9. Basics of using bash, and shell tools for covering several of the most common tasks

    Basics of using bash, and shell tools for covering several of the most common tasks Introduction ‍ M ...

  10. FastAPI依赖注入:参数共享与逻辑复用

    扫描二维码关注或者微信搜一搜:编程智域 前端至全栈交流与成长 第一章:依赖注入核心原理 1.1 依赖树构建机制 from fastapi import Depends def auth_service ...