简介:一份面向航空控制领域研究者的固定翼容错控制与控制分配Matlab资源包,聚焦多操纵面飞行器在传感器或执行机构故障下的重构与控制分配问题,覆盖故障检测与诊断、容错控制器设计、基于LMI/优化算法的控制分配等关键环节。包内含99个文件,主要包含Simulink模型(slx)、Matlab脚本(m)、数据文件(mat)以及两篇控制分配方向的文献(caj/pdf),另有辅助的slxc与xml配置和zip压缩包,整体大小约5.84MB。已有288人学习使用。资料不仅提供x_33_model.m、QP_FXP_PI Para.m等可直接运行的代码,还包含QP_FXP_2016a.slx等Simulink仿真模型,可用于复现多操纵面飞行器受限控制分配与重构研究,配套的郭梦旭、张栋等参考文献便于追溯理论依据。适合正在研究飞行控制重构、控制分配算法的研究生或工程师,借此可直接上手仿真验证,加速算法设计与对比分析。
1. 固定翼容错控制与控制分配:故障发生后,谁在负责改舵面指令
做固定翼六自由度仿真时,我遇到过最"经典"的一次翻车:右副翼模拟卡死,控制器还在按原来的分配表把滚转指令发给它,飞机在几秒内滚转、侧滑、掉高度一起出现。事后复盘发现,问题不在控制器,而在分配器——它不知道右侧副翼已经失去了响应。固定翼容错控制与控制分配,解决的就是"哪个舵面不行了、剩下的舵面怎么重新出力"这件事。控制分配负责把一个虚拟控制力矩(滚转、俯仰、偏航三个通道)拆成多个舵面的偏转指令;容错控制负责感知故障并调整分配的依据。用 Matlab 做这套仿真链路,不需要先懂很多前沿理论,把执行器故障模型、分配算法、重构逻辑串起来就能看到效果。适合正在做飞控方向的毕业生,以及想给自研固定翼飞控加一层保底逻辑的工程师。
2. 先搭执行器故障模型:四类故障模式和效率矩阵参数化
2.1 固定翼舵面与力矩通道的效率矩阵 B
固定翼控制分配的第一步,是拿到一张"舵面偏度 → 力矩"的映射表。常见做法是把六自由度非线性方程在某个配平点线性化,得到状态空间里的输入矩阵,习惯上记作 B。舵面向量 u 通常写成 [左副翼、右副翼、升降舵、方向舵],力矩向量 v 对应 [滚转力矩 L、俯仰力矩 M、偏航力矩 N] 三个通道。那么 v = B * u。
以一个常规布局固定翼为例,B 的结构大致是:
% 示意:3 个力矩通道,4 个舵面,单位 N*m/rad % 行:滚转 L、俯仰 M、偏航 N % 列:左副翼、右副翼、升降舵、方向舵 B = [ 0.35 0.35 0.00 -0.05 ; 0.00 0.00 0.62 0.00 ; 0.02 -0.02 0.00 -0.28 ];这个矩阵看起来简单,但它的每一列都来自气动导数。副翼主要产生滚转力矩,同时还附带一点偏航力矩;方向舵主要产生偏航力矩,大侧滑时也带滚转。升降舵相对独立。实际工程里,这些系数从风洞数据、CFD 或线性化工具箱里取,不要在仿真里拍脑袋写。B 矩阵是后面所有容错逻辑的"地基":故障发生,本质上就是 B 的某些列变了,控制分配器却还在用旧 B 算指令。
2.2 卡死、松浮、部分失效、饱和:故障在方程里长什么样
执行器故障有四种经典模式,建模时一定要分开,因为它们对分配器的影响不一样:
| 故障模式 | 数学表达 | 对分配器的影响 |
|---|---|---|
| 卡死(Lock-in-Place) | u_i(t) = δf(常数) | 效率矩阵对应列失效,且额外产生一个固定力矩 |
| 松浮(Floating) | η_i ≈ 0,舵面随风摆动 | 效率矩阵对应列接近零 |
| 部分失效(LoE) | η_i ∈ (0,1) | 效率矩阵对应列按比例缩小 |
| 饱和(Saturation) | 指令超出位置或速率限制 | 等效于一个时变的效率下降 |
把这四类统一写成一个参数化表达式,最方便的做法是:
% 执行器故障统一表达:u_act = E * u_cmd + u_bias % E = diag(eta),eta 是逐舵面健康系数,1 为健康,0 为完全失效 % u_bias 是卡死位置偏置,健康舵面为 0设计故障模型时,需要为每个舵面设置几个参数:健康系数 eta、卡死偏置 bias、位置上下限 umin/umax、速率限制 rmax。注意卡死和失效的区别:卡死舵面虽然指令控制不了它,但它仍然钉在某个角度,持续产生一个固定力矩;而松浮舵面基本不产生力矩。分配器重构时,前者要做力矩补偿,后者只需把对应列清零。
2.3 用 Matlab 把故障注入做成一个函数
我一般会把执行器故障封装成一个独立的 Matlab 函数,放在 Simulink 的 MATLAB Function 模块里调用,这样故障注入和物理执行器模型可以共用一份代码。函数签名和核心逻辑如下:
function u_act = act_fault_model(u_cmd, mode, params, u_prev, Ts) % 固定翼舵面故障模型 % mode: 'healthy' | 'lock' | 'floating' | 'loe' | 'saturation' % params: 结构体,含 eta, bias, umin, umax, rmax u_cmd = u_cmd(:); n = length(u_cmd); switch mode case 'lock' % 卡死:输出固定在 bias,指令完全不通过 eta = zeros(n,1); bias = params.bias; case 'floating' % 松浮:效率归零,无额外偏置 eta = zeros(n,1); bias = zeros(n,1); case 'loe' % 部分失效:eta 逐舵面给定,比如右副翼只剩 60% eta = params.eta; bias = zeros(n,1); otherwise % 健康 eta = ones(n,1); bias = zeros(n,1); end % 位置层面的"指令通过效率矩阵" u_int = diag(eta) * u_cmd + bias; % 速率限制:把上一时刻位置 u_prev 考虑进来 u_act = max(min(u_int, u_prev + params.rmax * Ts), ... u_prev - params.rmax * Ts); % 位置饱和限幅 u_act = max(params.umin, min(params.umax, u_act)); end这个函数的重点是:卡死模式下 eta 全为 0,指令被掐断,但 bias 保留,舵面位置钉在故障角度;LoE 模式下 eta 是 0 到 1 之间的向量,可以只让某一侧副翼降效;速率限制放在位置饱和之前,避免出现"指令一步跳变到新值"的理想化情形。在仿真里给这个函数传入不同 mode,就能在设定时刻模拟出固定翼最常见的执行器故障。参数设置上,rmax 一般取舵机实际角速度上限(比如 60 deg/s),位置限制按舵面机械行程取,两者不要一起忽略,否则后面验证重构逻辑时会得到过度乐观的结果。
3. 控制分配算法:从伪逆到二次规划,怎么写进 Matlab 还能跑闭环
3.1 伪逆分配的边界:约束去哪儿了
控制分配最直观的算法是伪逆法。已知 v = B * u,当 B 是"行数小于列数"的矩阵时,最小范数解是:
% 伪逆分配:把期望力矩拆到所有健康舵面上 u = pinv(B) * v; % 等价于 u = B' * inv(B * B') * v(当 B 满行秩时)伪逆法实现简单,B 固定时计算结果就是一组最小舵面偏度。它在固定翼仿真里有个明显问题:完全不考虑舵面的位置饱和和速率限制。如果期望力矩接近包线边缘,伪逆给出的指令里某个舵面可能超过机械行程,飞机实际得到的力矩和期望值差一大截。另一个问题是,伪逆会把指令分配给所有非零列,包括效率低下的舵面——故障后如果忘了更新 B,指令会继续流向已经卡死的舵面。所以伪逆只适合做健康情况下的快速原型,或者作为后面更严谨算法的初值。
3.2 加权最小范数与二次规划:把约束装进去
把位置约束和速率约束一起接进来,常见且工程上收敛稳定的是加权二次规划(QP)形式。目标函数和约束如下:
% 目标:最小化 力矩分配误差 + 舵面偏度代价 % J = (B*u - v)' * Wv * (B*u - v) + ... % (u - u_ref)' * Wu * (u - u_ref) % 约束:umin <= u <= umax % |u - u_prev| <= rmax * Ts在 Matlab 里可以直接用 quadprog 求解。我一般写成独立函数,方便在 Simulink 里按离散周期调用:
function u = alloc_qp(B, v, u_ref, u_prev, Wu, Wv, ... umin, umax, rmax, Ts) % 控制分配:加权二次规划 % B : 3x4 控制效率矩阵 % v : 3x1 期望力矩(来自外环控制器) % u_ref : 4x1 舵面偏好位置,通常取配平舵面或 0 n = size(B, 2); % 构造 QP 的 H 和 f % quadprog 目标为 0.5*u'*H*u + f'*u H = B' * Wv * B + Wu + 1e-8 * eye(n); f = -B' * Wv * v - Wu * u_ref; % 位置约束和速率约束合并为上下界 lb = max(umin, u_prev - rmax * Ts); ub = min(umax, u_prev + rmax * Ts); opts = optimoptions('quadprog', ... 'Display', 'none', ... 'Algorithm', 'interior-point-convex'); u = quadprog(H, f, [], [], [], [], lb, ub, [], opts); end这段代码里有几个关键点。H 里的 1e-8 正则项是为了防止 B'WvB 在故障后接近奇异时数值发散。速率约束没有写成 Aineq 不等式,而是直接并进 lb/ub,因为单个舵面的速率限制只和上一时刻位置有关,等价于上下界的收紧。Wv 和 Wu 是分配器的"手感":如果想让滚转力矩优先被精确满足,就把 Wv 的第一个对角线元素调大;如果想让升降舵少动,就把 Wu 对应对角线元素调大。常见的做法是先把 Wv 设为对角单位阵,让 Wu 的权重比 Wv 低两个量级,避免约束冲突时最优解偏向"舵面不动"。
3.3 分配器里的偏好与滚转-偏航耦合
固定翼控制分配很容易出现一个现象:左右副翼和方向舵在同一个优化问题里"打架"。比如要一个偏航力矩,方向舵可以出,左右副翼差动也能出一点;伪逆法会把两者都用上,导致副翼产生额外的滚转,方向舵又去补偏航,舵面来回消耗。工程上解决这个问题有两种习惯做法:一是在 u_ref 里给方向舵一个中位偏好,让优化器在满足力矩要求的前提下尽量少用方向舵;二是显式设置加权矩阵 Wu,方向舵对应的权重设大。这两种做法在效果上等价,我一般直接改 u_ref,因为四舵面布局里方向舵的铰链力矩和偏航效率都不如主翼面,保持它在中位附近更安全。
另一个容易被轻视的点是滚转-偏航耦合。方向舵偏转会带来少量滚转力矩,副翼差动也会产生诱导偏航,B 矩阵的非对角元素就是这两条耦合通道。如果手写分配逻辑时把滚转和偏航拆成两个独立问题分别求逆,侧滑角一大,实际力矩和期望值对不上,飞机就会出现"滚转指令给足了但飞机转不动"的体验。QP 分配天然处理了这种耦合,因为它是对整个三轴力矩向量求最小二乘。所以我的建议是:只要舵面之间存在交叉力矩,就不要做分通道分配,直接保持 v 为三维向量、B 为完整矩阵,让优化器自己去权衡。
4. 主动容错重构链路:从故障检测到分配权重切换
4.1 故障检测的两条路:舵面位置回读与模型残差
要在分配之前做容错,得先知道哪个舵面坏了。固定翼工程里最常见、也最可靠的办法是读舵机位置反馈。舵机自带电位计或编码器时,把 u_fbk 和 u_cmd 逐拍比较,连续超过阈值一段时间就判定对应舵面故障。这里的关键是"连续超过",而不是单拍超限,否则一个瞬时扰动就会误报。
function fd_flag = actuator_fdd(u_cmd, u_fbk, tol_pos, Ts, hold_time) % 执行器故障检测:指令-反馈持续偏差 % tol_pos : 位置偏差阈值,单位 rad % hold_time: 持续超差时间,超过才确认故障,单位 s err = abs(u_fbk - u_cmd); persistent cnt; if isempty(cnt) cnt = zeros(size(err)); end cnt(err > tol_pos) = cnt(err > tol_pos) + 1; cnt(err <= tol_pos) = 0; fd_flag = cnt >= hold_time / Ts; end第二种方案是模型残差法。用当前舵面指令和状态量,通过气动模型算出预期角加速度,再和惯导实测值比较。残差持续偏大,就说明某个舵面效率下降。这种方法不依赖位置反馈,适合舵机没有传感器、或者只想在仿真里做被动容错验证的场景。但它的误报率明显更高,因为气动模型本身有误差,机动时残差本来就大。我会用位置回读做主判据,用残差法做交叉验证,两个通道同时报警才切换分配器,这样能过滤掉大量假信号。
4.2 重构逻辑:效率矩阵修正和卡死力矩补偿
检测到故障后,重构逻辑要做三件事:把故障舵面的效率列清零、调整分配权重、补偿卡死舵面带来的常值力矩。以一个卡死舵面为例,它的实际偏角固定在 bias 处,仍然产生 B0(:,i) * bias 这个力矩。这个力矩不属于任何指令,如果不抵消,飞机会被它带着偏。正确的做法是让它从期望力矩 v 里减掉,再把剩余力矩交给分配器:
function [B_est, Wu_new, v_eff] = reconfigure(B0, health, bias_f, v) % health: 4x1 逻辑向量,1 健康,0 故障 % bias_f: 4x1 故障舵面的卡死偏置,健康舵面为 0 % 1. 效率矩阵修正:故障列清零 B_est = B0; for i = 1:length(health) if health(i) < 0.5 B_est(:, i) = 0; end end % 2. 分配权重:故障舵面给极大惩罚,避免指令再流向它 Wu_new = diag(1 + 10 * (1 - health)); % 3. 卡死力矩补偿:从期望力矩中扣掉故障舵面的固定贡献 v_eff = v - B0 * bias_f; end重构逻辑里最容易写错的是第三步。很多人只做了 B_est 清零,忘了扣 bias 力矩,结果飞机在故障后仍然朝一个方向偏,无论分配器怎么调都拉不回来。扣掉 B0 * bias_f 之后,分配器就只需要让健康舵面去产生"期望力矩减去故障扰动"的净力矩,飞机才能在新的平衡点附近稳定。注意补偿力矩用的是原始 B0,不是清零后的 B_est,因为卡死舵面确实在物理上出力了,只是这部分力不受控。
4.3 把重构和外环控制器解耦:控制器不需要重新设计
主动容错与控制分配之间有一层容易被忽略的关系:重构逻辑只需改"舵面指令的生成方式",不需要碰外环姿态控制器。固定翼外环控制器输出的是期望力矩 v,它不关心这些力矩由哪几个舵面产生。故障发生时,飞行状态会变化,外环控制器会自然修正期望力矩;分配器只需要确保"给定的 v 尽量被剩余舵面实现"。这种结构在工程上是极大的解脱——控制器参数不需要随故障切换,重构逻辑只需要在 B 和权重上做文章,调试边界清晰。
有一个前提条件:外环控制器必须有余量,也就是故障后剩余舵面还能提供足够的控制力矩。如果卡死的是一个关键舵面且剩余舵面全部饱和,重构逻辑能做的只是"优雅地降级",不可能无中生有。所以在仿真里验证容错方案时,第一件事就是检查故障后 B_est 的秩和舵面裕度,别把容错控制当成无限推力。
5. 固定翼分配仿真的踩坑清单:五条血泪经验
5.1 效率矩阵当成常数:换飞行点直接翻车
现象:在配平点 A 调好的分配器,切到高速或大攻角状态后,力矩指令虽然没变,但飞机侧滑越来越大。
原因:B 矩阵的数值本质上是气动导数乘以动压,动压随空速变化非常明显。把 B 当成常矩阵,等于假设飞行状态永远贴着配平点走,大机动时分配结果和真实力矩严重不符。
解决:把 B 拆成"常数系数矩阵"和"动压标量"两部分。线性化得到的系数先按动压归一化,使用分配器时乘以当前动压,或者直接做成 B(q) = q * B_coeff,按马赫数/攻角查表插值。注意故障重构时也要用当前的 B 估算,不能用配平点 B 去补偿卡死力矩。
5.2 重构权重硬切换:舵面抖到怀疑人生
现象:故障注入后,分配权重瞬间从 0 跳到 10,健康舵面的偏角也瞬间跳变,仿真里舵机速率限制被顶满,机身出现高频抖动。
原因:权重硬切换相当于给分配器输入了一个阶跃,所有健康舵面同时重新分配负载,指令出现跳变。
解决:给重构权重加一个时间斜坡,常见做法是把切换过程拉长到 0.1~0.2 秒,让健康舵面逐步接管。实现上可以在 Simulink 里用斜坡发生器或者一阶惯性环节处理 Wu_new,也可以直接在重构函数里做一个权重插值。这是我调容错逻辑时花时间最多的地方,斜坡时间太短依旧抖,太长则故障后的瞬态掉高度控制不住。
5.3 pinv 写进连续子系统:代数环玄学
现象:同一个 QP 分配器单独测试正常,放进 Simulink 连续模型后报代数环错误,或者仿真步长被压得极慢。
原因:把 B 的伪逆或 quadprog 求解直接写在连续子系统的前向通路里,分配器的输出又反馈到同一个子系统状态量上,形成了瞬时依赖环。
解决:让分配器在离散采样周期内运行。最朴素的做法是把整个分配子系统设为离散模块,Ts 取 10~20 ms;或者在一个连续子系统内为 u_prev 加一个 Memory 块,把速率约束里的上一时刻位置真正存下来。固定翼飞行控制本来就是数字飞控,分配器按离散周期跑完全符合实际。
5.4 零空间被浪费:副翼和方向舵互相顶牛
现象:分配结果健康,力矩指令也算对了,但左右副翼持续差动、方向舵偏到极限,舵面利用率极高,一遇到饱和就控制不住。
原因:伪逆法求的是最小范数解,不是最小舵面消耗解。当偏航力矩可以由副翼差动和方向舵共同产生时,解不唯一,最小范数解会把指令稀释到所有幸存的舵面上。
解决:在 QP 目标函数里加 u_ref 偏好项,或者把 Wu 设成非均匀权重。对固定翼,我习惯把方向舵权重设高、副翼权重设低,迫使分配器优先用副翼做滚转、方向舵只在必要时出偏航力矩。这样既节省方向舵行程,也降低了操纵耦合带来的风险。
5.5 故障检测延迟:飞机已经在掉高度了
现象:故障注入 2 秒后重构逻辑才动作,期间飞机高度损失了 50 米,最后虽然恢复姿态,但已经触发了最低安全高度逻辑。
原因:FDD 的确认时间太长。检测阈值设得紧容易误报,设得松则故障确认慢。工程上经常默认"连续超差 100 ms 就确认",但真机里舵机偶尔会有瞬时超差,这个逻辑不能照搬。
解决:把故障确认时间分成两级——第一级是"疑似",只抬高分配器对对应舵面的权重,不改变 B_est;第二级是"确认",才做效率矩阵清零和卡死补偿。疑似阶段不剥夺舵面控制权,可以用很小的代价换取响应速度。故障注入验证时,把确认时间从 50 ms 到 500 ms 都扫一遍,看你最关心的高度损失指标落在哪个区间。
6. 闭环验证的进阶做法:故障注入矩阵与分配器带宽
6.1 用故障注入脚本把故障组合跑一遍
容错控制最容易出现的问题是在一个故障点调好了,换一个故障点就崩。所以我习惯在仿真验证阶段写一个故障注入脚本,把故障类型、故障舵面、发生时间、卡死幅值排列组合,批量跑蒙特卡洛仿真。故障注入矩阵大致是:
| 故障舵面 | 故障类型 | 注入时刻 | 卡死角度 |
|---|---|---|---|
| 左副翼 | 卡死 | 5 s | +10 deg |
| 右副翼 | LoE 50% | 8 s | - |
| 方向舵 | 卡死 | 6 s | -5 deg |
| 升降舵 | 松浮 | 10 s | - |
每个组合下记录三组指标:姿态角峰值误差、高度损失、健康舵面的饱和时间占比。饱和时间占比超过 30% 的组合,说明剩余控制裕度不足,重构逻辑再完美也救不回来,这类结果要在报告里单独标出。
6.2 分配器带宽:和舵机带宽留一个十倍间隔
分配器本质上是一个带约束的静态优化器,但仿真里它跑在离散采样步上,也存在"带宽"问题。我一般把分配器的采样率设为舵机带宽的十倍以上。如果舵机模型的时间常数是 5 rad/s,分配器至少跑到 50 Hz;搭配 100 Hz 的控制器更新率更稳妥。QP 分配器在每次采样里都要调用 quadprog,求解耗时是主要瓶颈,所以在验证阶段我会先在脚本里批量跑一遍单步求解,统计最坏耗时,确认不超过采样周期的 20%,再放进仿真回路。这个检查能避免后期发现"故障瞬间分配器算不完一场仿真"的尴尬。
我自己踩过的一次教训:先调外环控制器、后调分配器重构逻辑,结果飞机每次在故障后都先剧烈抖动,我一直以为是控制器参数问题,反复调大调了小半个月,换成先固定控制器、只改分配权重和补偿项之后,两个星期就收敛了。控制分配和容错逻辑是一体的,但调试顺序千万别反。分配器是把虚拟力矩落成舵面偏角的那一道闸门,固定翼容错方案的成败,七成在故障模型和 B 矩阵估计上,剩下的三成才知道控制器有多能扛。希望帮到你。
本文还有配套的精品资源,点击获取