3. 李群与李代数:李群基础、指数映射与对数映射、扰动模型、Sophus库使用
各位同学,欢迎来到第三章。这一章我们聊点硬核的——李群与李代数。
说实话,我刚入行那会儿,看到「李群」这两个字,第一反应是「这玩意儿跟我有啥关系?」。后来在做一个VIO项目时,优化位姿总是跑飞,我才意识到:没有李代数,你连个像样的姿态优化都做不了。嗯,今天我们就把它彻底讲明白。
3.1 为什么需要李群与李代数?
先问个问题:在SLAM里,我们怎么表示相机的位姿?
答案通常是旋转矩阵 R 和平移向量 t。旋转矩阵属于特殊正交群 SO(3),变换矩阵属于特殊欧氏群 SE(3)。
但问题来了——
- 旋转矩阵是正交的,有9个参数,但自由度只有3。你直接优化9个参数,约束条件很难满足。
- 更麻烦的是,你没法直接对旋转矩阵做加法。R + ΔR 大概率不再是旋转矩阵。
说白了,SO(3) 和 SE(3) 是流形,不是向量空间。你不能用常规的微积分去处理它们。
李代数就是来解决这个问题的。它把流形上的优化问题,转化成了向量空间里的优化问题。你想想看,这多方便。
核心思想:李群是流形,李代数是它的切空间。我们在切空间里做优化,再映射回流形上。
3.2 李群基础
先给个定义。李群是一种连续群,它既是群又是光滑流形。常见的李群有:
| 李群 | 含义 | 维度 |
|---|---|---|
| SO(3) | 三维旋转群 | 3 |
| SE(3) | 三维刚体变换群 | 6 |
| SO(2) | 二维旋转群 | 1 |
| SE(2) | 二维刚体变换群 | 3 |
SO(3) 的定义很简单:
SO(3) = { R ∈ ℝ³ˣ³ | RRᵀ = I, det(R) = 1 }
SE(3) 则是:
SE(3) = { T = [R t; 0 1] | R ∈ SO(3), t ∈ ℝ³ }
我个人习惯把 SE(3) 想象成一个「带旋转的平移」。你想想看,一个刚体在空间中的运动,不就是先转再移吗?
3.3 指数映射与对数映射
这是李群和李代数之间的桥梁。说白了,指数映射把李代数(切空间)映射到李群(流形),对数映射反过来。
3.3.1 SO(3) 的指数映射
对于 SO(3),它的李代数是 so(3),由三维向量 φ 的反对称矩阵组成:
so(3) = { Φ = φ^ = [0 -φ₃ φ₂; φ₃ 0 -φ₁; -φ₂ φ₁ 0] | φ ∈ ℝ³ }
指数映射公式:
R = exp(φ^) = I + sin(θ)/θ · φ^ + (1-cos(θ))/θ² · (φ^)²
其中 θ = ||φ||。
这个公式是不是很眼熟?对,就是罗德里格斯公式。所以李代数 so(3) 的物理意义就是旋转向量。
我的经验:我在做IMU预积分时,经常用这个公式把角速度增量转换成旋转矩阵。注意 θ 很小的时候要用泰勒展开,避免数值问题。
3.3.2 SE(3) 的指数映射
SE(3) 的李代数是 se(3),由六维向量 ξ = [ρ; φ] 组成。指数映射为:
T = exp(ξ^) = [R t; 0 1]
其中 R 由 φ 通过 SO(3) 的指数映射得到,t 由 ρ 和 φ 共同决定。
对数映射就是反过来,从 T 求 ξ。这个在代码里很常用,比如从变换矩阵里提取旋转向量。
3.4 扰动模型
终于到了最实用的部分。在SLAM后端优化中,我们需要对位姿求导。但直接对 R 求导不行,怎么办?
两种方式:
- 李代数求导:对 R 对应的李代数 φ 求导。
- 扰动模型:给 R 左乘或右乘一个小扰动,对扰动求导。
我个人更推荐扰动模型。为什么?因为它更简洁,而且不涉及复杂的指数映射求导。
3.4.1 SO(3) 的左扰动模型
假设我们有一个旋转矩阵 R,给它左乘一个小扰动 ΔR = exp(δφ^),然后对 δφ 求导:
∂(Rp) / ∂δφ = -R·p^
这个结果非常漂亮。你想想看,一个复杂的旋转求导,最后就变成了一个简单的反对称矩阵乘法。
注意:左扰动和右扰动结果不一样。左扰动对应世界坐标系下的扰动,右扰动对应相机坐标系下的扰动。我在项目中吃过这个亏,大家一定要分清。
3.4.2 SE(3) 的扰动模型
对于 SE(3),左扰动模型给出:
∂(Tp) / ∂δξ = [I₃, -p^; 0, 0]
这个雅可比矩阵在BA优化和VIO中频繁出现。我建议你把它背下来,或者至少知道怎么推导。
3.5 Sophus库使用
理论讲完了,我们看看怎么用代码实现。Sophus 是 Strasdat 写的李代数库,现在已经是 Eigen 的官方模块了。
3.5.1 安装与包含
#include <sophus/se3.hpp>
#include <sophus/so3.hpp>
如果你用 CMake,记得链接:
find_package(Sophus REQUIRED)
target_link_libraries(your_target Sophus::Sophus)
3.5.2 基本操作
// 创建 SO3 从旋转矩阵
Eigen::Matrix3d R = Eigen::AngleAxisd(M_PI/4, Eigen::Vector3d::UnitZ()).toRotationMatrix();
Sophus::SO3d SO3_R(R);
// 创建 SO3 从旋转向量
Sophus::SO3d SO3_v(0, 0, M_PI/4);
// 指数映射:李代数 -> 李群
Eigen::Vector3d phi(0.1, 0.2, 0.3);
Sophus::SO3d SO3_exp = Sophus::SO3d::exp(phi);
// 对数映射:李群 -> 李代数
Eigen::Vector3d phi_log = SO3_exp.log();
// 创建 SE3
Sophus::SE3d SE3_Rt(R, Eigen::Vector3d(1, 2, 3));
// 扰动模型示例
Eigen::Vector3d p(1, 0, 0);
Eigen::Vector3d delta_phi(0.01, 0, 0);
Sophus::SO3d R_new = SO3_R * Sophus::SO3d::exp(delta_phi);
Eigen::Vector3d p_new = R_new * p;
避坑指南:我曾经在优化时忘记对扰动取对数,直接用了增量加法。结果迭代了几步就发散。记住:李代数上的增量必须通过指数映射回到李群。
3.6 知识体系总览
下面这张图总结了本章的核心逻辑。我建议你多看几遍,把整个脉络理清楚。
3.7 本章小结
这一章我们讲了:
- 李群 SO(3) 和 SE(3) 是什么,为什么需要它们
- 指数映射和对数映射怎么用
- 扰动模型如何优雅地解决求导问题
- Sophus 库的基本用法
说实话,李群李代数这东西,光看理论容易晕。我建议你打开编辑器,把上面的代码跑一遍。亲手试试指数映射、对数映射、扰动求导,比看十遍书都管用。
嗯,下一章我们会把这些知识用到实际的位姿图优化中。到时候你就知道,今天学的这些,全是真功夫。