3、欧拉角:定义、万向锁与旋转顺序
欧拉角这个东西,说实话,是每个做机器人的人都会遇到的「老朋友」。我刚入行那会儿,觉得它直观又好用,后来在项目里被它坑过几次,才真正理解了它的脾气。
简单来说,欧拉角就是用三个角度来描述一个物体的朝向。比如飞机,你可以说它「俯仰了多少度,偏航了多少度,滚转了多少度」。听起来很自然对吧?但这里头藏着不少门道。
3.1 欧拉角的定义
欧拉角的本质,是「绕三个坐标轴依次旋转」。每次旋转都绕着物体当前的某个轴,而不是世界坐标系下的固定轴。这一点很重要,我见过不少新手在这里搞混。
常用的表示方式有几种:
- ZYX 顺序:先绕 Z 轴转(偏航),再绕 Y 轴转(俯仰),最后绕 X 轴转(滚转)。这是航空领域最常用的。
- ZYZ 顺序:先绕 Z,再绕 Y,再绕 Z。常见于机械臂的运动学分析。
- XYZ 顺序:先绕 X,再绕 Y,再绕 Z。有些传感器会这么输出。
每个顺序对应一组不同的旋转矩阵。比如 ZYX 顺序的旋转矩阵可以写成:
R = Rz(yaw) * Ry(pitch) * Rx(roll)
注意乘法的顺序——从右往左读。先转的轴在右边,后转的在左边。我刚开始写代码时总搞反,后来养成了一个习惯:每次写完都拿个手机在手里转一转验证一下。
核心要点:欧拉角用三个角度描述旋转,但旋转顺序决定了最终朝向。同一个角度值,不同顺序会得到完全不同的结果。
3.2 万向锁问题
这是欧拉角最出名的一个坑。什么叫万向锁?说白了,就是当中间那个轴的旋转角度达到 ±90° 时,你会丢失一个自由度。
举个例子。你用 ZYX 顺序控制一个云台。当俯仰角(绕 Y 轴)转到 90° 时,你会发现:不管你怎么转偏航(绕 Z)和滚转(绕 X),云台都只能在一个平面里动。你失去了一个方向的调节能力。
为什么会这样?
因为当俯仰角为 90° 时,第一次旋转的 Z 轴和第三次旋转的 X 轴,在空间中对齐了。两个轴变成了同一个轴。三个自由度变成了两个。
我曾经在一个无人机项目里遇到过这个问题。当时用欧拉角做姿态控制,飞机做大角度俯冲时突然失控了。查了半天,发现就是万向锁导致的。从那以后,我只要做大角度运动,就坚决不用欧拉角。
避坑指南:我曾经在调试一个机械臂时,用欧拉角做逆解,结果在某个奇异点附近怎么都算不对。后来换成四元数,问题立刻解决了。如果你要做全姿态运动,建议直接上四元数或旋转矩阵。
3.3 不同旋转顺序的影响
不同旋转顺序,不仅仅是数学上的差异,在实际应用中影响很大。
| 旋转顺序 | 常见应用 | 特点 |
|---|---|---|
| ZYX | 无人机、飞行器 | 偏航-俯仰-滚转,直观且符合直觉 |
| ZYZ | 机械臂运动学 | 避免某些奇异点,适合关节型机器人 |
| XYZ | IMU 传感器 | 某些传感器默认输出顺序 |
| YXZ | 相机姿态 | 适合俯仰-偏航-滚转的场景 |
你想想看,如果你用 ZYX 顺序,偏航角的变化会影响俯仰和滚转的「参考系」。而如果你用 XYZ 顺序,情况又不一样了。所以,在写代码之前,一定要搞清楚你的传感器或算法用的是哪种顺序。
我个人习惯在代码里这样处理:
// 明确指定旋转顺序
enum class EulerOrder {
ZYX, // 航空标准
ZYZ, // 机械臂
XYZ // 传感器
};
// 根据顺序生成旋转矩阵
Matrix3d eulerToMatrix(double roll, double pitch, double yaw, EulerOrder order) {
Matrix3d R;
switch(order) {
case EulerOrder::ZYX:
R = Rz(yaw) * Ry(pitch) * Rx(roll);
break;
case EulerOrder::ZYZ:
R = Rz(yaw) * Ry(pitch) * Rz(roll);
break;
// ... 其他顺序
}
return R;
}
嗯,这里要注意:不同库的默认顺序可能不一样。比如 ROS 里的 tf 库,默认用的是 ZYX。而某些数学库可能用 XYZ。我建议你在项目一开始就统一约定好顺序,并在文档里写清楚。
实用技巧:如果你不确定当前用的欧拉角是什么顺序,可以拿一个已知姿态做测试。比如让物体绕某个轴转 90°,看输出值的变化规律,就能反推出顺序。
3.4 知识体系图
下面这张图总结了欧拉角的核心知识点,方便你快速回顾:
欧拉角虽然直观,但用起来要小心。我个人建议:如果你只是做小角度的姿态微调,用欧拉角没问题。但如果你要做全姿态运动,或者你的系统可能经过奇异点,那就老老实实用四元数或者旋转矩阵吧。
好了,关于欧拉角就聊这么多。记住三个关键词:定义、万向锁、旋转顺序。下次写代码时多留个心眼,别在同一个坑里摔两次。