4、推力分配算法:伪逆法、二次规划法、神经网络法
好,咱们进入正题。推力分配,说白了就是——你手里有十几个甚至几十个电机,每个都能出力,但飞机只有一个总推力需求和几个力矩需求。怎么把这些“大目标”拆成每个电机的“小指令”?这就是推力分配算法要干的事。
我个人习惯把推力分配比作“分蛋糕”。蛋糕就那么大(总推力),但每个人(电机)能吃的量有限,而且有的人位置好(力臂长),稍微多吃点就能让飞机转起来。怎么分得又快又准?嗯,这里就有讲究了。
4.1 伪逆法:最直接的“一刀切”
伪逆法,也叫最小二乘法。它的核心思想很简单:既然方程个数(控制目标)少于变量个数(电机数量),那就找一个“最省力”的解。
数学上,我们通常把推力分配写成:
B * u = v
其中,B 是控制效率矩阵(每个电机对推力和力矩的贡献系数),u 是电机推力向量,v 是期望的控制量(总推力 + 三个轴的力矩)。
伪逆法的解就是:
u = B^T * (B * B^T)^(-1) * v
这个公式看着唬人,其实干的事就是:在满足 B*u=v 的所有解里,挑一个 u 的模长最小的。说白了,就是让所有电机出力尽可能均衡。
优点:计算量极小,实时性极好。在嵌入式芯片上,一次伪逆计算也就几十微秒。
缺点:完全不考虑电机限幅。如果某个电机算出来要出 120N 的力,但它最大只能出 100N,伪逆法不会帮你处理——它就硬给,然后执行器饱和,控制效果直接崩掉。
我的经验:伪逆法适合做“初始解”或者“参考解”。我在做四旋翼样机时,先用伪逆算一遍,再做个简单的限幅裁剪,对付一般工况够用了。但你要是搞 18 个电机的分布式推进,伪逆法就不太够看了——限幅问题会让你头疼死。
4.2 二次规划法:带约束的“最优解”
二次规划法(Quadratic Programming, QP)就聪明多了。它把推力分配写成一个优化问题:
minimize: J = (B*u - v)^T * W * (B*u - v) + u^T * R * u
subject to: u_min <= u <= u_max
你看,目标函数有两项:第一项是“控制误差”,第二项是“能耗惩罚”。权重矩阵 W 和 R 可以调,比如你更在意跟踪精度,就把 W 调大;更在意省电,就把 R 调大。
最关键的是——它直接带了约束条件 u_min <= u <= u_max。每个电机的出力上限、下限,甚至变化率限制,都能写进去。
优点:能处理各种约束,解出来的结果一定是物理上可执行的。
缺点:计算量大。实时求解 QP 问题,在普通 MCU 上可能得跑几毫秒甚至十几毫秒。对于 50Hz 甚至 100Hz 的控制周期,这个时间预算很紧张。
避坑指南:我曾经在一个项目里直接用 MATLAB 的 quadprog 函数生成代码,结果在 STM32H7 上跑一次要 8ms。后来换了 OSQP 求解器,优化到 1.2ms。所以,求解器的选择非常关键——别自己手写 QP 求解器,除非你是数学博士。
另外,QP 的权重调参也是个手艺活。我建议你先固定 R 为一个很小的值(比如 0.001),然后调 W 让跟踪误差满足要求。最后再慢慢增加 R,找到一个“精度”和“能耗”的平衡点。
4.3 神经网络法:数据驱动的“黑盒”
神经网络法,听起来高大上,其实思路也很朴素:既然伪逆法和 QP 都有各自的毛病,那能不能让神经网络“学”一个映射关系?输入是期望控制量 v 和当前状态,输出就是每个电机的推力指令 u。
具体做法分两步:
- 离线训练:用 QP 或者伪逆法生成大量样本——比如 10 万组 (v, u) 对。然后用这些数据训练一个前馈神经网络。
- 在线推理:飞行时,直接把
v塞进神经网络,前向传播一次,得到u。
网络结构不用太复杂,我一般用 3-4 层全连接层,每层 32-64 个神经元,激活函数用 ReLU。输入层维度是 4(总推力 + 三轴力矩),输出层维度是电机数量。
// 伪代码示例:神经网络推力分配
float nn_allocate(float F_des, float Mx_des, float My_des, float Mz_des) {
float input[4] = {F_des, Mx_des, My_des, Mz_des};
float output[N_MOTORS];
// 前向传播
for (int layer = 0; layer < N_LAYERS; layer++) {
for (int j = 0; j < layer_sizes[layer+1]; j++) {
output[j] = bias[layer][j];
for (int i = 0; i < layer_sizes[layer]; i++) {
output[j] += weights[layer][i][j] * (layer==0 ? input[i] : hidden[layer-1][i]);
}
output[j] = relu(output[j]); // 或者用 tanh
}
}
return output;
}
优点:推理速度极快。一次前向传播,在 Cortex-M4 上也就几十微秒,比 QP 快两个数量级。
缺点:训练数据依赖 QP 或伪逆法生成,本质上是在“模仿”已有算法。如果训练数据覆盖不到某些极端工况,神经网络可能会给出离谱的结果。
我的建议:神经网络法最适合做“在线查表”的替代品。如果你有一张巨大的推力分配表(比如 100 万行),查表太慢,用神经网络拟合一下,效果很好。但如果你追求绝对的安全性和可解释性,还是老老实实用 QP 吧。
4.4 三种方法的对比
| 方法 | 计算速度 | 约束处理 | 实现难度 | 适用场景 |
|---|---|---|---|---|
| 伪逆法 | 极快(~50μs) | 无 | 低 | 简单系统、快速原型 |
| 二次规划法 | 中等(~1-5ms) | 强 | 中 | 高可靠、多约束系统 |
| 神经网络法 | 快(~100μs) | 弱(需训练保证) | 高 | 大规模系统、查表替代 |
你看,没有银弹。伪逆法快但不聪明,QP 聪明但慢,神经网络快但需要大量数据。实际工程中,我见过不少团队把三种方法混着用——正常工况用伪逆法,遇到限幅时切到 QP,再配合一个神经网络做热备份。
4.5 核心逻辑框架图
下面这张图,是我自己画的一个推力分配算法选型决策流程。你想想看,拿到一个分布式电推进项目,第一步该选哪个方法?
这张图我画得比较直白。你从“开始”往下走,先看电机数量——少于 8 个,伪逆法基本够用;多于 8 个,再看约束条件。约束严格就走 QP,但 QP 算不过来怎么办?那就用神经网络法替代。嗯,实际项目中,我一般会在 QP 和神经网络之间加一个“看门狗”——如果神经网络输出超出限幅,立刻切回 QP 算一帧作为安全兜底。
好了,推力分配这块就聊到这儿。三种方法各有各的脾气,选哪个取决于你的硬件算力、实时性要求、以及对安全性的执念。我个人建议:新手从伪逆法入手,跑通了再升级到 QP;神经网络嘛,等你把前两种玩透了再碰,不然出了问题你都不知道锅该甩给谁。