3. 欧拉角与四元数:姿态表示的两种武器
说到姿态表示,圈里一直有个老话题——欧拉角和四元数到底选哪个?我刚开始做飞控那会儿,觉得欧拉角多直观啊,俯仰、横滚、偏航,一看就懂。直到有一次,无人机在天上突然“抽搐”了一下,差点炸机……查了半天,原来是万向锁搞的鬼。从那以后,我对四元数再也不敢轻视了。
今天咱们就把这两个东西掰开揉碎,看看它们各自有什么本事,又该怎么互相转换。
3.1 欧拉角(RPY)——直观但带坑
欧拉角用三个角度描述旋转:横滚(Roll)、俯仰(Pitch)、偏航(Yaw)。说白了,就是绕X轴、Y轴、Z轴分别转多少度。
优点:直观、易理解、占用内存小(3个浮点数)。
缺点:存在万向锁问题、插值不平滑、旋转顺序依赖性强。
万向锁是什么?我举个例子。你把手机平放在桌上,先绕Z轴转90度(偏航),再绕Y轴转90度(俯仰),这时候你会发现——绕X轴的旋转和绕Z轴的旋转效果重叠了。你丢失了一个自由度。这就是万向锁。
避坑指南:我曾经在机械臂控制中用过欧拉角做插值,结果末端执行器走了一条奇怪的弧线。后来换成四元数球面插值,轨迹才变得平滑。如果你的系统涉及大角度旋转或连续姿态变化,欧拉角不是好选择。
3.2 四元数——数学优雅,工程实用
四元数是个超复数,形式为 q = w + xi + yj + zk,其中 w 是实部,(x, y, z) 是虚部。它用4个参数表示旋转,没有奇点,插值平滑。
| 特性 | 欧拉角 | 四元数 |
|---|---|---|
| 参数数量 | 3 | 4 |
| 万向锁 | 存在 | 不存在 |
| 插值平滑性 | 差 | 好(球面插值) |
| 计算效率 | 高(三角函数多) | 高(乘法为主) |
| 直观性 | 强 | 弱 |
| 内存占用 | 12字节 | 16字节 |
你想想看,四元数为什么没有万向锁?因为它把旋转表示成绕一个单位向量旋转某个角度,而不是分解成三个轴的顺序旋转。这就避免了“顺序依赖”带来的奇点。
3.3 相互转换方法
实际工程中,我们经常需要在欧拉角和四元数之间来回切换。比如传感器输出欧拉角,但姿态解算用四元数。下面给出Python实现。
3.3.1 欧拉角 → 四元数
假设旋转顺序为 ZYX(偏航→俯仰→横滚),这是航空领域最常用的约定。
import math
def euler_to_quaternion(roll, pitch, yaw):
"""
将欧拉角(弧度)转换为四元数
旋转顺序:ZYX(偏航→俯仰→横滚)
"""
cy = math.cos(yaw * 0.5)
sy = math.sin(yaw * 0.5)
cp = math.cos(pitch * 0.5)
sp = math.sin(pitch * 0.5)
cr = math.cos(roll * 0.5)
sr = math.sin(roll * 0.5)
w = cr * cp * cy + sr * sp * sy
x = sr * cp * cy - cr * sp * sy
y = cr * sp * cy + sr * cp * sy
z = cr * cp * sy - sr * sp * cy
return (w, x, y, z)
3.3.2 四元数 → 欧拉角
def quaternion_to_euler(w, x, y, z):
"""
将四元数转换为欧拉角(弧度)
输出顺序:(roll, pitch, yaw)
"""
# 计算俯仰角(注意万向锁检测)
sin_pitch = 2.0 * (w * y - z * x)
# 检测万向锁
if abs(sin_pitch) >= 1.0:
pitch = math.copysign(math.pi / 2, sin_pitch)
# 此时横滚和偏航耦合,通常设横滚为0
roll = 0.0
yaw = math.atan2(2.0 * (w * z + x * y), 1.0 - 2.0 * (y * y + z * z))
else:
pitch = math.asin(sin_pitch)
roll = math.atan2(2.0 * (w * x + y * z), 1.0 - 2.0 * (x * x + y * y))
yaw = math.atan2(2.0 * (w * z + x * y), 1.0 - 2.0 * (y * y + z * z))
return (roll, pitch, yaw)
个人经验:我在做IMU数据融合时,习惯用四元数做状态估计,只在日志记录和调试界面才转成欧拉角。这样既避免了万向锁,又方便人看。另外,四元数转欧拉角时一定要做万向锁检测,否则你会得到NaN。
3.4 知识体系总览
下面这张图帮你理清欧拉角和四元数的关系,以及在实际系统中的使用流程。
3.5 工程实践建议
说了这么多,到底该怎么选?我给出几条实战经验:
- 传感器驱动层:用欧拉角接收原始数据(大部分IMU芯片直接输出RPY)
- 姿态解算层:转成四元数做滤波和融合(避免万向锁,计算效率高)
- 控制层:用四元数做插值和指令生成(球面插值平滑自然)
- 显示层:转回欧拉角给人看(直观,方便调试)
又一个坑:四元数一定要归一化!我见过有人直接拿传感器原始四元数做运算,结果姿态越算越偏。原因就是四元数模长偏离了1。每次更新后加一行 q /= norm(q),能省很多调试时间。
嗯,欧拉角和四元数就聊到这儿。记住一句话:人看用欧拉角,机器算用四元数。掌握了转换方法,你在姿态估计、机器人控制、无人机飞控这些领域就能游刃有余了。