第4章:姿态误差度量

各位同学,欢迎来到《高动态姿态跟踪控制实战》的第四讲。

前面几章我们把姿态描述的基础打牢了。今天聊一个更实际的问题——姿态误差到底怎么算?

你可能会想,误差不就是期望值减实际值吗?嗯,在欧几里得空间里确实是这样。但姿态这东西,它生活在SO(3)群上,不是线性空间。直接相减,结果毫无意义。我刚开始做飞控时就在这上面栽过跟头——用欧拉角直接做差当误差,结果控制器在奇异点附近直接炸了。

所以,今天我们来系统梳理一下:旋转矩阵误差、四元数误差、MRP误差,以及它们各自的脾气秉性。

核心观点:姿态误差不是简单的减法,而是“从一个姿态到另一个姿态的旋转”。这个旋转越小,误差越小。

姿态误差度量知识体系 姿态误差 旋转矩阵误差 四元数误差 MRP误差 误差特性分析 唯一性 连续性 计算效率

4.1 姿态误差的本质定义

先搞清楚一件事:姿态误差 = 从当前姿态到期望姿态所需的旋转

假设当前姿态为 \(R_a\),期望姿态为 \(R_d\)。那么误差旋转矩阵 \(R_e\) 满足:

R_e = R_d^T * R_a    (或者 R_e = R_a * R_d^T,取决于定义习惯)

我个人习惯用 \(R_e = R_d^T R_a\)。为什么?因为当 \(R_a = R_d\) 时,\(R_e = I\),误差为零,很直观。

注意,这里的 \(R_e\) 本身也是一个旋转矩阵。它描述的是:从期望坐标系到当前坐标系的旋转。如果这个旋转是零,说明姿态完全对齐。

小技巧:在实际代码中,我通常用轴角表示法来提取误差的大小。旋转矩阵的迹(trace)直接给出了旋转角:\(\cos\theta = (tr(R_e) - 1)/2\)。当 \(\theta\) 很小时,可以直接用 \(\theta\) 作为误差标量。

4.2 旋转矩阵误差

旋转矩阵误差是最“原始”的误差形式。它直接保留了全部姿态信息,没有奇异性问题。

定义:

R_e = R_d^T * R_a

当 \(R_e = I\) 时,误差为零。但实际控制中,我们很少直接用 \(R_e\) 作为反馈信号,因为它是9个元素,冗余度太高。

常用的误差向量:

从 \(R_e\) 中提取的误差向量通常有两种形式:

  • so(3) 映射: \(\Psi = (R_e - R_e^T)^\vee\),即取反对称部分的三个独立元素
  • 轴角映射: \(\mathbf{e} = \sin\theta \cdot \mathbf{n}\),其中 \(\theta\) 和 \(\mathbf{n}\) 是 \(R_e\) 的旋转角和轴

我在项目中遇到过一个问题:当误差角接近180°时,\(\sin\theta\) 很小,误差向量趋近于零,但实际误差很大。这就是所谓的“大误差小信号”问题。解决办法?用 \(\theta \cdot \mathbf{n}\) 代替 \(\sin\theta \cdot \mathbf{n}\),但要注意 \(\theta\) 的符号判断。

注意:旋转矩阵误差在 \(\theta = \pi\) 时存在歧义。两个不同的旋转轴可能对应同一个旋转矩阵。这在连续控制中需要特别处理。

4.3 四元数误差

四元数误差是工程中最常用的形式。为什么?因为它计算简单,没有奇异性。

定义:

q_e = q_d^{-1} ⊗ q_a

其中 \(q_d\) 是期望四元数,\(q_a\) 是当前四元数,⊗ 表示四元数乘法。

展开写:

q_e = [q_d0 * q_a - q_d^T * q_a_vec;
       q_d0 * q_a_vec + q_a0 * q_d_vec + q_d_vec × q_a_vec]

嗯,看起来有点复杂。但实际代码里就一行的事。

误差向量提取:

通常取四元数的矢量部分作为误差信号:

e = q_e_vec = [q_e1, q_e2, q_e3]^T

当误差很小时,\(q_e0 ≈ 1\),矢量部分 ≈ \(\frac{1}{2}\theta \mathbf{n}\)。所以直接用矢量部分乘以2,就近似得到了轴角误差。

我的经验:四元数误差的矢量部分在 \(\theta < 90^\circ\) 时表现很好。但如果误差超过90°,矢量部分开始“饱和”——它不再随误差线性增长。这时候我建议用 \(2 \cdot \text{sign}(q_e0) \cdot q_e_vec\) 来补偿。

代码示例:

// 四元数误差计算
Quaternion q_error = q_desired.conjugate() * q_actual;

// 提取误差向量(用于反馈)
Vector3d error_vec = q_error.vec();  // 取虚部

// 处理大误差情况
if (q_error.w() < 0) {
    error_vec = -error_vec;  // 保证最短路径
}

// 小角度近似:误差角 ≈ 2 * ||error_vec||
double angle_error = 2.0 * asin(error_vec.norm());

避坑指南:我曾经在四元数误差的符号上吃过亏。记住,\(q\) 和 \(-q\) 表示同一个姿态。所以计算误差时,要确保 \(q_e0 \geq 0\),否则取负。这样可以保证误差沿着最短路径收敛。

4.4 修正罗德里格斯参数(MRP)误差

MRP 是姿态表示中的“小众选手”,但在某些场景下非常好用。

定义:

MRP 由旋转轴 \(\mathbf{n}\) 和旋转角 \(\theta\) 定义:

σ = tan(θ/4) * n

MRP 误差的计算方式与四元数类似:

σ_e = σ_d 与 σ_a 的 MRP 合成(通过四元数中转)

具体步骤:

  1. 将 MRP 转为四元数:\(q = \frac{1}{\sqrt{1+|\sigma|^2}} [1, \sigma^T]^T\)
  2. 计算四元数误差:\(q_e = q_d^{-1} q_a\)
  3. 将 \(q_e\) 转回 MRP:\(\sigma_e = \frac{q_e_vec}{1+q_e0}\)

MRP 的独特优势:

  • 当 \(\theta < 180^\circ\) 时,MRP 是唯一的
  • 误差信号在 \(\theta\) 较小时近似线性
  • 计算量比四元数略小(3个参数 vs 4个参数)

但要注意:当 \(\theta \to 360^\circ\) 时,MRP 会趋于无穷大。所以实际使用中,通常配合“影子 MRP”来切换。

我的建议:如果你做的是小角度跟踪(比如卫星姿态保持),MRP 是个不错的选择。但如果是大角度机动,我建议用四元数,更稳妥。

4.5 误差特性分析

最后,我们来对比一下这三种误差度量的特性。

特性 旋转矩阵误差 四元数误差 MRP 误差
参数数量 9(冗余) 4(归一化约束) 3(无约束)
奇异性 θ = 360°
小角度线性度 好(so(3)映射) 好(矢量部分)
大角度表现 需处理符号歧义 需处理双值性 需切换影子
计算效率
工程常用度 低(理论分析用) 高(实际飞控用) 中(特定场景用)

关键结论:

  • 如果你在做理论推导,旋转矩阵误差最干净,SO(3)上的几何结构一目了然
  • 如果你在写实际飞控代码,四元数误差是最稳妥的选择,没有之一
  • 如果你追求极致计算效率且工作在小角度范围,MRP 值得一试

个人经验:我在做无人机竞速控制时,曾经尝试用 MRP 代替四元数。结果在大角度翻滚时,MRP 直接飞到了无穷大,控制器瞬间失稳。后来我加了一个影子 MRP 切换逻辑,才解决问题。但说实话,为了省那一点点计算量,不值得这么折腾。最后还是老老实实用四元数。

好了,关于姿态误差的度量,今天就聊到这里。记住一句话:没有最好的误差度量,只有最适合你场景的误差度量。理解每种度量的脾气,你才能在控制中游刃有余。


专注资料整理