3、姿态解算基础:欧拉角、旋转矩阵、四元数、坐标系定义与转换
各位同学,欢迎来到第三讲。
说实话,姿态解算是整个飞控系统的灵魂。你想想看,无人机在天上飞,它得知道自己「脑袋」朝哪、「身子」歪没歪。这个「自知之明」,就是靠姿态解算给的。
我当年刚入行时,被欧拉角和四元数绕得晕头转向。后来踩了不少坑,才慢慢理清楚。今天我就把这些经验掰开揉碎,讲给你听。
3.1 坐标系定义:先把「家」安好
做姿态解算,第一件事就是定坐标系。坐标系没定好,后面全是糊涂账。
我们常用两套坐标系:
- 地理坐标系(n系):也叫导航坐标系。通常取「北-东-地」或者「北-东-天」。我个人习惯用「北-东-地」,因为和惯导教材一致。X轴指北,Y轴指东,Z轴指向地心。
- 机体坐标系(b系):固定在无人机上。X轴指向机头,Y轴指向右翼,Z轴指向机身下方(符合右手定则)。
嗯,这里要注意:两个坐标系的原点重合,都在无人机的重心。我们做的所有姿态解算,本质上就是求这两个坐标系之间的旋转关系。
核心思想:姿态解算 = 求「b系」相对于「n系」的旋转关系。
3.2 欧拉角:最直观,但最危险
欧拉角是三个角度:横滚角(Roll, φ)、俯仰角(Pitch, θ)、偏航角(Yaw, ψ)。
说白了,就是绕三个轴依次转三次。顺序很重要!我们常用的是「Z-Y-X」顺序:先绕Z轴转ψ(偏航),再绕Y轴转θ(俯仰),最后绕X轴转φ(横滚)。
我曾经踩过的坑:有一次做固定翼飞控,我用了「X-Y-Z」顺序,结果飞机一抬头,横滚角也跟着变。查了两天,才发现是旋转顺序搞反了。记住:旋转顺序不可交换,这是欧拉角最大的坑。
欧拉角的优点很明显:直观,三个数就能描述姿态。但缺点也很致命:万向锁(Gimbal Lock)。当俯仰角接近±90°时,横滚和偏航会耦合,丢失一个自由度。你想想看,无人机做筋斗动作时,俯仰角刚好经过90°,这时候欧拉角就失效了。
我的建议:欧拉角只适合做人机交互(比如显示给飞手看),不适合做内部解算。内部解算,请用四元数。
3.3 旋转矩阵:数学上完美,计算上沉重
旋转矩阵是一个3×3的正交矩阵,记作 C_n^b(从n系到b系的旋转矩阵)。
用欧拉角可以构建旋转矩阵:
C_n^b = R_x(φ) · R_y(θ) · R_z(ψ)
其中:
R_x(φ) = [1, 0, 0;
0, cosφ, sinφ;
0, -sinφ, cosφ]
R_y(θ) = [cosθ, 0, -sinθ;
0, 1, 0;
sinθ, 0, cosθ]
R_z(ψ) = [cosψ, sinψ, 0;
-sinψ, cosψ, 0;
0, 0, 1]
旋转矩阵的好处是:没有奇点,数学上完美。而且向量旋转、坐标系变换都可以直接用矩阵乘法搞定。
但坏处是:9个参数,计算量大。在嵌入式MCU上,每次更新都要做9次乘加运算,还要做正交化修正(因为数值误差会导致矩阵不再正交)。
实际工程中:旋转矩阵常用于坐标变换(比如把加速度计读数从b系转到n系),但很少用于姿态递推。姿态递推,我们交给四元数。
3.4 四元数:飞控界的「瑞士军刀」
四元数是个超复数,形式为:q = q0 + q1·i + q2·j + q3·k,其中 i² = j² = k² = i·j·k = -1。
你别被数学吓到。在飞控里,我们只把它当成一个4维单位向量来用:
q = [q0, q1, q2, q3]ᵀ,且 q0² + q1² + q2² + q3² = 1
四元数最大的优点是:紧凑、无奇点、计算快。4个参数,没有万向锁,而且更新时只需要做四元数乘法(4次乘加),比旋转矩阵快一倍以上。
从欧拉角转四元数:
q0 = cos(φ/2)·cos(θ/2)·cos(ψ/2) + sin(φ/2)·sin(θ/2)·sin(ψ/2)
q1 = sin(φ/2)·cos(θ/2)·cos(ψ/2) - cos(φ/2)·sin(θ/2)·sin(ψ/2)
q2 = cos(φ/2)·sin(θ/2)·cos(ψ/2) + sin(φ/2)·cos(θ/2)·sin(ψ/2)
q3 = cos(φ/2)·cos(θ/2)·sin(ψ/2) - sin(φ/2)·sin(θ/2)·cos(ψ/2)
从四元数转欧拉角:
φ = atan2(2·(q0·q1 + q2·q3), 1 - 2·(q1² + q2²))
θ = asin(2·(q0·q2 - q3·q1))
ψ = atan2(2·(q0·q3 + q1·q2), 1 - 2·(q2² + q3²))
我的习惯:在飞控代码里,我永远用四元数做姿态递推。只在最后输出给地面站时,才转成欧拉角。这样既避免了万向锁,又保证了计算效率。
3.5 它们之间的转换关系
这三种表示方法可以互相转换。我整理了一张表,你收藏好:
| 转换方向 | 方法 | 注意事项 |
|---|---|---|
| 欧拉角 → 旋转矩阵 | 按顺序乘三个基本旋转矩阵 | 注意旋转顺序(Z-Y-X) |
| 欧拉角 → 四元数 | 用半角公式计算 | 注意角度单位(弧度) |
| 旋转矩阵 → 四元数 | 从矩阵元素反解 | 注意数值稳定性 |
| 四元数 → 旋转矩阵 | 用四元数元素构造 | 确保四元数已归一化 |
| 四元数 → 欧拉角 | 用atan2和asin | 俯仰角接近±90°时注意 |
我曾经犯过的错:有一次在STM32上做四元数转欧拉角,我直接用asin(),结果输入值略大于1(因为浮点误差),直接返回NaN。从那以后,我都在asin()前加一句if(val > 1.0f) val = 1.0f;。这种小细节,能救你一命。
3.6 实际工程中的选择建议
说了这么多,到底怎么选?我给你一个实战清单:
- 姿态递推(IMU积分):用四元数。效率高,无奇点。
- 坐标变换(比如把加速度转到导航系):用旋转矩阵。直观,好调试。
- 人机交互(显示给飞手):用欧拉角。飞手只看角度,不看四元数。
- 数据存储(比如记录飞行日志):用四元数。4个float,比9个float省空间。
总结一句话:心里想着欧拉角,手里算着四元数,代码里用着旋转矩阵。这三者你都得会,缺一不可。
好了,这一讲就到这里。下一讲我们开始讲「姿态解算的核心算法:互补滤波与卡尔曼滤波」。到时候我会手把手带你写代码。下课!
课后练习:写一个函数,输入四元数,输出欧拉角。注意处理asin的边界情况。写完后,用单位四元数(1,0,0,0)测试,看看输出是不是(0°, 0°, 0°)。