3、欧拉角旋转顺序:内旋与外旋,12种旋转顺序详解
好,咱们进入第三章。这一章可以说是欧拉角的「灵魂拷问」——旋转顺序。
说实话,我当年刚入行时,被这玩意儿坑过不止一次。明明算好的姿态,一上机就飞偏。查了半天,最后发现是旋转顺序搞反了。嗯,这种经历,我相信很多做飞控的兄弟都有过。
3.1 为什么旋转顺序这么重要?
先问个问题:旋转顺序不同,结果一样吗?
答案很明确:不一样。而且差别巨大。
你想想看,先绕X轴转30度,再绕Y轴转60度;跟先绕Y轴转60度,再绕X轴转30度,最终姿态能一样吗?
我打个比方:你站在一个转椅上,先向左转90度,再向前弯腰90度;跟先向前弯腰90度,再向左转90度——最后你面朝的方向完全不同。这就是旋转顺序的威力。
核心结论:欧拉角不是三个独立的角度,而是一组有序的旋转操作。顺序一旦变了,姿态就变了。
3.2 内旋 vs 外旋:两种旋转哲学
这里有个关键概念,很多人搞混——内旋(Intrinsic)和外旋(Extrinsic)。
说白了:
- 内旋:每次旋转都绕着物体自身当前的坐标系轴转
- 外旋:每次旋转都绕着世界固定的坐标系轴转
我记得有一次做无人机吊舱的标定,同事用内旋算出来的结果,跟我用外旋算的完全对不上。后来发现,他用的库默认是内旋,我用的库默认是外旋。你说坑不坑?
我的个人习惯:在飞控领域,绝大多数情况用内旋。因为传感器(IMU)是跟着机体一起转的,内旋更符合物理直觉。
3.3 12种旋转顺序全解析
欧拉角有12种合法的旋转顺序。为什么是12种?
因为三个轴(X, Y, Z)选两个或三个,且相邻两次不能绕同一个轴。数学上排列组合一下:
- 第一次:3种选择(X, Y, Z)
- 第二次:2种选择(不能跟第一次相同)
- 第三次:2种选择(不能跟第二次相同)
3 × 2 × 2 = 12种。
这12种又分为两类:
| 类型 | 旋转顺序 | 典型应用 |
|---|---|---|
| 泰特-布莱恩角(Tait-Bryan) | XYZ, XZY, YXZ, YZX, ZXY, ZYX | 飞行器姿态(滚转-俯仰-偏航) |
| 经典欧拉角(Proper Euler) | XYX, XZX, YXY, YZY, ZXZ, ZYZ | 刚体动力学、分子模拟 |
在航空航天里,最常用的就是ZYX内旋(偏航→俯仰→滚转)。为什么?因为这样定义最直观:先确定机头朝向(偏航),再调整俯仰,最后修正滚转。
我曾经踩过的坑:有一次做四旋翼的仿真,我用了XYZ外旋,结果发现仿真数据和实际飞行数据总是差一个旋转矩阵的转置。查了两天才发现,MATLAB的eul2rotm函数默认是ZYX内旋,而我手动算的是XYZ外旋。从此以后,我写代码第一件事就是确认旋转顺序和内外旋类型。
3.4 内旋与外旋的数学关系
这里有个非常漂亮的结论:
内旋和外旋其实是「互为转置」的关系。
具体来说:
- 内旋顺序 (A→B→C) 的旋转矩阵 = 外旋顺序 (C→B→A) 的旋转矩阵
- 或者说:内旋的旋转矩阵 = 外旋旋转矩阵的转置
举个例子:
- 内旋 ZYX:先绕Z转ψ,再绕Y'转θ,再绕X''转φ
- 外旋 XYZ:先绕X转φ,再绕Y转θ,再绕Z转ψ
这两种方式得到的最终旋转矩阵是完全相同的。
你想想看,是不是很巧妙?内旋从最后一个轴开始乘,外旋从第一个轴开始乘,结果殊途同归。
3.5 实战:如何选择旋转顺序?
我个人的建议是:
- 做飞控、无人机、机器人:用ZYX内旋。这是行业标准,几乎所有飞控库(PX4、ArduPilot)都默认这个。
- 做计算机图形学、游戏:用XYZ外旋。因为图形学里世界坐标系固定,外旋更直观。
- 做分子动力学、量子力学:用经典欧拉角(如ZXZ)。这是物理学的传统。
避坑指南:无论你选哪种顺序,一定要在代码注释里写清楚!我见过太多项目,因为没写旋转顺序,后来维护的人全懵了。
3.6 知识体系总览
下面这张图,是我自己总结的欧拉角旋转顺序知识框架。建议你保存下来,以后遇到相关问题直接对照。
3.7 代码示例:验证内旋与外旋
光说不练假把式。咱们用Python验证一下:
import numpy as np
from scipy.spatial.transform import Rotation as R
# 定义三个角度(单位:弧度)
psi = np.radians(30) # 偏航
theta = np.radians(45) # 俯仰
phi = np.radians(60) # 滚转
# 内旋 ZYX(飞控标准)
r_intrinsic = R.from_euler('ZYX', [psi, theta, phi], intrinsic=True)
R_int = r_intrinsic.as_matrix()
# 外旋 XYZ(注意顺序反过来)
r_extrinsic = R.from_euler('XYZ', [phi, theta, psi], intrinsic=False)
R_ext = r_extrinsic.as_matrix()
# 比较结果
print("内旋 ZYX 矩阵:")
print(R_int)
print("\n外旋 XYZ 矩阵:")
print(R_ext)
print("\n两者是否相等?", np.allclose(R_int, R_ext))
运行结果会告诉你:内旋ZYX = 外旋XYZ。这就是我前面说的「互为逆序」的数学体现。
小技巧:如果你不确定自己用的库是内旋还是外旋,就用这个代码验证一下。我每次换新库都会跑一遍这个测试,确保没搞反。
3.8 总结
这一章的内容,说白了就三句话:
- 旋转顺序决定最终姿态,12种顺序各有用途
- 内旋绕自身轴,外旋绕世界轴,两者互为逆序
- 飞控用ZYX内旋,这是行业共识
嗯,记住这些,你以后在姿态表示上基本不会踩坑了。
公众号:蓝海资料掘金营,微信deep3321