当你用旋转矩阵或四元数进行连续插值,或者在SLAM后端优化中调整机器人的位姿时,本质上是在一个光滑的“形状”上进行操作。这个形状不是简单的平面或球面,而是一种称为“李群”的数学结构。李代数则是描述这个形状在局部“看起来像什么”的工具。理解它们,是解锁现代机器人状态估计和运动规划核心算法的钥匙。
生活类比:想象一个可以自由旋转和移动的魔方(李群)。你手里有一本说明书(李代数),上面记录了从当前状态出发,进行所有微小转动和滑动操作的“配方”。这本说明书只在魔方当前位置附近有效,但它能精确指导你如何到达附近的任何状态。
技术定义:在机器人学中,李群是一个同时是光滑流形(连续空间)和群的集合,例如所有三维旋转(SO(3))或所有三维刚体运动(SE(3))。李代数则是对应李群在单位元处的切空间,它描述了群元素在无穷小变化下的行为,通常用向量表示。
我们熟悉的旋转矩阵集合SO(3)就是一个李群。它的李代数so(3)是一个由三维向量组成的空间。这个三维向量有一个奇妙的性质:它可以生成一个反对称矩阵。
具体来说,对于李代数 so(3) 中的一个向量 φ = [φ₁, φ₂, φ₃]ᵀ,我们可以通过一个“帽子”算子(∧)将其映射为一个3x3的反对称矩阵:
// 三维向量到反对称矩阵
Eigen::Vector3d phi(phi_x, phi_y, phi_z);
Eigen::Matrix3d phi_hat;
phi_hat << 0, -phi_z, phi_y,
phi_z, 0, -phi_x,
-phi_y, phi_x, 0;
这个反对称矩阵 φ^ 就是李代数 so(3) 的元素。它和旋转矩阵 R(李群 SO(3) 的元素)之间通过指数映射和对数映射联系起来:
指数映射: 将李代数 so(3) 中的元素 φ 映射到李群 SO(3) 中的旋转矩阵 R。物理意义是:绕轴 φ/‖φ‖ 旋转角度 ‖φ‖。公式为 R = exp(φ^)。在实际计算中,我们使用罗德里格斯公式来高效计算这个指数映射。
对数映射: 将旋转矩阵 R 映射回李代数 so(3) 中的向量 φ。这是指数映射的逆运算,φ = log(R)∨(∨是∧的逆运算,将反对称矩阵变回向量)。
为什么需要这个转换?因为李代数 so(3) 只是一个普通的三维向量空间。在其上进行加法、求导等运算,比直接在旋转矩阵(有正交约束 RᵀR=I)上进行要简单得多。我在一个机械臂轨迹优化项目中就踩过坑:直接在旋转矩阵上做梯度下降,迭代几步后旋转矩阵就不再正交了,导致整个优化崩溃。后来改用李代数空间进行优化,问题迎刃而解。
机器人的位姿包含旋转和平移,对应的李群是特殊欧氏群 SE(3)。它的李代数 se(3) 是一个六维向量空间。
| 特性 | 旋转群 SO(3) | 刚体运动群 SE(3) |
|---|---|---|
| 群元素 | 3x3 旋转矩阵 R | 4x4 变换矩阵 T = [R t; 0 1] |
| 自由度 | 3 | 6 |
| 李代数 | so(3): 三维向量 φ | se(3): 六维向量 ξ = [ρ, φ]ᵀ |
| 李代数元素形式 | 3x3 反对称矩阵 φ^ | 4x4 矩阵 ξ^ = [φ^ ρ; 0 0] |
| 指数映射 | R = exp(φ^) | T = exp(ξ^) |
这里,ξ 是 se(3) 的元素,由平移部分 ρ(一个三维向量)和旋转部分 φ(一个三维向量)组成。它的“帽子”算子生成一个4x4矩阵。SE(3)的指数映射也有闭式解(虽然比SO(3)的罗德里格斯公式复杂一些),它同时生成了旋转和平移。
这是李群李代数在机器人学中最核心的应用。在优化问题(如SLAM、视觉里程计)中,我们需要调整机器人的位姿 T 以最小化误差函数。这就要求我们计算误差函数关于位姿 T 的导数。
由于 T 是李群元素,我们无法定义“T + ΔT”。正确的做法是定义两种扰动模型:
两种模型在理论上是等价的,只是导数形式略有不同。实际开发中,大部分SLAM库(如g2o, Ceres-Solver的Sophus接口)默认使用左扰动模型。选择一个并保持一致即可。
假设我们有一个误差函数 e(T),它计算观测值与基于位姿 T 的预测值之间的差。我们需要求 ∂e/∂T。使用左扰动模型,我们实际上计算的是误差函数对李代数扰动 δξ 的导数:
// 伪代码:利用扰动模型计算雅可比矩阵的核心思想
Eigen::Matrix<double, 2, 6> jacobian; // 假设误差e是2维,位姿有6自由度
for (int i = 0; i < 6; ++i) {
// 1. 生成第i个自由度上的微小扰动向量δξ
Eigen::Vector6d delta = Eigen::Vector6d::Zero();
delta(i) = 1e-6; // 微小量
// 2. 将扰动向量转换为李群上的扰动变换
Sophus::SE3d T_perturbed = Sophus::SE3d::exp(delta) * T;
// 3. 计算扰动后的误差
Eigen::Vector2d e_perturbed = computeError(T_perturbed);
// 4. 数值导数作为雅可比矩阵的第i列
jacobian.col(i) = (e_perturbed - e) / 1e-6;
}
当然,对于许多常见的误差函数(如重投影误差),我们可以推导出解析的雅可比矩阵形式,这比数值微分更高效、更精确。但上述数值微分的思路清晰地展示了扰动模型的用法。
编者提示: 在实际项目中,我强烈建议使用成熟的数学库(如 Eigen 配合 Sophus 库)来处理李群李代数的运算。Sophus库提供了SO(3)、SE(3)等类的实现,包括指数/对数映射、扰动模型求导等。自己实现这些运算极易出错,特别是SE(3)指数映射的闭式解涉及复杂的矩阵运算。直接调用 Sophus::SE3d::exp(xi) 和 T.log() 既安全又高效。
让我们通过一个简化的图优化SLAM后端流程,看看李群李代数如何嵌入其中。
1. 构建位姿图: 节点是机器人在不同时刻的位姿 T_i ∈ SE(3),边是两个位姿间的相对运动约束(来自里程计或闭环检测)T_ij。
2. 定义误差: 对于连接T_i和T_j的边,其预测值为 T_j 相对于 T_i 的变换:T_ij_pred = T_i⁻¹ · T_j。误差定义在李群上:e_ij = log( (T_ij_pred)⁻¹ · T_ij_meas )∨。这个误差 e_ij 是一个六维向量(se(3)的元素)。
3. 线性化求解: 将所有边的误差堆叠成总误差向量 e。我们需要求解位姿的增量 δξ(李代数向量),使得 e(δξ) 最小化。这需要计算误差 e 关于每个位姿 T_k 的雅可比矩阵 J_k = ∂e/∂δξ_k(使用左扰动或右扰动模型)。然后通过高斯牛顿或列文伯格-马夸尔特算法求解线性方程 (JᵀJ) δξ = -Jᵀe。
4. 更新位姿: 求解得到的 δξ_k 是每个节点在李代数空间中的增量。我们通过指数映射更新位姿:T_k_new = exp(δξ_k) · T_k_old(左扰动模型)。
整个优化过程在李代数空间(一个无约束的向量空间)中进行迭代求解,而位姿的表示和存储始终保持在李群 SE(3) 上。这完美地解决了在流形上进行优化的问题。
使用 Eigen 和 Sophus 库(一个用于李群李代数的C++库)完成以下小实验:
Sophus::SE3d::exp(xi) 计算扰动变换。T_left = exp(xi) * T1 和 T_right = T1 * exp(xi)。T1.inverse() * T_left 和 T_right * T1.inverse(),观察结果。理解左扰动和右扰动对位姿影响的区别。T_new = exp(δξ) * T_old 和右扰动模型 T_new = T_old * exp(δξ) 计算出的新位姿 T_new 是相同的。