1. 从一次姿态数据对不上的问题说起
前阵子帮一个朋友调无人机飞控日志,他拿着两套代码来问我:为什么同一块IMU出来的原始数据,一套代码换算出的欧拉角在俯仰角到90度的时候会出现横滚角和偏航角突然乱跳,另一套代码就不会。我问他:你先告诉我,这两套代码用的旋转顺序是不是一样的?他愣了一下,翻了半天代码才确认,一套用的是ZYX顺序,另一套是YZX顺序。
这就是欧拉角最典型的坑——表面上大家都说"欧拉角就是绕三个轴转三次嘛",但转哪三个轴、按什么顺序转、在什么坐标系下转,这三件事只要有一个不一致,算出来的角度就完全不是一回事。很多做飞控、机器人、三维渲染、游戏开发的人,第一版程序跑出来的姿态跟预期对不上,十有八九不是数学算错了,而是欧拉角定义的某个细节没对齐。
这篇就打算把欧拉角的定义从头到尾掰开揉碎讲一遍,重点不是背公式,而是搞清楚每条定义背后的几何直觉和工程后果。适合那些人——刚接触姿态解算、刚学机器人学、做三维引擎或传感器融合、甚至只是被万向节锁这个概念吓到过的同学。把这篇文章看完,你会能清楚地回答三个问题:欧拉角到底在转什么、为什么旋转顺序会改变结果、万向节锁到底"锁"的是什么。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 欧拉角的原始定义:三次旋转背后的几何直觉
2.1 从"绕一个轴转"到"绕三个轴转"
欧拉角的起源可以追溯到18世纪欧拉(Leonhard Euler)对刚体旋转的研究。欧拉用一个著名的定理定下了基调:任何刚体在三维空间中的姿态变化,无论看起来多么复杂,都可以分解为绕某个固定轴的一次旋转。但这里有个实际问题——一次旋转虽然数学上简洁,工程上却很难用。
举个例子,你坐在飞机里,机头先朝东再抬头30度,然后向右倾斜20度。如果要用"绕某一个任意轴转一个角度"来描述这个姿态,你得先算出一个综合旋转轴和综合旋转角,完全不直观。而欧拉角的方式是:把这个姿态分解成三个绕标准坐标轴的旋转——先绕z轴转一个偏航角,再绕新的y轴转一个俯仰角,最后绕更新的x轴转一个横滚角。三个角度各管各的,跟飞行员的操作习惯完全一致,这才能在仪表盘上显示、在摇杆上控制。
不过这种"各管各的"是有代价的:它引入了旋转顺序问题。三维旋转不是加法的交换律——先横滚再俯仰,和先俯仰再横滚,最终机头朝向完全不同。这就回到了开头的那个场景:两套代码用了不同的旋转顺序,相同的角速度数据换算出来的角度当然就不一样。
2.2 基元旋转:构成欧拉角的积木
在理解三次旋转如何组合之前,先得明确"绕坐标轴的一次旋转"到底是什么。数学上,这是一个基元旋转矩阵。绕x轴转角度θ的矩阵是:
code复制R_x(θ) = [ 1 0 0 ]
[ 0 cos(θ) -sin(θ) ]
[ 0 sin(θ) cos(θ) ]
注意这里用的是右手定则:右手拇指指向旋转轴正方向,四指弯曲的方向就是正角度方向。所以绕x轴正转时,y轴会转向z轴。
绕y轴和绕z轴同理,形式略有不同:
code复制R_y(θ) = [ cos(θ) 0 sin(θ) ]
[ 0 1 0 ]
[ -sin(θ) 0 cos(θ) ]
R_z(θ) = [ cos(θ) -sin(θ) 0 ]
[ sin(θ) cos(θ) 0 ]
[ 0 0 1 ]
这三个矩阵是整个欧拉角体系的"积木"。任意一个欧拉角定义,本质上就是选三个基元旋转矩阵,按特定顺序相乘。矩阵乘法的顺序就是旋转施加的顺序。
2.3 内旋还是外旋:一个被低估的前提差异
这里必须花大篇幅说一个特别容易被忽略、却是工程中出问题最多的地方:内旋(intrinsic)与外旋(extrinsic)的区别。
外旋(extrinsic)的意思是:三次旋转都绕固定不动的参考坐标系的轴进行。比如说,先绕世界坐标系的z轴转yaw,再
