利用模糊控制器建立管道压力控制模型
本例演示使用模糊控制器建立管道压力控制模型。
模型示意图:

模糊控制器是通过块Engee Function
来描述的。您可以使用 Julia 编程语言的代码。有关使用Engee Function
的更多详情,请参阅相应的文档文章。
为了创建模糊控制器,我们将使用名为FuzzyLogic的 Julia 编程语言库。要在 Engee 中安装该库,我们需要执行以下代码单元:
Pkg.add(["FuzzyLogic"])
Pkg.add("FuzzyLogic")
运行已安装的FuzzyLogic库和Plots绘图库。
using FuzzyLogic
using Plots
在下面的代码单元中,使用宏@mamfis
构建了马姆达尼模糊推理系统。该系统的构建包括成员函数的定义。我们声明了一个函数tipper
,它接受一个参数delta_p
。该变量将代表来自加法器的输入数据,该加法器用于计算设定压力和测量压力之间的差值。
压差范围和阀门横截面积输出的成员函数包括
- 高斯成员函数
GaussianMF(x, y)
- 其中 x 为平均值,定义差值中心,y 为标准偏差 - 三角形成员函数
TriangularMF(x, y, z)
,其中 x 是三角形的左角,z 是右角,y 是三角形的峰值 - 梯形成员函数
TrapezoidalMF(x, y, z, k)
,其中 x 是梯形的左下角,k 是右下角,y 和 z 分别是左上角和右上角
变量domain
定义了定义成员函数的范围。
然后定义模糊逻辑规则,将输入和输出变量联系起来。例如
如果delta_p
非常高,那么control_signal
将完全打开。
如果delta_p
很高,那么control_signal
将部分打开。
其余条件以此类推。
fis = @mamfis function tipper(delta_p)::control_signal
delta_p := begin
domain = -50000.0:50000.0
very_high = GaussianMF(17000.0, 3000.0) # Очень высокое давление
high = GaussianMF(15000.0, 4120.0) # Высокое давление
moderate = GaussianMF(-6000.0, 3000.0) # Нормальное давление
low = GaussianMF(-15000.0, 3000.0) # Низкое давление
very_low = GaussianMF(-30000.0, 3000.0) # Очень низкое давление
end
control_signal := begin
domain = 0.0:0.005
fully_closed = TriangularMF(0.00000000001, 0.00000001, 0.000001) # Полностью закрытая заслонка
partially_closed = TriangularMF(0.0000009, 0.000005, 0.00001) # Частично закрытая заслонка
neutral = TriangularMF(0.00001, 0.00002, 0.0003) # Нейтральное положение (половина открыта)
partially_open = TrapezoidalMF(0.0003, 0.0009, 0.003, 0.0044) # Частично открытая заслонка
fully_open = TriangularMF(0.004, 0.0047, 0.005) # Полностью открытая заслонка
end
# Работающие правила
delta_p == very_high --> control_signal == fully_open
delta_p == high --> control_signal == partially_open
delta_p == moderate --> control_signal == neutral
delta_p == low --> control_signal == partially_closed
delta_p == very_low --> control_signal == fully_closed
end
绘制输入数据的成员函数图:
plot(fis, :delta_p)
为数据的输出数据绘制成员函数图:
plot(fis, :control_signal)
为了将模糊推理系统作为一个控制系统来实现,有必要从中生成一个独立于库且仅由 Julia 语言基本结构定义的函数。为此,我们可以使用函数compilefis(fis)
,其中参数 fis 是前面定义的模糊推理系统,并写入一个变量。借助该函数生成的代码结果将写入变量 asd:
asd = compilefis(fis)
写入变量 asd 的结果将覆盖代码单元:
function tipper(delta_p)
very_high = exp(-((delta_p - 17000.0) ^ 2) / 1.8e7)
high = exp(-((delta_p - 15000.0) ^ 2) / 3.39488e7)
moderate = exp(-((delta_p - -6000.0) ^ 2) / 1.8e7)
low = exp(-((delta_p - -15000.0) ^ 2) / 1.8e7)
very_low = exp(-((delta_p - -30000.0) ^ 2) / 1.8e7)
ant1 = very_high
ant2 = high
ant3 = moderate
ant4 = low
ant5 = very_low
control_signal_agg = collect(LinRange{Float64}(0.0, 0.005, 101))
@inbounds for (i, x) = enumerate(control_signal_agg)
fully_closed = max(min((x - 1.0e-11) / 9.99e-9, (1.0e-6 - x) / 9.9e-7), 0)
partially_closed = max(min((x - 9.0e-7) / 4.1000000000000006e-6, (1.0e-5 - x) / 5.0e-6), 0)
neutral = max(min((x - 1.0e-5) / 1.0e-5, (0.0003 - x) / 0.00028), 0)
partially_open = max(min((x - 0.0003) / 0.0006000000000000001, 1, (0.0044 - x) / 0.0014000000000000002), 0)
fully_open = max(min((x - 0.004) / 0.0007000000000000001, (0.005 - x) / 0.0002999999999999999), 0)
control_signal_agg[i] = max(max(max(max(min(ant1, fully_open), min(ant2, partially_open)), min(ant3, neutral)), min(ant4, partially_closed)), min(ant5, fully_closed))
end
control_signal = ((2 * sum((mfi * xi for (mfi, xi) = zip(control_signal_agg, LinRange{Float64}(0.0, 0.005, 101)))) - first(control_signal_agg) * 0.0) - last(control_signal_agg) * 0.005) / ((2 * sum(control_signal_agg) - first(control_signal_agg)) - last(control_signal_agg))
return control_signal
end
在定义了描述模糊输出系统的函数tipper()
之后,我们可以根据输入数据绘制输出数据记录,为此,我们定义了从 -50000 到 50000 Pa 的压力值矢量,如之前写入变量domain
所示,并将其传递给函数:
x = collect(range(-50000.0,50000.0,10001));
绘制所得函数的响应曲线:
plot(x, tipper.(x), linewidth=3)
我们可以得到阀门开启面积与设定点和传感器信号之间压力差的关系曲线。
通过代码生成获得的函数可以写入文件。为此,我们将格式为 Expr 的变量 asd 转换为字符串格式,以便进一步写入:
text_function = string(asd)
将字符串格式的函数写入文件:
f = open("/user/start/examples/controls/liquid_pressure_regulator_fuzzy/liquid_pressure_regulator_fuzzy.jl","w") # создание файла
write(f, text_function) # запись в файл
close(f) # закрытие файла
我们将生成的**.jl**格式文件与函数tipper()
一起放入程序块Engee Function
,并使用函数include()
执行。为此,请进入程序块参数,点击 "查看掩码 "按钮,选择 "主要 "选项卡并点击 "编辑源代码",然后在打开的窗口中编写:
struct Block <: AbstractCausalComponent; end
function (c::Block)(t::Real, delta_p)
include("liquid_pressure_regulator_fuzzy.jl")
返回控制信号
结束
接下来,运行模型并绘制数据。
定义加载和运行模型的函数:
function start_model_engee()
try
engee.close("liquid_pressure_regulator_fuzzy", force=true) # закрытие модели
catch err # в случае, если нет модели, которую нужно закрыть и engee.close() не выполняется, то будет выполнена её загрузка после catch
m = engee.load("$(@__DIR__)/liquid_pressure_regulator_fuzzy.engee") # загрузка модели
end;
try
engee.run(m) # запуск модели
catch err # в случае, если модель не загружена и engee.run() не выполняется, то будут выполнены две нижние строки после catch
m = engee.load("$(@__DIR__)/liquid_pressure_regulator_fuzzy.engee") # загрузка модели
engee.run(m) # запуск модели
end
end
运行模拟
try
start_model_engee() # запуск симуляции с помощью специальной функции, реализованной выше
catch err
end;
从 simout 变量中选择数据并写入变量:
sleep(5)
result = simout;
res = collect(result)
将两个回路的设定值调节器和压力传感器的信号写入变量:
PID_регулятор = collect(res[1])
Нечёткий_регулятор = collect(res[3])
Сигнал_задатчика = collect(res[4]);
模拟结果的可视化
plot(PID_регулятор[:,1], PID_регулятор[:,2], linewidth=3, label="PID-регулятор")
plot!(Нечёткий_регулятор[:,1], Нечёткий_регулятор[:,2], linewidth=3, label="Нечёткий регулятор")
plot!(Сигнал_задатчика[:,1], Сигнал_задатчика[:,2], linewidth=3, label="Сигнал задатчика")
分析图表后,我们可以发现,与 PID 调节器不同,模糊控制器的过冲更小,瞬态过程的持续时间也更短。但模糊控制器也有缺点,那就是它的信号经常会偏离设定点信号一定的值,但这可以通过更精确地调整归属函数和求解规则来纠正。
结论
本例演示了用不同的自动控制系统,特别是 PID 控制器和模糊控制器,对两个物理对象进行建模。我们构建了一个模糊推理系统,然后将其转换为一个适合嵌入 Engee 功能块的函数。结果,模糊控制器与 PID 控制器一样好。