李群李代数是视觉SLAM里几乎每个人都要翻一次的高墙。很多同学学到第三讲就开始劝退,我当初也一样,盯着SO(3)、se(3)这些符号看了两天,完全想不明白"为什么要这么折腾"。但说实话,这一讲是整个SLAM后端优化的地基,搞不懂它,之后看BA、看位姿图优化都是云里雾里。这篇笔记我按自己的理解重新梳理了一遍核心概念,附带了课后典型习题的详细求解思路,最后补充了一些动手实操时容易踩的坑。
1. 李群与李代数到底在解决什么问题
1.1 旋转矩阵的"加法不封闭"困局
先问一个最基础的问题:为什么要引入李群李代数?答案要从旋转矩阵的运算性质说起。我们知道旋转矩阵构成的特殊正交群SO(3)满足两个关键性质:正交性($R^T R = I$)和行列式为1($\det(R) = 1$)。这意味着两个旋转矩阵相乘的结果仍然是旋转矩阵,群结构对乘法是封闭的。但问题来了——两个旋转矩阵相加,结果还是旋转矩阵吗?答案显然是否定的。
这就是SLAM中最麻烦的一件事。我们在优化位姿时,本质上是在调整旋转矩阵的数值,让重投影误差最小化。最自然的做法是像优化普通向量一样,给当前的$R$加上一个增量$\Delta R$,然后继续迭代。但$R + \Delta R$根本不在SO(3)上,它的正交性和行列式约束全被破坏了。你强行把加法的结果当作旋转矩阵使用,很快就会得到一堆毫无意义的数值。
所以问题的核心就变成了:如何在保持旋转矩阵约束的前提下,对旋转量进行正常的加法式优化。李群李代数给出的答案是——把旋转矩阵"换个坐标系"表示,在那个坐标系里可以做加法,算完再映射回来。
1.2 群的定义与李群的结构
要理解李群,先得知道"群"是什么。群是一个集合加上一种运算,满足封闭性、结合律、存在幺元、存在逆元这四个条件。SO(3)和SE(3)都是典型的李群,因为它们除了满足群的性质之外,还具备流形的结构——也就是光滑可微。这句话听起来很抽象,你可以把李群理解成"一个带约束的光滑曲面",每个旋转矩阵都是这个曲面上的一个点,曲面在局部看起来像平直的欧氏空间。
关键洞察来了:既然曲面在每个点附近都"像"平面,那我们就可以在这个局部平面上研究旋转的变化规律。这个局部平面,就是该点处的切空间,而李代数恰恰就是这个切空间。所以李代数并不是什么玄乎的东西,它就是李群在单位元处的切空间,描述了李群局部无穷小的运动方式。
1.3 SLAM中实际使用的李群与李代数
在视觉SLAM中,我们最关心四样东西:$SO(3)$、$SE(3)$以及它们对应的李代数$\mathfrak{so}(3)$、$\mathfrak{se}(3)$。它们的关系可以整理成一张表:
| 名称 | 含义 | 维度 | 代表元素 |
|---|---|---|---|
| $SO(3)$ | 三维旋转矩阵群 | 3 | $R_{3\times3}$ |
| $SE(3)$ | 三维刚体变换矩阵群 | 6 | $T_{4\times4}$ |
| $\mathfrak{so}(3)$ | 旋转矩阵对应的李代数 | 3 | 三维向量$\phi$ |
| $\mathfrak{se}(3)$ | 变换矩阵对应的李代数 | 6 | 六维向量$\xi$ |
有一点要特别注意:$SE(3)$是6维的,但变换矩阵$T$是4×4共16个元素,所以$SE(3)$实际上是一个16维空间中的6维约束流形。这也就是为什么我们不能直接对$T$做加法优化——16个元素之间存在着很强的内在约束,只有6个自由度是真正独立的。
2. 指数映射与对数映射:李代数与李群之间的桥梁
2.1 从旋转向量到旋转矩阵的指数映射
李代数$\mathfrak{so}(3)$里的元素是一个三维向量$\phi$,它的物理意义其实就是旋转向量:方向代表旋转轴,模长代表旋转角度。所以从$\mathfrak{so}(3)$到$SO(3)$的指数映射,本质上是把旋转向量变成旋转矩阵的过程。这个映射的公式就是大名鼎鼎的罗德里格斯公式:
$$R = \cos\theta \cdot I + (1 - \cos\theta) \cdot aa^T + \sin\theta \cdot a^{\wedge}$$
其中$\theta$是旋转向量的模长,$a$是单位旋转轴向量。这个公式完美体现了旋转向量的几何含义。但很多同学并不知道这个公式是怎么来的,这里我用自己的理解推导一遍关键思路。
从矩阵指数的定义出发,$e^{\phi^{\wedge}} = \sum_{n=0}^{\infty} \frac{1}{n!}(\phi^{\wedge})^n$。直接展开这个级数是很难算的。关键在于利用反对称矩阵的一个性质:三维反对称矩阵$A$满足$A^3 = -|A|^2 A$,这里的$|A|$不是矩阵的行列式,而是对应向量的模长。
利用这个性质,$A$的奇数次幂都能化简成$A$的倍数,偶数次幂都能化简成$A^2$的倍数。把无穷级数拆成奇数项和偶数项,就能凑出$\sin\theta$和$\cos\theta$的级数展开式。整个推导过程就像把复杂的指数运算降维成三角函数运算,这也是罗德里格斯公式的精髓所在。
2.2 为什么矩阵指数能收敛成有限项
你可能好奇,一个无限级数求和为什么最后收敛成一个有限项的组合?关键就隐藏在反对称矩阵的幂次周期性上。三维反对称矩阵的平方乘以自身的模长平方等于立方,这使得幂次呈现出3阶循环的特征。换句话说,$A$的幂次只存在两种独立形式:$A$和$A^2$,更高次的幂能不断化简回这两种形式。
这样的性质大大简化了计算。理论上$e^{A}$涉及无穷多次矩阵乘法,但因为幂次的周期性,无穷级数被压缩成了对$\sin$和$\cos$两个标量三角函数的求和。这就是为什么罗德里格斯公式不仅形式优美,而且计算效率极高——每次矩阵指数运算只需要计算两个三角函数值和三次矩阵加法乘法。
2.3 SE(3)的指数映射而不仅仅是平移叠加
$\mathfrak{se}(3)$的情况比$\mathfrak{so}(3)$更复杂。$\mathfrak{se}(3)$中的元素是一个六维向量$\xi = [\rho, \phi]^T$,其中$\rho$是平移相关的分量,$\phi$是旋转分量。它的指数映射可以分块处理:
$$T = \exp(\xi^{\wedge}) = \begin{bmatrix} R & J\rho \ 0 & 1 \end{bmatrix}$$
这里的$R$就是由$\phi$决定的旋转矩阵,$J$是一个3×3的矩阵,通常被称为雅可比矩阵。注意:$J\rho$并不等于$\rho$本身,说明李代数中的平移分量和平移向量之间差了一个$J$变换。这件事在SLAM初始化代码中特别容易出错,很多人直接从$\mathfrak{se}(3)$里取出平移分量去更新位置,结果发现轨迹出现奇怪的漂移,往往就是忽略了$J$的作用。
$J$的具体表达式为:
$$J = \frac{\sin\theta}{\theta}I + \left(1-\frac{\sin\theta}{\theta}\right)aa^T + \frac{1-\cos\theta}{\theta}a^{\wedge}$$
当旋转角度很小时,$J$趋近于单位阵,但这个近似只有在角度趋近于0时才成立。实际处理中位姿变化较大时不能随便做近似。
2.4 对数映射与李代数坐标提取
反过来,从李群到李代数的映射叫对数映射。给定旋转矩阵$R$,可以通过$\theta = \arccos\left(\frac{\mathrm{tr}(R)-1}{2}\right)$求出旋转角,再通过$Rn = n$求出旋转轴。这里$n$是$R$特征值为1时对应的特征向量,换句话说旋转轴就是$R$不变的那条方向。
从$SE(3)$提取$\mathfrak{se}(3)$也是两步:先通过对数映射提取旋转部分得到$\phi$,然后用$t = J\rho$反解出$\rho = J^{-1}t$。$J$的逆有解析表达式,不需要做矩阵求逆。在实际工程里,如果你用的是Sophus库,这些映射都封装成了库函数,不需要手推,但理解原理对调试帮助极大——我遇到过好几次位姿图优化发散,最后查出就是因为手动提取扰动增量时搞错了平移部分的坐标变换。
3. 伴随性质与BCH公式:理解扰动的两种姿势
3.1 李群乘法与李代数加法的对应关系
引入李代数之后有一个快乐:把李群的乘法对应到李代数的加法。但实际计算中这个对应并不完美,存在一个重要的修正公式——BCH公式。用数学公式表达的话,$\exp(A)\exp(B)$并不等于$\exp(A+B)$,中间还差一些高阶项。这个差异在SLAM中不能被忽略,因为它直接决定了优化迭代时的增量更新方式。
BCH公式的完整形式极其复杂,但在SLAM中我们通常只需要两个近似形式:左乘小扰动的近似和右乘小扰动的近似。例如当$\phi_1$很小时,有:
$$\ln(\exp(\phi_1^{\wedge})\exp(\phi_2^{\wedge}))^{\vee} \approx J_l^{-1}(\phi_2)\phi_1 + \phi_2$$
这里的$J_l$就是前面提到的左雅可比矩阵。这个公式的含义是:在旋转矩阵上左乘一个小量对应的李代数增量,近似等于在当前李代数上加一个经过$J_l^{-1}$修正的量。
3.2 左扰动模型与右扰动模型的选择
在视觉SLAM后端的求导中,最常用的操作是对$\exp(\phi^{\wedge}) p$求$\phi$的导数。直接按定义求导需要处理矩阵指数的无穷级数,推导过程非常痛苦。于是有了两个替代方案:左扰动模型和右扰动模型。
左扰动模型的核心想法是给$R$左乘一个微小的旋转扰动$\Delta R = \exp(\delta\phi^{\wedge})$,然后按导数定义展开:
$$\frac{\partial (Rp)}{\partial \phi} = \lim_{\delta\phi\rightarrow 0} \frac{\exp(\delta\phi^{\wedge})\exp(\phi^{\wedge})p - \exp(\phi^{\wedge})p}{\delta\phi} \approx -(Rp)^{\wedge}$$
右扰动模型则是右乘一个微小扰动,结果类似于$-p^{\wedge}R^T$,形式上差了一个旋转的共轭变换。两者都是有效的,只是最终展开的雅可比矩阵形式不同。我个人的经验是:在视觉SLAM残差模型中优先使用左扰动模型,因为它的结论形式更简洁,视觉几何中通常已经约定以左乘旋转为主,这样维护代码不容易混乱。
3.3 扰动模型与导数模型的内在区别
很多同学区分不开"扰动模型"和"导数模型",这是两个完全不同的事。导数模型是对李代数直接求导,相当于把旋转矩阵看成李代数的一个函数,然后对这个函数取微分,结果形式复杂必须借助左雅可比修正。扰动模型则是把李群上的乘法扰动当作变量,通过极限过程求导数,结果形式简单干净。
如果类比理解的话,导数模型相当于你坐在旋转的车上观察整个世界的变化,扰动模型相当于你在车外轻轻推一下车再观察变化。后者在实际工程中更常用,因为它不需要计算复杂的$J_l^{-1}$项,每一步迭代的开销更小。SLAM这种对实时性要求极高的场景,省掉一次3×3矩阵求逆都是值得的。
4. 课后典型习题思路精讲
4.1 旋转矩阵与反对称矩阵的性质验证题
这类题目通常要求证明$(R\phi)^{\wedge} = R\phi^{\wedge}R^T$或者验证$\mathbf{a}^{\wedge}\mathbf{b} = -\mathbf{b}^{\wedge}\mathbf{a}$。核心思路只有一个:把$\phi^{\wedge}$还原到叉乘的定义。叉乘满足$\mathbf{a} \times \mathbf{b} = -\mathbf{b} \times \mathbf{a}$,利用这个反交换性质,很多看似复杂的矩阵恒等式能快速化简。
举个例子,要证明$u^{\wedge}v = -v^{\wedge}u$,你只需要明白左端等于$u \times v$,右端等于$-v \times u$,而$v \times u = -u \times v$,所以两边恰好相等。这类题目主要是帮助你建立"反对称矩阵就是叉乘的矩阵表示"这个直觉,在后续推导李代数雅可比时,这个直觉会反复用到。
4.2 指数映射的级数展开计算题
有一类高频习题是给定具体的$\phi$向量,要求计算$e^{\phi^{\wedge}}$。常规做法是打开罗德里格斯公式硬算,但不推荐,因为计算量大且容易出错。更好的做法是分三步走:先求模长$\theta$,再求单位旋转轴$\mathbf{a}$,最后代入罗德里格斯公式。
这里有一个容易被忽略的小细节:当$\theta$非常接近0时,罗德里格斯公式中的$\sin\theta/\theta$和$(1-\cos\theta)/\theta^2$会面临数值不稳定的问题。工程上的处理方式是当$\theta$小于某个阈值时直接取近似$R \approx I + \phi^{\wedge}$,这个近似在微小旋转下精度完全够用。
4.3 加速度计或角速度积分相关的推导题
第三讲的部分习题会引出旋转的时间导数关系:$\dot{R} = \phi^{\wedge} R$。这其实是旋转矩阵的运动学方程。解题时从$R R^T = I$出发,两边对时间求导得到$\dot{R}R^T + R\dot{R}^T = 0$,这说明$\dot{R}R^T$是反对称的,可以写成某个向量$\omega$的反对称矩阵形式,于是得到$\dot{R} = \omega^{\wedge}R$。这个$\omega$就是角速度的坐标表示。
这个结论在SLAM的IMU预积分中至关重要。IMU测得的角速度正是这里的$\omega$,通过对$\omega^{\wedge}$做指数积分,就能从起始旋转外推出后续时刻的旋转。搞懂了这道题的推导过程,再看IMU预积分公式会轻松一大截,不然那些带时间积分的旋转公式就像是天书。
5. Sophus库实操与常见坑记录
5.1 Sophus的安装与基本用法
李群李代数的手推是一回事,工程落地是另一回事。视觉SLAM中最常用的李代数库是Sophus,它提供了$SO(3)$、$SE(3)$等类型的完整实现,同时支持数值导数验证,学习阶段极为好用。
安装方式通常是通过源码构建,依赖fmt库。在较新的系统中可以用CMake直接集成,也可以在项目CMakeLists中通过FetchContent方式拉取。具体做法不再赘述,但有一条建议:一定要统一fmt版本,我遇到过几次编译报错,最后都是因为fmt新旧版本接口不兼容导致的。
5.2 代码中的典型操作示例
Sophus中常用操作可以整理成以下示例:
#include <sophus/se3.hpp> #include <sophus/so3.hpp> // 由旋转向量构造SO3 Sophus::SO3d R = Sophus::SO3d::exp(phi); // 由旋转矩阵构造SO3 Sophus::SO3d R2(matrix_3d); // 从SO3提取旋转矩阵 Eigen::Matrix3d rotation_matrix = R.matrix(); // 对数映射:从SO3提取李代数 Eigen::Vector3d phi_again = R.log(); // 左扰动更新 Sophus::SO3d updated_R = Sophus::SO3d::exp(delta_phi) * R; // SE3的构造与分解 Sophus::SE3d T(Eigen::Quaterniond(qw, qx, qy, qz), translation_vector); Eigen::Vector3d t = T.translation(); Sophus::SO3d R_part = T.so3();有一个细节值得注意:Sophus中T.translation()返回的是平移向量$t$,也就是$J\rho$之后的结果,而不是$\mathfrak{se}(3)$里的$\rho$。搞混这两个量会导致调试时出现"明明位姿更新的量看起来没问题,但轨迹就是不对"的诡异现象。
5.3 三个最常踩的坑
第一个坑是更新顺序。刚接触SLAM时容易写成R = R * exp(dphi),但优化器要求的是世界系到相机系的位姿,扰动应该作用在合适的侧。视觉几何中重投影误差对相机位姿的雅可比是按左扰动推导的,所以更新时必须用左乘方式,否则整个梯度方向就反了,迭代过程当场发散。
第二个坑是旋转矩阵的数值归一化。有些外部库返回的旋转矩阵由于数值误差可能不再是严格正交的,直接喂给Sophus的构造函数可能导致断言失败。我在调试中遇到过好几次"奇怪的内存错误",最后定位到是SO3构造时检查行列式不为1而崩溃。正确的做法是先用Eigen::Quaterniond做一次归一化,再传给Sophus。
第三个坑是矩阵与向量的数据类型不匹配。Sophus与Eigen结合时对浮点类型敏感,单精度类型的SE3f和双精度的Eigen::Matrix3d混用会导致编译错误。建议整个工程统一使用double类型,除非是在嵌入式环境中有明确的内存限制,否则单精度在做非线性优化时精度真的不够用。
6. 学习路径上的建议与避坑感悟
6.1 李群李代数要不要手推公式
很多同学纠结要不要把每一个公式都自己推导一遍。我的看法是:必考的核心公式要会推,应用型的公式会用就行。罗德里格斯公式、指数映射、扰动模型这些骨架必须亲自动手,因为它们决定了你对后续内容的理解。而BCH公式的完整展开、$J$矩阵的逆推导这类细枝末节,只需要知道存在性和物理含义,用的时候查库即可。
更合理的学习节奏是:先通读一遍书,把概念框架建立起来,然后动手做第3讲的习题巩固推导,最后上手Sophus写一段包含球面插值或位姿变换的测试代码。整个过程控制在3到5天比较合适,拖太久容易遗忘前面的数学基础。
6.2 学习中的常见误区
第一个误区是死抠李代数完整定义和泛函分析背景。李群李代数在数学上是一个非常深刻的分支,但SLAM领域用到的只是其中很小一部分。在两个小扰动近似的层面理解它,远比研究群论本身更有利于你写出能跑的SLAM系统。不用因为数学背景不够而产生焦虑。
第二个误区是跳过习题直接看代码。视觉SLAM十四讲最值钱的部分恰恰是那些课后推导题,它们把概念和工程问题拧在了一起。直接看代码容易陷入"代码能跑但是换一个场景就不会改"的尴尬局面。我自己做了三遍习题,每做一遍对李代数的理解都上一个台阶。
第三个误区是从头手写李代数实现。学习阶段可以使用Sophus,完全没有必要自己实现一个李代数库。自己写不等于理解了原理,反而容易被各种边界条件和数值陷阱消耗大量时间。先会用,再理解,最后才是自己造轮子。
6.3 这一讲与后续章节的联系
接下来进入第四讲的相机模型与非线性优化时,你会发现李代数无处不在。对空间点$P$投影到像素坐标系的过程求导,本质上是同时对旋转量$\phi$和平移量$\rho$做扰动,这就直接用到左扰动模型。第五讲的多视图几何和第七讲之后的BA优化,也全部建立在位姿的李代数表示上。
所以这一讲不是孤立的数学游戏,而是整个SLAM系统优化框架的地基。如果你现在感觉概念还有点飘,非常正常,后续章节会反复使用这些概念,用得多自然就熟了。遇到不懂的地方随时回来翻这一讲,每次都会有新的收获。
最后分享一个我在实际学习中的方法:每学完一小节,就在纸上用一句话总结这一小节"到底在干什么",比如"指数映射是把旋转向量变成旋转矩阵""扰动模型是换了一种求导姿势"。这种概括能力会在你组合各个模块时爆发出巨大的作用。李群李代数这一关一旦跨过去,后面的路就顺畅多了。