简介:这是一份MATLAB的H∞控制器仿真源码,面向控制理论与工程方向的在校学生、科研人员及工程师,解决鲁棒控制器设计落地难、缺少可运行样例的问题。H∞控制以最小化系统对不确定性及扰动的敏感性为目标,利用状态空间模型结合LMI方法完成控制器求解,源码通过单个M脚本呈现完整主流程,包括被控对象建模、控制器设计函数调用与仿真设置,紧凑且便于直接运行调试。压缩包共1个文件,类型为.m脚本,包体仅749B,将H∞控制器设计的关键环节浓缩在一个文件内,省去复杂工程配置,适合快速理解核心逻辑。该资源已有286人学习浏览,具备一定参考热度。通过学习这份源码,能上手hinfstruct或hinfsyn函数的实际调用方式,掌握从系统模型定义到仿真输出的基本链路,也可作为后续Simulink联合仿真或课程实验的起步模板,对入门鲁棒控制实践很有帮助。
1. 拿到 H∞ 控制器仿真源码,先想清楚这四件事
一个典型场景:你负责的伺服台架在低频段受阶跃扰动后要快速回零,但系统里有未建模的柔性模态和参数漂移。PID 调了三版,幅值裕度够的时候相位裕度不够,超调压下去了抗扰能力又没了。此时“基于 MATLAB 的 H∞ 控制器仿真源码”这类标题频繁出现在搜索结果里,说明需求很集中——不是要用 H∞ 证明什么理论,而是要尽快跑通一条从被控对象模型到控制器验证的完整链路。H∞ 控制器的意义在于把设计目标收敛成一个优化问题:找到控制器 K,使闭环传递函数从扰动 w 到性能输出 z 的 H∞ 范数最小,同时满足稳定性和可实现性约束。这比手工调增益要符合工程直觉,因为你只需要把“哪里要硬、哪里要软”翻译成权重函数。本文面向有控制理论基础、但没完整走完一遍 H∞ 设计流程的工程师,用 MATLAB 的 Control System Toolbox(尤其是 hinfsyn 和 augw)作为主线,穿插仿真发散排查、权重函数调整和模型降阶等实战问题。全文不依赖任何商业工具箱之外的功能,代码在 R2018b 及以上版本均可运行。
2. 增广被控对象模型:H∞ 仿真的地基是权重函数和 augw 拼装
2.1 先搭一个能跑的被控对象:双质量弹簧阻尼系统
H∞ 控制器设计的第一步不是写控制器,而是把被控对象整理成标准形式:
z 是性能输出,w 是外部扰动,u 是控制输入,y 是量测输出。
我在工程里最常用的测试模型是一个双质量弹簧阻尼系统,用它可以复现大多数伺服机构在柔性传动下的动态特性。假设质量 m1 和 m2 通过刚度 k 和阻尼 c 耦合,控制力 F 作用于 m1,扰动 d 作用于 m2,量测输出是 m2 的位移和速度,性能输出是 m2 的位移和 m1 的控制力。
% plant_setup.m % 双质量弹簧阻尼系统:柔性传动下的典型被控对象 m1 = 2.5; % kg,驱动侧质量 m2 = 1.2; % kg,负载侧质量 k = 800; % N/m,连接刚度 c = 3; % N.s/m,阻尼系数 % 状态变量:x1, v1, x2, v2 A = [0 1 0 0; -k/m1 -c/m1 k/m1 c/m1; 0 0 0 1; k/m2 c/m2 -k/m2 -c/m2]; B1 = [0; 0; 0; 1/m2]; % 扰动 d 作用在负载侧 B2 = [0; 1/m1; 0; 0]; % 控制力 F 作用在驱动侧 % 量测输出:负载位移和负载速度 C2 = [1 0 0 0; 0 0 1 0]; D21 = [0; 0]; D22 = [0; 0]; % 性能输出:负载位移 z1、控制力 u 的加权 z2 C1 = [1 0 0 0; 0 0 0 0]; D11 = [0; 0]; D12 = [0; 1/m1]; % 控制力直接进入性能输出 Pnom = ss(A, [B1 B2], [C1; C2], [D11 D12; D21 D22]); Pnom.StateName = {'x1','v1','x2','v2'};B1是扰动到状态量的输入通道,B2是控制通道,这两个通道在增广后必须严格区分,因为权重函数只对指定的通道生效。C2是量测输出,代表控制器真实拿得到的信息。C1是性能输出,它决定了你的优化目标优先级——这里我希望负载位移尽量小,所以把x2放进C1。D12不为零是因为控制力本身也是性能指标的一部分,如果不加入这个项,求解器会因为控制量无约束而给出非常大的控制器增益。
提示:
ss组装时,B矩阵按列拼接的规则是[B1 B2],D矩阵按四象限拼接[D11 D12; D21 D22]。行列数一旦对不上,后面augw和hinfsyn的输入输出维度校验就会报错。
2.2 权重函数怎么选:性能权重 Wp 和控制权重 Wu 的配合
现在进入 H∞ 仿真的核心环节——增广被控对象。常见做法是用augw完成输入权重、性能权重和控制权重的拼接。augw需要的权重对象有三种类型:Wp是性能权重,通常是一个高通或低通滤波器,决定干扰抑制的频带;Wu是控制权重,限制控制量的大小;Wn是噪声权重,如果不考虑传感器噪声可以用很窄带的低通代替,避免求解器在极端频段做无意义的优化。
性能权重Wp的设计准则是:在需要抑制扰动的频段取高增益,其他频段取低增益。最常见的一阶形式是:
% weight_design.m % 性能权重:低频高增益,抑制 10 rad/s 以下的扰动 wp_bw = 10; % 期望的扰动抑制带宽 wp_eps = 0.01; % 允许的稳态误差比例 Wp = tf([1/wp_bw + wp_eps], [1/wp_bw wp_eps]); Wp = ss(Wp); % 控制权重:限制控制量带宽,避免高频颤振 Wu = tf(0.01 * [1 20], [1 200]); Wu = ss(Wu); % 噪声权重:传感器噪声在 100 rad/s 以上按白噪声处理 Wn = ss(tf(0.001));Wp的低频增益是1/wp_eps,加入后闭环系统在扰动频段的灵敏度函数被压到wp_eps以下,对应稳态扰动抑制比约 1%。wp_bw对应开环穿越频率的上界,它和系统的右半平面零点或延迟时间有直接关系,设得过高会让求解器找不到可行解。很多场景下你不需要手动调出完美的 Wp,只要跑通闭环后观察灵敏度函数再作修正即可,这一点后面仿真发散排查一节会细讲。
Wu的作用是过滤控制量指令里的高频成分。如果系统里存在未建模的高频模态,而你没有加Wu,求解出来的控制器会在高频处产生很大的控制速率,仿真时表现为控制力震荡或发散。
2.3 用 augw 拼装标准型,维度信息是仿真排错的第一现场
augw的调用形式是Paug = augw(Pnom, Wp, Wu, Wn),其中性能通道、控制通道、噪声通道按顺序对应Pnom的第一个输入(扰动)、第二个输入(控制)和第一个输出(性能)、第二个输出(量测)来拼装。如果你希望同时压抑制两个不同频段的扰动,可以自行手动拼装P,不再用augw的简写形式,而是把两个权重对角放入广义被控对象。
% aug_plant.m Paug = augw(Pnom, Wp, Wu, Wn); % 查看增广后的 I/O 名称,确认通道顺序 Paug.InputName拼装完成后用size(Paug)检查:输入个数是 3(扰动 w、控制 u、噪声 wn),输出个数是 3(性能 z、量测 y)。记住这个数字,因为后面hinfsyn里必须显式指定ny(量测输出个数)和nu(控制输入个数),这两个参数错了,求解结果就是乱的。
常见的坑是直接用Pnom的输入输出个数去套augw后的矩阵,导致后续systf或lft互联时维度不匹配,报错信息又长又难读。我的习惯是拼装后立刻执行:
[P_, nw, nu_, nz, ny_] = ltiblock.Utilities.checkSignals(Paug);不需要手动记忆这些变量,但要在工作区里盯着Paug的状态数和 I/O 个数。状态数超过 20 时,后面求解会变慢甚至出现数值坏条件,此时考虑对Paug做平衡截断降阶,这个技巧在第 5 章展开。
3. 用 hinfsyn 求解 H∞ 控制器并搭闭环仿真:参数、γ 迭代与验证
3.1 最小可复现命令:从增广模型到控制器只差 3 行
增广模型就绪后,标准解法是用hinfsyn直接求解 Riccati 方程。这里我遇到过最多的问题不是控制器设计本身,而是调用参数遗忘:hinfsyn的输入是(P, ny, nu),其中P必须是增广后的广义被控对象,ny是量测输出个数(C2的行数),nu是控制输入个数(B2的列数)。这是最容易写错的地方。
% hinfsyn_solve.m ny = 2; % 量测输出:负载位移、速度 nu = 1; % 控制输入:力 F gam_range = [0.01, 20]; % γ 搜索范围 [K, CL, ginfo] = hinfsyn(Paug, ny, nu, gam_range(1), gam_range(2), 0.001);逻辑说明:hinfsyn做的是带 γ 迭代的 Riccati 求解。它先检查 γ 区间内是否存在可行的控制器,然后按tol=0.001做二分搜索,找到使闭环 H∞ 范数最小的 γ 值。K返回的是控制器对象(状态空间形式),CL是闭环传递函数矩阵,ginfo里包含 γ 值上下界和最终的 γ 达到值。求解完成后,务必查看ginfo.gfin,它表示实际达到的性能水平,通常略大于理论上界。
γ 迭代的物理含义是:γ 越小,闭环系统对扰动的抑制能力越强,但控制器增益和带宽会越高。当hinfsyn报告不可行时,优先检查gam_range(1)是否过小导致 Riccati 解不存在。此时可以把gam_range改成[0.1, 50]重试。
3.2 闭环搭建与频域验证:用灵敏度函数和补灵敏度函数定位性能边界
求解完成后,不能直接投入仿真验证。我的习惯是先搭一个干净的开环互联做频域分析,再进时域仿真。控制器K和被控对象Pnom互联时存在正负反馈习惯问题,MATLAB 的feedback默认负反馈,但hinfsyn返回的K是在正反馈约定下求解的,所以互联时需要加负号。
% close_loop_check.m % 注意:hinfsyn 的控制器用在负反馈回路时要串一个负号 K_neg = -K; CL_s = feedback(Pnom * K_neg, eye(ny)); % 灵敏度函数 S = 1/(1+PK) S = 1 / (1 + Pnom * K_neg); S = minreal(S); % 消除零极点相消 % 画出灵敏度函数的奇异值 sigma(S, logspace(-2, 3, 500)); grid on; xlabel('Frequency (rad/s)'); ylabel('Singular Values (dB)'); title('Sensitivity Function S(s)');逻辑说明:灵敏度函数S的低频增益直接对应扰动抑制能力。如果仿真发散或稳态误差偏大,第一步是画S看低频段是否低于 0 dB——如果低频段大于 0 dB,说明扰动不但没被抑制反而被放大,问题出在权重或控制器极性上。minreal在这里不能省略,因为hinfsyn产生的控制器会和被控对象发生零极点相消,保留相消对后续采样仿真会留下数值隐患。
3.3 时域仿真验证:阶跃扰动和控制量饱和需要分别检查
频域验证通过后,进时域仿真。最直接的验证方式是取一组有代表性的扰动信号,用lsim看闭环响应。这里要注意控制器是连续时间状态空间,仿真步长要足够小以避免离散化误差。控制量饱和是另一个重点:H∞ 控制器在理论上不感知饱和,但真实系统一定存在饱和,所以要在仿真里单独抽出控制量通道看是否存在幅值越界。
% time_domain_sim.m t = 0:0.001:5; % 仿真时长 5s,采样步长 1ms d = ones(size(t)); % 常值扰动 % 闭环系统对扰动的响应:从第二个输入(扰动)到第一个输出(负载位移) sys_d2x1 = CL_s(1, 2); [y, tsim, x] = lsim(sys_d2x1, d, t); % 控制量通道:需要重新搭一个从扰动到控制输出的传递 % 控制器输出 u = K * y,其中 y 是闭环量测输出 sys_d2u = -K_neg * (Pnom(2,:) * (1 / (1 + Pnom * K_neg))); [uc, ~] = lsim(sys_d2u, d, t); figure; subplot(2,1,1); plot(tsim, y, 'LineWidth', 1.2); ylabel('负载位移 (m)'); grid on; subplot(2,1,2); plot(tsim, uc, 'LineWidth', 1.2); ylabel('控制力 (N)'); grid on;这里sys_d2u的推导略绕,但常用做法不是手工算这个传递函数,而是直接用K_neg和Pnom的闭环互联函数取出所有通道:
CL_all = lft(Paug, K);lft直接给出闭环广义系统,所有通道的 I/O 关系一目了然。推荐用这种方式,少折腾符号推导。
| 验证项 | 方法 | 通过标准 |
|---|---|---|
| H∞ 范数 | norm(CL_all, Inf) | 小于等于设定的 γ |
| 低频扰动抑制 | sigma(S, w)取低频值 | 低于 -20 dB |
| 高频补灵敏度 | sigma(T, w)取高频值 | 低于 6 dB |
| 控制量峰值 | lsim提取控制通道 | 低于执行器上限 |
4. 仿真发散排查:从 γ 值到模型数值坏条件的 4 个检查点
4.1 双质量模型里的"隐形发散源":代数环和状态数失控
lsim或sim跑着跑着结果炸掉,这是 H∞ 仿真里最磨人的问题。很多人第一反应是控制器参数不对,但以我的经验,发散多发生在以下 4 个环节。
发散源之一:hinfsyn返回的闭环模型里存在未消除的数值坏条件状态。增广模型中权重的极点如果特别靠近虚轴,比如Wp的极点设在1e-7这个量级,Riccati 解会给出极大的内部状态,离散化后一个步长就溢出。解决方法是把权重极点的实部设到1e-3以上。
发散源之二:控制器K和Pnom互联时正负号错导致正反馈。hinfsyn默认正反馈约定,直接用feedback(Pnom, K)会得到正反馈回路,稳定裕度立刻崩掉。这类发散的特征是仿真前几步就有巨大超调,且与仿真步长无关。
发散源之三:量测输出y包含直接馈通项(D22非零),而控制器内部也有直接馈通,构成代数环。大多数实际系统D22为零,如果你搭建的ss模型中不小心让D22出现非零小值,闭环仿真时会遇到隐式代数环求解失败,lsim直接报错,simulink则在代数环求解器上卡死。
发散源之四:γ 搜索区间设置不当。gam_range(1)设得过低会让hinfsyn在每次迭代时都解不出 Riccati 方程,最终返回的控制器是边缘状态,仿真时一步就发散。这种情况ginfo.gfin会等于或非常接近gam_range(1),这是一个明确的诊断信号。
4.2 三个必查指标:返回 γ 值、灵敏度峰值、控制带宽
| 排查项 | 诊断方法 | 典型问题 |
|---|---|---|
| γ 是否贴近区间边界 | ginfo.gfinvsgam_range(1) | 贴近下界说明性能被压到极限,需要放宽 |
| 灵敏度峰值 | norm(S, Inf) | 大于 2(6dB)时系统对参数摄动很敏感 |
| 控制器带宽 | bandwidth(K) | 大于被控对象右半平面零点的 2 倍时仿真易发散 |
排查顺序建议从控制器带宽入手。H∞ 求解器会把控制器带宽推向权重允许的上限,如果控制器带宽是系统刚性的 5 倍以上,仿真里稍微有一点数值噪声就会激励出高频振荡,随后迅速发散。这种情况通常在Wp或Wu上强行压低高频增益即可缓解。
4.3 实用招术:对权重和模型做平衡截断后再跑仿真
另一个频繁使用的技巧是平衡截断。augw拼装后系统的状态数是Pnom加上两个权重滤波器加总,很容易到 20~30 阶。除非你刻意做全阶 H∞,否则这个阶数对仿真验证没有任何帮助,反而引入数值风险。我的习惯是求解前先对Pnom做balred降阶到 6 阶以内,求解后再对控制器做一次模型降阶,最后做闭环验证。
% model_reduce.m % 对被控对象和控制器的平衡截断降阶 rP = balred(Pnom, 6); % 保留前 6 阶主导模态 % 重新增广并求解 Paug_r = augw(rP, Wp, Wu, Wn); [Kr, CLr, ginfor] = hinfsyn(Paug_r, ny, nu, 0.01, 20, 0.001); % 比较降阶前后闭环响应 CL_orig = lft(Paug, K); CL_reduced = lft(Paug_r, Kr);降阶后仿真发散的风险显著降低。但要注意balred只能处理稳定模型,如果你的Pnom本身含积分环节(如电机轴系),先在Pnom外部加一个小的阻尼项或拆出积分模式再降阶。
提示:不要对含纯积分器的模型直接调用
balred,它会因不稳定模式拒绝执行。常见做法是把积分模式剥离开,只降阶有阻尼的动态部分,仿真时再串回积分器。
5. 参数化 H∞ 控制器与工业级仿真验证的衔接技巧
5.1 用 hinfstruct 替换 hinfsyn:把固定结构控制器约束进仿真
如果你拿到的不是简单的全阶问题,而是要求控制器必须是二阶或三阶(比如嵌入现有 PLC 或运动控制卡),hinfsyn返回的全阶状态空间控制器就没法直接用。此时要用hinfstruct,它能对固定结构控制器调参,但需要把被控对象包装成genss。
% hinfstruct_example.m % 固定结构控制器:PI + 一阶低通 K0 = tunableSS('K', 3, 1, 2); % 3 阶控制器,1 输入 2 量测 % 闭环系统组装 CL = lft(Paug, K0); % 设定性能约束:H∞ 范数小于 2 opt = hinfstructOptions('Display', 'final', 'RandomStart', 5); [Kt, fval] = hinfstruct(CL, opt);hinfstruct的输出不是状态空间对象,而是genss块里的可调参数值——这里直接Kt就是调整后的控制器。它在仿真里的价值在于,你可以直接把它接到simulink的LTI System块里跑硬件在环仿真,控制器的阶数不会让实时目标机的计算负担失控。配合hinfstructOptions的RandomStart,可以从多组初值里选择收敛结果最好的那个。
5.2 仿真验证的收尾技巧:把离散化时机从"控制器求解后"推迟到"验证通过后"
最后分享一个很实用但容易被忽视的做法。很多人习惯在初始阶段就把控制器离散化,然后用离散控制器的仿真结果来验证性能。但离散化引入的相位滞后应该在验证闭环后的时域响应后再做,而不是提前叠加。原因是当你看到响应振荡或发散时,你得在连续时间层面判断是"H∞ 设计本身的问题"还是"离散化引起的相位裕度损失"。分开验证可以省去一多半排查时间。
% discretize_last.m Ts = 0.002; % 控制周期 2ms,对应 Nyquist 频率约 1570 rad/s Kd = c2d(K_neg, Ts, 'tustin'); % 双线性变换,保持频率响应形状 % 与连续控制器对比:检查离散化后的灵敏度函数是否仍然满足要求 Sd = 1 / (1 + c2d(Pnom, Ts, 'zoh') * Kd); sigma(S, Sd, logspace(-2, 3, 500)); legend('连续', '离散');这里'tustin'适合 H∞ 控制器,因为它能保持连续域的频率响应畸变最小。检查离散和连续的灵敏度曲线,如果两者在交叉频率附近有明显偏差,说明你的采样周期相对控制器带宽过高——调整办法是减小Ts或重新设计Wu以降低控制器带宽。等两条曲线贴合良好,再接入真实的 Simulink 离散仿真或半实物平台,后续的模型验证工作就可以完全基于这个离散控制器展开了。
本文还有配套的精品资源,点击获取