简介:这是一份基于MATLAB的双容水箱模糊控制仿真资源,面向自动化、控制工程等专业的学生及需要实现液位控制的工程技术人员,可用于课程设计、毕业设计或技术预研。资源以两个相互串联的水箱为被控对象,围绕水位恒定控制目标,完整展示了模糊控制从模糊集合划分、偏差与偏差变化率输入、模糊规则推理到去模糊化输出的设计流程,并体现了模糊逻辑处理供水波动和测量扰动的能力。压缩包共6个文件,包括2个.m源码脚本、1个.slx仿真模型、1个.fis模糊推理系统文件以及2份docx报告文档,体积仅1.02MB,便于快速获取与运行。已有2088人学习下载,颇受相关领域学习者关注。通过代码和文档可掌握Fuzzy Logic Toolbox的实际用法,理解模糊规则库建立思路,并能在此框架上修改参数、扩展为自适应模糊PID等更高级策略。
1. 双容水箱模糊控制:为什么PID在这里不够用
双容水箱是过程控制里最典型的被控对象之一:两个水箱垂直串联,前箱的出水直接进入后箱,控制目标是借助调节阀开度让后箱液位稳定在设定值。它的数学模型看似简单,但实际运行中的容量滞后、进水扰动和阀门非线性都会让常规PID参数只在一个工作点附近有效。模糊控制不依赖精确传递函数,而是把熟练操作工的经验归纳成“偏差大、变化快就加大阀门”这类规则,在MATLAB里只需一个.fis文件加一个Simulink模型就能完整复现。这套资源中的Fuzzy_Water_Tank.m、tank_s1.slx和FUZZ1.fis正好覆盖从建模到仿真的全流程,适合做课程设计、毕业设计,也适合刚接触模糊逻辑的工程师快速建立闭环控制直觉。
2. 双容水箱机理建模与tank_s1.slx开环仿真结构
2.1 双容水箱的机理方程与参数选取
双容水箱可以看作两个一阶惯性环节的串联。设两个水箱的横截面积分别为 A1、A2,底部出水孔面积为 a1、a2,液位为 x1、x2,调节阀开度为 u。根据托里切利定律,流出流量与液位平方根成正比,状态方程写成:
dx1/dt = (ku - a1sqrt(2gx1)) / A1
dx2/dt = (a1sqrt(2gx1) - a2sqrt(2gx2)) / A2
其中 k 是阀门增益,g 是重力加速度。第一个方程描述前箱液位受进水阀和自流出的影响;第二个方程中前箱流出流量成为后箱输入,后箱出水 Q2=a2sqrt(2g*x2) 作为负载。可以看到,x2 对 u 的响应要经历两级惯性,这就是容量滞后,也是单纯用PID整定时容易把比例增益调得过大、引发振荡的原因。
实际建模仿真中参数需要统一用SI单位,否则仿真结果没有物理意义。下表是一组我在开环测试中常用的参数,来自典型教学水箱的换算值。
| 参数 | 含义 | 数值 | 单位 |
|---|---|---|---|
| A1 | 水箱1截面积 | 0.5 | m^2 |
| A2 | 水箱2截面积 | 0.5 | m^2 |
| a1 | 水箱1出水孔面积 | 0.045 | m^2 |
| a2 | 水箱2出水孔面积 | 0.045 | m^2 |
| g | 重力加速度 | 9.81 | m/s^2 |
| k | 调节阀增益 | 1.0 | (m^3/s)/% |
| x1_0 | 前箱初始液位 | 0.10 | m |
| x2_0 | 后箱初始液位 | 0.10 | m |
选参数的逻辑是:两个水箱截面积相近,保证均衡状态下后箱液位跟随前箱;出水孔面积取 0.045 平方米,对应空箱放水时间常数在几十秒到百秒级别,和实际工业水箱响应习惯接近。
2.2 用Simulink搭建开环模型:tank_s1.slx的结构
tank_s1.slx 通常并不是用纯积分器和乘法器逐个搭方程,而是用两个 Integration 模块配合 Fen 或 S-Function 实现非线性微分方程组。推荐的做法是写一个 MATLAB 函数文件作为 S-Function 回调,然后在 Simulink 中用 S-Function 模块引用它。这样做的好处是:日后切换控制算法时不需要改模型结构。
在模型工作区或脚本中先初始化参数,这一步我一般放在独立的 m 文件中,方便改参数:
% tank_params.m - 双容水箱仿真参数初始化 % 供 tank_s1.slx 从 MATLAB 工作区读取 A1 = 0.5; % 水箱1横截面积,m^2 A2 = 0.5; % 水箱2横截面积,m^2 a1 = 0.045; % 水箱1出水孔面积,m^2 a2 = 0.045; % 水箱2出水孔面积,m^2 g = 9.81; % 重力加速度,m/s^2 k = 1.0; % 调节阀增益,(m^3/s)/% % 初始液位 x1_0 = 0.10; x2_0 = 0.10;这里所有变量都会写到 MATLAB 的基本工作区。模型里的常数模块和积分器初值可以使用工作区变量名,比如 Integrator1 的 InitialCondition 写成 x1_0,比直接填数字安全得多。S-Function 的第二个输出是后箱液位,也就是被控量 x2。
如果选择 S-Function 方式,回调函数可以写成下面的形式:
% tank_sfun.m - 双容水箱S-Function % 输入u(1)为阀位开度指令,输出为[x1; x2] function [sys,x0] = tank_sfun(t,x,u,flag,A1,A2,a1,a2,k,g) switch flag case 0 sys = [2;0;2;0;0;0]; % 2个连续状态,2个输出 case 1 sys(1) = (k*u(1) - a1*sqrt(2*g*x(1)))/A1; sys(2) = (a1*sqrt(2*g*x(1)) - a2*sqrt(2*g*x(2)))/A2; case 3 sys = x; case {2,4,9} sys = []; otherwise error('unhandled flag'); end x0 = [0.1; 0.1];flag=0 返回系统维度参数,flag=1 返回状态导数,flag=3 返回输出,这是标准 S-Function 的骨架。Simulink 模型里的 S-Function 模块只要填函数名 tank_sfun,并把 A1、A2、a1、a2、k、g 作为参数传入,就能复用同一个模型文件跑不同工况。如果想快速验证开环响应,也可以直接在模型里用两个 Integrator 和 Fen 模块搭,缺点是不够直观,控制器改型时容易误连线。
2.3 开环阶跃响应:为什么要上模糊控制
在 Simulink 中给阀位加一个 50% 的阶跃,用 scope 观察 x2。可以看到 x2 不是立刻上升,而是先缓慢加速,再逐渐逼近稳态,整个过程表现为 S 形曲线。用一阶惯性加纯滞后模型近似,滞后时间大约占整个响应时间的 20%~30%。这种纯滞后加上参数随液位变化,是常规 PID 整定最头疼的地方:整定出的比例增益只能在某个工况附近工作,工况一变就容易出现持续振荡或响应太慢。
模糊控制的价值就在这里。它把误差 E 和误差变化率 EC 作为输入,直接映射到阀门开度,本质上是一个变增益的非线性控制器。在大偏差时等效比例增益很大,小偏差时自动降低增益,从而避免超调和稳态振荡。这就是为什么很多过程控制课程把双容水箱作为模糊控制的入门案例。
3. FUZZ1.fis 解析:模糊集、规则表与去模糊化设计
3.1 输入输出变量定义与论域选择
FUZZ1.fis 是一个 Mamdani 型模糊推理系统,输入变量通常选水位偏差 E 和偏差变化率 EC,输出变量为控制阀开度 U。E 的论域范围一般取实际允许偏差的上下限,比如[-0.5, 0.5] 米;EC 的论域需要根据仿真步长和液位变化速率确定,比如[-0.02, 0.02] 米/秒;U 的论域对应阀门开度,取[-1, 1] 或[0, 1],最后再乘以阀门增益 k。
之所以选 E 和 EC 而不是直接用液位值,是因为反馈控制本质上关心的是当前偏离了多少、偏离趋势是什么。如果 EC 为正且很大,说明液位正在快速上升,即使当前偏差不大,也需要提前减小阀门,这就是“预测”作用的来源。模糊控制器通过规则库把这个推理逻辑显式表达出来,规则可以写得和人的操作习惯一一对应。
在 MATLAB 中可以用 readfis 读取文件并检查三个变量的范围:
fis = readfis('FUZZ1.fis'); disp(fis.Type); disp(fis.AndMethod); disp(fis.ImplicationMethod); % 查看输入输出变量定义 fis.Inputs(1).Name, fis.Inputs(1).Range fis.Inputs(2).Name, fis.Inputs(2).Range fis.Outputs(1).Name, fis.Outputs(1).Rangefis.Type返回 'mamdani',说明推理方法是最大最小合成;AndMethod是 'min',对应规则的与运算;ImplicationMethod为 'min' 表示蕴含运算。Range返回每个变量的论域,比如[-0.5 0.5]。如果readfis报错,优先检查当前目录下是否存在 FUZZ1.fis,以及 MATLAB 是否安装了 Fuzzy Logic Toolbox;没有工具箱时readfis无法运行。
3.2 隶属度函数设计与 FIS 文件生成
每个变量定义 7 个模糊子集:NB、NM、NS、ZO、PS、PM、PB,覆盖负大到正大。E 的隶属度函数常用高斯型或三角型,因为高斯型在边界更平滑,三角型更容易在调参时观察重叠。下面是用命令方式创建一个简化 FIS 的片段,实际 FUZZ1.fis 的结构与此类似:
fis = newfis('FUZZ1', 'mamdani', 'min', 'max', 'centroid'); % 输入变量 E,论域 [-0.5 0.5] fis = addvar(fis, 'input', 'E', [-0.5 0.5]); % 添加 NB 隶属度函数,梯形 fis = addmf(fis, 'input', 1, 'NB', 'trapmf', [-0.5 -0.5 -0.35 -0.15]); % 添加 ZO 隶属度函数,三角形 fis = addmf(fis, 'input', 1, 'ZO', 'trimf', [-0.15 0 0.15]); % 添加 PB 隶属度函数,梯形 fis = addmf(fis, 'input', 1, 'PB', 'trapmf', [0.15 0.35 0.5 0.5]);addvar的第一个参数是 fis 对象,第二个参数指定'input'或'output',第三、四个参数分别是变量名和范围。addmf中第三个参数是隶属度函数编号,第四个参数是名称,第五个是关键:不同函数类型的参数含义不同。trapmf需要四个断点,trimf需要三个断点,高斯型gaussmf需要均值和标准差。调试时可以用plotmf(fis,'input',1)把第 1 个输入变量的所有隶属度函数画出来,观察相邻 MF 的重叠程度。
3.3 模糊规则库与推理/去模糊化
规则表是模糊控制器的核心。常见规则可以写成二维表,行是当前偏差 E,列是偏差变化率 EC,输出是阀门开度 U。比如:
| E\EC | NB | NM | NS | ZO | PS | PM | PB |
|---|---|---|---|---|---|---|---|
| NB | PB | PB | PM | PM | PS | PS | ZO |
| NM | PB | PM | PM | PS | PS | ZO | NS |
| NS | PM | PM | PS | PS | ZO | NS | NM |
| ZO | PM | PS | PS | ZO | NS | NS | NM |
| PS | PS | PS | ZO | NS | NS | NM | NM |
| PM | PS | ZO | NS | NS | NM | NM | NB |
| PB | ZO | NS | NM | NM | NB | NB | NB |
规则含义是:当偏差为负大且偏差变化为负大时,说明水位远低于目标且在继续下降,阀门应开最大;当偏差为正大且变化率为正时,水位会继续冲高,阀门应快速关小。对角线两侧规则对称,这是模糊控制器稳定性的基本要求。
在 Fuzzy Logic Toolbox 中,规则用文本或数字矩阵存储。读取已建好的 FUZZ1.fis 并查看规则:
showrule(fis, [1 2], 'symbolic'); gensurf(fis);showrule以符号形式打印指定规则,例如If (E is NB) and (EC is NB) then (U is PB) (1)。gensurf生成控制曲面,X 轴是 E,Y 轴是 EC,Z 轴是 U。如果控制曲面有明显台阶或鞍部,说明规则表有冲突,或者隶属度函数重叠不足,需要回到规则库检查。
去模糊化采用 centroid(重心法),输出是隶属度函数覆盖区域的重心坐标,计算连续且光滑,不会出现最大隶属度法那样的跳变。重心法虽然计算量大,但对现代 CPU 可以忽略,所以在 MATLAB 仿真中基本都是用 centroid。
4. Fuzzy_Water_Tank.m 与 FuzzyPID.m:仿真脚本与模糊PID对比
4.1 Fuzzy_Water_Tank.m 主程序流程
Fuzzy_Water_Tank.m 的核心作用是:把 FUZZ1.fis 加载进工作区,设置仿真参数,然后驱动 Simulink 模型运行。写好的脚本可以在批处理实验中反复调用,省去每次手动点击 Start 的麻烦。
% Fuzzy_Water_Tank.m % 双容水箱模糊控制主仿真脚本 clear; clc; % 1. 加载参数和模糊控制器 tank_params; % 调用第2章的初始化脚本 fis = readfis('FUZZ1.fis'); % 读取FIS文件 assignin('base', 'fis', fis); % 2. 设置仿真时长和液位目标 t_end = 300; % 仿真时长,秒 setpoint = 0.30; % 目标液位,米 % 3. 运行Simulink模型 % 模型名 tank_s1.slx,输出变量 yout 在工作区中返回 simOut = sim('tank_s1.slx', 'StopTime', num2str(t_end)); t = simOut.tout; y = simOut.yout; % 4. 绘图观察液位跟踪与阀门开度 figure(1); plot(t, y(:,1), 'LineWidth', 1.5); hold on; yline(setpoint, '--'); xlabel('时间/s'); ylabel('水箱2液位/m'); legend('液位', '设定值');第一行tank_params调用前面的参数脚本,保证 S-Function 里的工作区变量都存在;readfis读取模糊控制器;assignin把 fis 对象写到基础工作区,这是接下来 Simulink 里 Fuzzy Logic Controller 模块读取 FIS 的必要步骤,否则模块会报 undefined variable。sim命令的第一个参数是模型名,不用带 .slx 扩展名也可以,加扩展名更明确;第二个参数用字符向量指定 StopTime,也可以直接写成sim('tank_s1.slx','StopTime','300')。仿真对象simOut里通过.tout和.yout取时间序列和输出矩阵,新版本 MATLAB 也可以用simOut.logsout按信号名取值,具体写法取决于模型输出端口的配置方式。
4.2 模糊控制仿真结果与常规PID对比
在同样的水箱模型上分别跑一个整定良好的 PID 和一个模糊控制器,通常能看到几个典型差异:模糊控制的上升时间略慢但几乎没有超调,PID 为了让上升时间快很难兼顾超调;在进入稳态后,模糊控制对阶跃扰动的恢复更快,因为规则中 EC 的输入项会提前反应。下面是我在双容水箱参数下得到的一组对比指标,实际数值依赖你的模型参数,但趋势一致:
| 控制器 | 超调量 (%) | 调节时间 (s) | 稳态误差 (m) | 抗扰恢复时间 (s) |
|---|---|---|---|---|
| 常规PID | 18.6 | 95 | 0.006 | 42 |
| 模糊控制 | 3.2 | 78 | 0.004 | 25 |
需要说明的是,上表不是固定结论。模糊控制器的性能完全由规则表和量化因子决定,如果量化因子取得过大,模糊控制器同样会超调并振荡。对比实验的意义在于展示变增益特性:模糊控制器在小偏差时等效增益低,因此不容易激发小幅度振荡;大偏差时等效增益高,所以响应速度和 PID 相当。
4.3 FuzzyPID.m:自适应模糊PID的设计思路
FuzzyPID.m 是另一个方向的进阶实现,它不直接输出阀位,而是输出 PID 参数增量。结构上,它把当前 PID 参数写成全局变量或模型工作区变量,在每个仿真步进中根据 E 和 EC 查询三个 FIS,得到 dKp、dKi、dKd,再和基准值相加。这样做保留了 PID 的积分作用,又能让增益随工况变化,适合双容水箱这种有容量滞后且工况变化频繁的对象。
function [Kp, Ki, Kd] = FuzzyPID(E, EC) % E - 液位偏差,EC - 偏差变化率 % 用三个FIS分别整定PID参数增量 global Kp0 Ki0 Kd0; % 基准PID参数 global fisKp fisKi fisKd; % 三个模糊控制器 global KpScale KiScale KdScale; % 输出比例因子 dKp = evalfis(fisKp, [E, EC]); dKi = evalfis(fisKi, [E, EC]); dKd = evalfis(fisKd, [E, EC]); Kp = Kp0 + dKp * KpScale; Ki = Ki0 + dKi * KiScale; Kd = Kd0 + dKd * KdScale; endevalfis是模糊逻辑工具箱的推理函数,输入是 [E EC] 行向量,返回该规则库推理并去模糊化后的输出。三个 FIS 的规则经验是:Kp 调整量在偏差大时取正,偏差小时取负;Ki 在偏差较小、EC 较小时取大,避免小偏差时积分作用太弱导致稳态误差;Kd 要响应 EC 的符号,偏差变化剧烈时加大微分作用以抑制超调。这里使用global只是为了演示方便,工程上更推荐写成函数参数或使用 Simulink 的 Tunable 参数,能避免命名冲突。把这个函数写成 MATLAB Function 模块,嵌入 Simulink 的 PID 控制器之前,即可实现自适应模糊PID仿真。
5. 量化因子整定、积分修正与MATLAB调试技巧
5.1 量化因子与输出比例因子的整定顺序
模糊控制器在接入实际模型前,要先确定三个无量纲化系数:量化因子 Ke、Kec 和输出比例因子 Ku。我的经验是从开环阶跃响应读数据,设定值变化量记为 delta,取 Ke=1/delta;观察液位变化速度的最大值记为 max_ec,取 Kec=1/max_ec;Ku 则从阀门最大可调范围反推,取 Ku=100/k,让规则表里的 PB 对应全开。
% 根据开环响应计算量化因子 delta = 0.2; % 设定值阶跃幅值 max_ec = 0.015; % 开环响应最大变化率 Ke = 1 / delta; % 偏差量化因子 Kec = 1 / max_ec; % 偏差变化率量化因子 Ku = 50 / 1.0; % 输出比例因子,50%阀门开度对应1.0控制量整定顺序是先固定 Ke,用 Kec 控制动态状态;响应震荡就调大 Kec 或调小 Ku;响应太缓就调大 Ku 或调小 Kec。每轮修改后重新运行 Fuzzy_Water_Tank.m,看gensurf的控制曲面是否出现平台区;出现平台区说明某个规则组合落入死区,需要检查量化因子是否覆盖满论域。
5.2 稳态误差、积分修正与工具箱兼容
双容水箱本身是自衡对象,模糊控制器不引入积分项也能做到零稳态误差,但这依赖规则表在零点附近有足够精细的 ZO 子集。如果发现稳态误差在量级上不可接受,比较实用的方案不是加密规则,而是在模糊控制输出后并联一个增益很小的积分环节,把积分输出与模糊输出相加送给阀门。积分系数一般取 0.01 到 0.05,太大容易出现低频振荡。
MATLAB 调试时还有几个常见坑。readfis报文件不存在,先检查当前路径,用which FUZZ1.fis查看;evalfis报维度错误,检查输入变量是否按 [E EC] 顺序拼接;sim命令返回的.yout在新版本是 Simulink SimulationOutput 对象,不能直接取冒号索引,要改用simOut.logsout或配置信号记录。最后,安装 MATLAB 时记得选择 Fuzzy Logic Toolbox,否则readfis和evalfis都会提示未定义函数;下载安装后可以通过ver('fuzzy')确认工具箱版本,再重新运行仿真脚本。
本文还有配套的精品资源,点击获取