4. 动力学建模:牛顿-欧拉方程、刚体运动学、空气动力学、电机与螺旋桨模型

好,咱们进入正题。这一章是四轴飞行器控制的理论基石——动力学建模。说白了,就是搞清楚飞机为什么会飞,以及怎么用数学描述它的运动。

我个人习惯把建模分成四块:刚体运动学、牛顿-欧拉方程、空气动力学、电机与螺旋桨模型。每一块都踩过坑,咱们慢慢聊。

4.1 刚体运动学:位置、速度与姿态

先讲运动学。运动学只描述运动,不管力。你想想看,飞机在天上飞,它的位置、速度、加速度怎么表示?

我们通常用两个坐标系:地球坐标系(惯性系)机体坐标系。地球坐标系固定在地面,机体坐标系跟着飞机转。

位置和速度很简单,就是在地球坐标系下的三个分量:x, y, z 和 vx, vy, vz。但姿态就有点绕了。

描述姿态,我建议用四元数。为什么?因为欧拉角有万向锁问题,我在项目中吃过这个亏。有一次调试悬停,飞机突然乱转,查了半天发现是欧拉角在俯仰90度时奇异性导致的。从那以后,我全部改用四元数。

四元数 q = [q0, q1, q2, q3]^T,满足 q0^2 + q1^2 + q2^2 + q3^2 = 1。它的更新方程是:

dq/dt = 0.5 * q ⊗ ω

其中 ω 是机体角速度,⊗ 表示四元数乘法。这个方程在代码里实现很简单,但要注意归一化——我见过有人忘了归一化,积分几次后四元数就飘了。

注意:四元数更新后一定要归一化!否则姿态会慢慢漂移。我曾经在仿真里跑了一小时,发现飞机自己转了90度,就是因为没归一化。

4.2 牛顿-欧拉方程:力与力矩

运动学讲完了,该讲动力学了。动力学研究力和运动的关系。四轴飞行器是刚体,所以用牛顿-欧拉方程。

牛顿方程(平动):

m * dv/dt = F_gravity + F_thrust + F_drag

m 是质量,v 是速度。F_gravity 是重力,F_thrust 是四个电机产生的总推力,F_drag 是空气阻力。

欧拉方程(转动):

J * dω/dt = τ - ω × (J * ω)

J 是惯性矩阵,ω 是角速度,τ 是外力矩。注意那个叉乘项 ω × (J * ω),这是陀螺效应。我刚开始做控制时忽略了这一项,结果高速旋转时飞机抖得厉害。

嗯,这里要注意:惯性矩阵 J 是对角阵吗?理论上,如果飞机完全对称,J 是对角阵。但实际中,电池、摄像头等负载会导致不对称。我建议在建模时保留非对角项,虽然计算复杂点,但更准确。

核心公式总结:

  • 平动:m * a = F_total
  • 转动:J * α = τ_total - ω × (J * ω)

4.3 空气动力学:阻力与升力

空气动力学是四轴飞行器建模里最玄学的部分。为什么?因为空气流动太复杂了,很难精确建模。

我们通常用简化模型:

  • 升力:每个螺旋桨产生的升力与转速平方成正比。F_i = k_f * ω_i^2
  • 阻力:平动时受到的空气阻力与速度平方成正比。F_drag = 0.5 * ρ * C_d * A * v^2
  • 反扭矩:螺旋桨旋转时产生的反扭矩。τ_i = k_m * ω_i^2

k_f 和 k_m 是螺旋桨的力系数和力矩系数,需要通过实验标定。我记得第一次做标定时,用拉力计测数据,发现 k_f 在不同转速下不是常数——因为螺旋桨在高速时效率会下降。

所以,我建议在建模时把 k_f 和 k_m 做成转速的函数,而不是常数。虽然麻烦点,但精度提升明显。

避坑指南:我曾经用恒定的 k_f 做仿真,结果实际飞行时推力总是不够。后来发现,螺旋桨在低转速和高转速下的效率差很多。最好做一组标定曲线,插值使用。

4.4 电机与螺旋桨模型

电机和螺旋桨是执行机构,把控制信号变成力和力矩。常用的电机是无刷直流电机(BLDC),配合电子调速器(ESC)。

电机模型通常用一阶惯性环节:

dω/dt = (1/τ_m) * (ω_cmd - ω)

τ_m 是电机时间常数,ω_cmd 是期望转速,ω 是实际转速。这个模型很简单,但够用。

螺旋桨模型就是前面说的:

F = k_f * ω^2
τ = k_m * ω^2

注意,四个螺旋桨的旋转方向是交替的:两个顺时针,两个逆时针。这样做的目的是抵消反扭矩,让飞机不会自旋。

我建议在代码里这样组织电机模型:

// 电机模型
typedef struct {
    float omega;      // 当前转速
    float omega_cmd;  // 期望转速
    float tau_m;      // 时间常数
    float kf;         // 力系数
    float km;         // 力矩系数
} MotorModel;

void motor_update(MotorModel *motor, float dt) {
    // 一阶惯性
    motor->omega += (motor->omega_cmd - motor->omega) / motor->tau_m * dt;
    // 限幅
    if (motor->omega < 0) motor->omega = 0;
    if (motor->omega > MAX_OMEGA) motor->omega = MAX_OMEGA;
}

这个代码我用了很多年,简单可靠。但要注意,实际电机的响应比一阶模型复杂,有延迟、有非线性。如果做高精度控制,建议用更精细的模型。

4.5 完整动力学方程

把上面所有东西拼起来,就是完整的动力学方程:

// 状态量:位置、速度、姿态(四元数)、角速度
// 输入量:四个电机的转速指令

// 1. 电机模型:计算实际转速
for (i = 0; i < 4; i++) {
    motor_update(&motor[i], dt);
}

// 2. 力和力矩计算
F_total = [0, 0, -m*g] + sum(F_i) + F_drag;
tau_total = sum(tau_i) + tau_gyro;

// 3. 牛顿方程:更新速度
dv = F_total / m * dt;

// 4. 欧拉方程:更新角速度
domega = J_inv * (tau_total - omega × (J * omega)) * dt;

// 5. 运动学:更新位置和姿态
pos += vel * dt;
q += 0.5 * q ⊗ omega * dt;
q = normalize(q);

这个流程就是四轴飞行器仿真的核心。我在做飞控算法验证时,都是先跑这个模型,再上真机。为什么?因为真机炸一次成本太高,仿真里随便炸。

重要提醒:仿真模型再精确,也和真实飞行有差距。我建议在仿真中故意加入一些噪声和不确定性,比如传感器噪声、电机延迟、重心偏移等。这样仿真出来的结果才更有参考价值。

4.6 参数辨识

模型建好了,参数怎么来?靠猜肯定不行。我常用的方法:

  • 质量 m:直接称重
  • 惯性矩阵 J:用 CAD 软件估算,或者做摆锤实验
  • 力系数 k_f、力矩系数 k_m:用拉力台标定
  • 电机时间常数 τ_m:测阶跃响应,拟合一阶模型
  • 阻力系数 C_d:风洞实验,或者飞行数据反推

说实话,参数辨识是最枯燥但最重要的一步。我见过有人直接拿别人的参数用,结果飞机飞起来像喝醉了酒。每个飞机的参数都不一样,必须自己测。

我的经验:参数辨识不要一次做完。先标定电机和螺旋桨,再标定惯量,最后标定气动参数。分步做,容易排查问题。

4.7 小结

这一章内容不少,但核心就几句话:

  • 运动学描述位置和姿态的变化
  • 动力学描述力和运动的关系
  • 空气动力学简化了升力和阻力
  • 电机和螺旋桨把控制信号变成力

把这些模型写进代码,你就有了一个四轴飞行器的仿真器。下一章,我们会在这个模型基础上设计控制器。嗯,到时候你会发现,模型越精确,控制器越好调。

好了,这一章就到这。有什么问题,咱们下一章见。