第二章:坐标系与姿态表示

各位同学,咱们今天聊聊坐标系和姿态表示。说实话,这章是传感器融合的“地基”。地基没打好,后面盖什么楼都得塌。我当年刚入行时,就在这上面栽过跟头——明明算法算得挺漂亮,结果飞控一跑起来,姿态直接翻了个个儿。后来才发现,是坐标系定义搞反了。

2.1 世界坐标系与机体坐标系

先说说世界坐标系。你可以把它想象成一个“绝对参考系”。通常我们取东北天(ENU)或者北东地(NED)作为标准。我个人习惯用东北天,因为直观——X轴指向东,Y轴指向北,Z轴指向天。嗯,这里要注意,不同行业习惯不一样,航空航天喜欢NED,机器人领域更爱ENU。

机体坐标系呢?它绑在设备上。比如你手里拿个PDA,屏幕朝上,那X轴一般指向设备右侧,Y轴指向前方,Z轴垂直屏幕向上。说白了,设备怎么动,这个坐标系就怎么动。

为什么要区分这两个坐标系?因为传感器数据都是在机体坐标系下测的,但导航、控制需要世界坐标系下的信息。你得做转换。我在项目中遇到过,有人直接把加速度计读数当世界坐标系下的重力方向用,结果可想而知——姿态解算全乱套了。

核心要点: 所有传感器原始数据都在机体坐标系下。融合算法的第一步,就是把它们转换到世界坐标系。

2.2 欧拉角

欧拉角,说白了就是用三个角度来描述姿态。分别是:

  • 横滚角(Roll):绕X轴旋转,像飞机侧身
  • 俯仰角(Pitch):绕Y轴旋转,像飞机抬头低头
  • 偏航角(Yaw):绕Z轴旋转,像飞机转向

你想想看,这三个角度是不是很直观?我刚开始学的时候也觉得挺好理解。但实际用起来,坑不少。

避坑指南: 我曾经在项目里吃过“万向锁”的亏。当俯仰角接近±90度时,横滚和偏航会失去一个自由度,导致姿态解算不稳定。所以,如果你做的是全姿态运动(比如无人机特技飞行),别用欧拉角做内部运算。

欧拉角的旋转顺序也很关键。常见的顺序有ZYX、ZXY等。不同顺序,同一个角度组合代表不同姿态。我建议你在项目一开始就定好顺序,并在代码注释里写清楚。不然过两个月你自己都忘了。

2.3 旋转矩阵

旋转矩阵,就是用3x3的矩阵来表示旋转。它没有万向锁问题,运算也方便。比如,把机体坐标系下的向量v转换到世界坐标系下:

v_world = R * v_body

其中R就是旋转矩阵。它由三个基本旋转矩阵相乘得到:

R = Rz(yaw) * Ry(pitch) * Rx(roll)

每个基本旋转矩阵长这样(绕X轴旋转角度θ):

Rx(θ) = [1,    0,      0    ]
        [0,  cosθ,  -sinθ ]
        [0,  sinθ,   cosθ ]
小技巧: 旋转矩阵是正交矩阵,它的逆等于它的转置。这意味着你可以用R^T把世界坐标系下的向量转回机体坐标系。这个性质在传感器融合里经常用到,省去了求逆的麻烦。

不过旋转矩阵也有缺点——它用了9个参数,但实际只有3个自由度。冗余参数多了,运算量就大,还容易引入数值误差。我在做嵌入式优化时,就因为这个原因放弃了旋转矩阵,改用四元数。

2.4 四元数基础

四元数,听起来挺玄乎。其实你可以把它理解成“带约束的复数”。一个四元数q由四个分量组成:

q = [w, x, y, z]

其中w是实部,x、y、z是虚部。它满足一个约束:w² + x² + y² + z² = 1。这叫单位四元数。

为什么用四元数?三个理由:

  1. 无万向锁——随便你怎么转,不会丢自由度
  2. 运算快——只有4个参数,乘法比矩阵快
  3. 易插值——做平滑旋转时,球面线性插值(SLERP)很自然

四元数表示旋转的公式也很简洁。比如,用四元数q旋转一个向量v:

v' = q * v * q_conj

其中q_conj是q的共轭。注意,这里的乘法是四元数乘法,不是普通乘法。我刚开始写代码时,经常搞混乘法顺序,结果旋转方向反了。嗯,这个坑我踩过。

实战建议: 在嵌入式系统里,我通常用四元数做内部姿态运算,只在需要输出给用户或日志时,才转成欧拉角。这样既避免了万向锁,又保持了可读性。

2.5 三种表示方式的转换

实际项目中,这三种表示方式经常需要互相转换。我整理了一个对照表:

转换方向 公式/方法 注意事项
欧拉角 → 旋转矩阵 三个基本矩阵相乘 注意旋转顺序
旋转矩阵 → 四元数 从矩阵元素提取 注意数值稳定性
四元数 → 欧拉角 用atan2和asin 注意奇点处理

举个例子,从四元数转欧拉角的代码(C语言风格):

// 四元数 q = [w, x, y, z]
roll  = atan2(2*(w*x + y*z), 1 - 2*(x*x + y*y));
pitch = asin(2*(w*y - z*x));
yaw   = atan2(2*(w*z + x*y), 1 - 2*(y*y + z*z));

注意,当pitch接近±90度时,asin的参数可能超出[-1,1]范围。我建议加个钳位处理,防止NaN。

我的习惯: 在代码里统一用四元数做运算,只在最后输出时转成欧拉角。这样既高效又安全。你想想看,嵌入式资源那么紧张,能省一点是一点。

好了,这一章的内容就这些。坐标系、欧拉角、旋转矩阵、四元数,它们是传感器融合的“语言”。你掌握了它们,后面学卡尔曼滤波、姿态解算就会轻松很多。下一章,咱们聊聊加速度计和陀螺仪的数据怎么处理——那又是另一番天地了。