目录
概述
欧拉角是在三维空间中描述刚体方向的一种方式。欧拉角由三个角度组成,通常称为俯仰角(pitch)、滚转角(roll)和偏航角(yaw)。这些角度分别围绕物体的三个轴(通常是X、Y、Z轴)进行旋转。欧拉角的定义: 1. 俯仰角(pitch):围绕X轴的旋转角度,表示物体向上或向下的倾斜。 2. 滚转角(roll):围绕Y轴的旋转角度,表示物体向左或向右的翻滚。 3. 偏航角(yaw):围绕Z轴的旋转角度,表示物体在水平面上的方向。 注意:旋转顺序很重要。常见的顺序是:先偏航(yaw),再俯仰(pitch),最后滚转(roll)。但不同的应用可能使用不同的顺序。在航空航天领域,通常使用Z-Y-X顺序(即先偏航,再俯仰,最后滚转)。
1 欧拉角基础概念
欧拉角是描述物体在三维空间中方向的最直观方法之一,通过三个独立的旋转角度来定义物体相对于参考坐标系的姿态。
1.1 核心定义
欧拉角由三个旋转角度组成:
俯仰角 (Pitch):围绕X轴的旋转(抬头/低头)
滚转角 (Roll):围绕Y轴的旋转(左右倾斜)
偏航角 (Yaw):围绕Z轴的旋转(左右转向)
1.2 旋转顺序的重要性
欧拉角的表示取决于旋转顺序,常见顺序:
航空航天序列:偏航 → 俯仰 → 滚转 (Z-Y-X)
相机稳定序列:滚转 → 俯仰 → 偏航 (X-Y-Z)
机器人学序列:俯仰 → 滚转 → 偏航 (Y-X-Z)
1.3 欧拉角的数学表示
偏航角(yaw)ψ:绕Z轴旋转
俯仰角(pitch)θ:绕Y轴旋转
滚转角(roll)φ:绕X轴旋转
旋转矩阵R由三个基本旋转矩阵相乘得到(按顺序右乘):
R = R_z(ψ) * R_y(θ) * R_x(φ)
绕Z轴旋转ψ:
R_z(ψ) = | cosψ -sinψ 0 |
| sinψ cosψ 0 |
| 0 0 1 |
绕Y轴旋转θ:
R_y(θ) = | cosθ 0 sinθ |
| 0 1 0 |
| -sinθ 0 cosθ |
绕X轴旋转φ:
R_x(φ) = | 1 0 0 |
| 0 cosφ -sinφ |
| 0 sinφ cosφ |
因此,组合旋转矩阵R为:
R = R_z(ψ)*R_y(θ)*R_x(φ)
= | cosθ*cosψ sinφ*sinθ*cosψ - cosφ*sinψ cosφ*sinθ*cosψ + sinφ*sinψ | | cosθ*sinψ sinφ*sinθ*sinψ + cosφ*cosψ cosφ*sinθ*sinψ - sinφ*cosψ | | -sinθ sinφ*cosθ cosφ*cosθ |
完整数学公式:
2 从传感器数据计算欧拉角
2.1 仅用加速度计(静态条件)
void accel_to_euler(float ax, float ay, float az, float *roll, float *pitch)
{
// 计算滚转角 (绕Y轴)
*roll = atan2(ay, sqrt(ax*ax + az*az));
// 计算俯仰角 (绕X轴)
*pitch = atan2(-ax, az);
}
2.2 结合陀螺仪(动态条件)
void update_attitude(float *roll, float *pitch, float *yaw,
float gx, float gy, float gz,
float ax, float ay, float az,
float dt)
{
// 陀螺仪积分
*roll += gx * dt;
*pitch += gy * dt;
*yaw += gz * dt;
// 加速度计补偿(仅适用于低频运动)
float accel_roll = atan2(ay, sqrt(ax*ax + az*az));
float accel_pitch = atan2(-ax, az);
// 互补滤波
const float alpha = 0.98;
*roll = alpha * (*roll) + (1-alpha) * accel_roll;
*pitch = alpha * (*pitch) + (1-alpha) * accel_pitch;
}
2.3 从四元数转换
void quaternion_to_euler(float q0, float q1, float q2, float q3,
float *roll, float *pitch, float *yaw)
{
// 滚转 (X轴旋转)
*roll = atan2(2*(q0*q1 + q2*q3), 1 - 2*(q1*q1 + q2*q2));
// 俯仰 (Y轴旋转)
float sinp = 2*(q0*q2 - q3*q1);
if (fabs(sinp) >= 1)
*pitch = copysign(M_PI/2, sinp);
else
*pitch = asin(sinp);
// 偏航 (Z轴旋转)
*yaw = atan2(2*(q0*q3 + q1*q2), 1 - 2*(q2*q2 + q3*q3));
// 转换为角度
*roll *= 180.0/M_PI;
*pitch *= 180.0/M_PI;
*yaw *= 180.0/M_PI;
}
3 欧拉角的应用领域
3.1 无人机飞行控制
// PID控制器实现
void drone_control(float target_roll, float target_pitch, float target_yaw,
float current_roll, float current_pitch, float current_yaw,
float *motor1, float *motor2, float *motor3, float *motor4)
{
// PID参数
const float Kp = 2.5, Ki = 0.05, Kd = 0.8;
// 计算误差
float roll_error = target_roll - current_roll;
float pitch_error = target_pitch - current_pitch;
float yaw_error = target_yaw - current_yaw;
// PID计算 (简化版)
float roll_correction = Kp * roll_error;
float pitch_correction = Kp * pitch_error;
float yaw_correction = Kp * yaw_error;
// 分配电机输出
*motor1 = BASE_SPEED + roll_correction + pitch_correction + yaw_correction;
*motor2 = BASE_SPEED - roll_correction + pitch_correction - yaw_correction;
*motor3 = BASE_SPEED + roll_correction - pitch_correction - yaw_correction;
*motor4 = BASE_SPEED - roll_correction - pitch_correction + yaw_correction;
}
3.2 3D相机控制系统
void camera_stabilization(float current_roll, float current_pitch,
float *gimbal_roll, float *gimbal_pitch)
{
// 目标角度(水平)
const float target_roll = 0;
const float target_pitch = 0;
// 比例控制
const float K = 0.8;
*gimbal_roll = -K * current_roll;
*gimbal_pitch = -K * current_pitch;
// 限幅保护
*gimbal_roll = constrain(*gimbal_roll, -45, 45);
*gimbal_pitch = constrain(*gimbal_pitch, -30, 30);
}
3.3 虚拟现实头部追踪
void vr_head_tracking(float roll, float pitch, float yaw,
float *view_matrix)
{
// 创建旋转矩阵
float cosR = cos(roll), sinR = sin(roll);
float cosP = cos(pitch), sinP = sin(pitch);
float cosY = cos(yaw), sinY = sin(yaw);
// 构建视图矩阵 (列优先)
view_matrix[0] = cosY*cosP;
view_matrix[1] = sinY*cosP;
view_matrix[2] = -sinP;
view_matrix[4] = cosY*sinP*sinR - sinY*cosR;
view_matrix[5] = sinY*sinP*sinR + cosY*cosR;
view_matrix[6] = cosP*sinR;
view_matrix[8] = cosY*sinP*cosR + sinY*sinR;
view_matrix[9] = sinY*sinP*cosR - cosY*sinR;
view_matrix[10] = cosP*cosR;
// 位置部分设为单位矩阵
view_matrix[12] = view_matrix[13] = view_matrix[14] = 0;
view_matrix[15] = 1;
}
4 欧拉角与其他表示法的比较
特性 | 欧拉角 | 四元数 | 旋转矩阵 | 轴角 |
---|---|---|---|---|
参数数量 | 3 | 4 | 9 | 4 |
奇异性 | 有 (万向锁) | 无 | 无 | 无 |
插值 | 困难 | 简单 (球面插值) | 复杂 | 简单 |
计算效率 | 高 | 高 | 低 | 中 |
直观性 | 最好 | 差 | 中 | 中 |
组合旋转 | 困难 | 简单 | 简单 | 困难 |
存储需求 | 最小 | 小 | 大 | 小 |
5 欧拉角的最佳实践
5.1 避免万向节死锁
// 俯仰角安全限制
const float MAX_PITCH = 85.0f; // 度
float safe_pitch(float pitch)
{
if (pitch > MAX_PITCH) return MAX_PITCH;
if (pitch < -MAX_PITCH) return -MAX_PITCH;
return pitch;
}
// 在控制循环中使用
current_pitch = safe_pitch(current_pitch);
5.2 角度归一化
// 将角度归一化到 [-180, 180] 范围
float normalize_angle(float angle)
{
while (angle > 180.0f) angle -= 360.0f;
while (angle < -180.0f) angle += 360.0f;
return angle;
}
// 在误差计算中使用
float error = normalize_angle(target - current);
5.3 平滑滤波
// 一阶低通滤波器
float low_pass_filter(float new_value, float old_value, float alpha)
{
return alpha * new_value + (1 - alpha) * old_value;
}
// 在姿态更新中使用
current_roll = low_pass_filter(raw_roll, current_roll, 0.2f);
current_pitch = low_pass_filter(raw_pitch, current_pitch, 0.2f);
5.4 多传感器融合
void sensor_fusion(float *roll, float *pitch, float *yaw,
float accel_x, float accel_y, float accel_z,
float gyro_x, float gyro_y, float gyro_z,
float mag_x, float mag_y, float mag_z,
float dt)
{
// 1. 陀螺仪积分
*roll += gyro_x * dt;
*pitch += gyro_y * dt;
*yaw += gyro_z * dt;
// 2. 加速度计补偿滚转和俯仰
float accel_roll = atan2(accel_y, sqrt(accel_x*accel_x + accel_z*accel_z));
float accel_pitch = atan2(-accel_x, accel_z);
// 互补滤波
const float alpha = 0.98;
*roll = alpha * (*roll) + (1 - alpha) * accel_roll;
*pitch = alpha * (*pitch) + (1 - alpha) * accel_pitch;
// 3. 磁力计补偿偏航
float mag_roll = *roll;
float mag_pitch = *pitch;
// 磁力矢量补偿
float mag_x_comp = mag_x * cos(mag_pitch) + mag_z * sin(mag_pitch);
float mag_y_comp = mag_x * sin(mag_roll) * sin(mag_pitch) +
mag_y * cos(mag_roll) -
mag_z * sin(mag_roll) * cos(mag_pitch);
float mag_yaw = atan2(-mag_y_comp, mag_x_comp);
// 偏航滤波
*yaw = alpha * (*yaw) + (1 - alpha) * mag_yaw;
}
6 高级主题:方向余弦矩阵 (DCM)
当需要避免奇异性时,DCM 是欧拉角的良好替代方案:
1) DCM 初始化
void init_dcm(float dcm[3][3])
{
// 初始化为单位矩阵
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
dcm[i][j] = (i == j) ? 1.0f : 0.0f;
}
}
}
2)DCM 更新
void update_dcm(float dcm[3][3], float wx, float wy, float wz, float dt)
{
// 角速度矩阵
float omega[3][3] = {
{0, -wz, wy},
{wz, 0, -wx},
{-wy, wx, 0}
};
// 临时矩阵 I + Ω*dt/2
float temp[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
temp[i][j] = (i == j ? 1.0f : 0.0f) + omega[i][j] * dt / 2;
}
}
// 更新DCM: DCM' = DCM * (I + Ω*dt/2)
float new_dcm[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
new_dcm[i][j] = 0;
for (int k = 0; k < 3; k++) {
new_dcm[i][j] += dcm[i][k] * temp[k][j];
}
}
}
// 复制回原矩阵
memcpy(dcm, new_dcm, sizeof(new_dcm));
}
3)DCM 到欧拉角转换
void dcm_to_euler(const float dcm[3][3], float *roll, float *pitch, float *yaw)
{
// 俯仰角
*pitch = asin(-dcm[2][0]);
// 滚转角
*roll = atan2(dcm[2][1], dcm[2][2]);
// 偏航角
*yaw = atan2(dcm[1][0], dcm[0][0]);
// 转换为角度
*roll *= 180.0f / M_PI;
*pitch *= 180.0f / M_PI;
*yaw *= 180.0f / M_PI;
}