Engee 文档
Notebook

模糊调节器的管道压力控制仿真

本例演示了使用模糊调节器的管道压力控制仿真。

模型文件包含两个带控制系统的液压回路。 上层电路由PID控制器控制,其描述可以在社区中找到。:在трубопроводе压力控制的模拟管路压力控制。 两个电路中的设定点信号由块表示
Chart

下层电路具有物理块的类似参数。

模型图:

liquid_pressure_regulator_fuzzy_1--1728316412037.png

模糊控制器使用块描述 Engee Function. 它允许您使用Julia编程语言中的代码。 了解更多关于合作的信息 Engee Function 您可以在相应的文章документации.

要创建模糊控制器,让我们使用名为FuzzyLogic的Julia编程语言库。 要在Engee中安装它,您需要执行以下代码。:

In [ ]:
Pkg.add(["FuzzyLogic"])
In [ ]:
Pkg.add("FuzzyLogic")

我们正在启动已安装的FuzzyLogic库和Plots图表库。

In [ ]:
using FuzzyLogic
using Plots

在下面的代码单元格中,正在使用宏构建Mamdani模糊推理系统 @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 它将部分开放。
等其余条件。

In [ ]:
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
Out[0]:
tipper

Inputs:
-------
delta_p ∈ [-50000.0, 50000.0] with membership functions:
    very_high = GaussianMF{Float64}(17000.0, 3000.0)
    high = GaussianMF{Float64}(15000.0, 4120.0)
    moderate = GaussianMF{Float64}(-6000.0, 3000.0)
    low = GaussianMF{Float64}(-15000.0, 3000.0)
    very_low = GaussianMF{Float64}(-30000.0, 3000.0)


Outputs:
--------
control_signal ∈ [0.0, 0.005] with membership functions:
    fully_closed = TriangularMF{Float64}(1.0e-11, 1.0e-8, 1.0e-6)
    partially_closed = TriangularMF{Float64}(9.0e-7, 5.0e-6, 1.0e-5)
    neutral = TriangularMF{Float64}(1.0e-5, 2.0e-5, 0.0003)
    partially_open = TrapezoidalMF{Float64}(0.0003, 0.0009, 0.003, 0.0044)
    fully_open = TriangularMF{Float64}(0.004, 0.0047, 0.005)


Inference rules:
----------------
delta_p is very_high --> control_signal is fully_open
delta_p is high --> control_signal is partially_open
delta_p is moderate --> control_signal is neutral
delta_p is low --> control_signal is partially_closed
delta_p is very_low --> control_signal is fully_closed


Settings:
---------
- MinAnd()
- MaxOr()
- MinImplication()
- MaxAggregator()
- CentroidDefuzzifier(100)

绘制输入数据的隶属函数:

In [ ]:
plot(fis, :delta_p)
Out[0]:

绘制数据输出的隶属函数:

In [ ]:
plot(fis, :control_signal)
Out[0]:

为了将这种模糊推理系统实现为控制系统,有必要从中生成一个不依赖于库的函数,并且仅由Julia语言的基本构造定义。 为此,您可以使用该函数 compilefis(fis),其中fis自变量是先前定义并写入变量的模糊推理系统。 使用此函数生成代码的结果将写入asd变量。:

In [ ]:
asd = compilefis(fis)
Out[0]:
:(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)

记录在asd变量中的结果被重写成代码单元。:

In [ ]:
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
Out[0]:
tipper (generic function with 2 methods)

通过定义函数 tipper() 描述模糊输出系统,我们可以绘制输出数据对输入数据的依赖性。 为此,我们确定从-50000到50,000Pa的压力值向量,如之前在变量中所写的那样 domain 我们通过函数传递它:

In [ ]:
x = collect(range(-50000.0,50000.0,10001));

绘制结果函数的响应曲线:

In [ ]:
plot(x, tipper.(x), linewidth=3)
Out[0]:

有阀门打开面积对设定点和来自传感器的信号之间的压力差的依赖性的曲线图。

代码生成得到的函数可以写入文件。 为此,Expr格式的asd变量将转换为字符串格式以供进一步写入。:

In [ ]:
text_function = string(asd)
Out[0]:
"function tipper(delta_p)\n    very_high = exp(-((delta_p - 17000.0) ^ 2) / 1.8e7)\n    high = exp(-((delta_p - 15000.0) ^ 2) / 3.39488e7)\n    moderate = exp(-((delta_p - -6000.0) ^ 2) / 1.8e7)\n    low = exp(-((delta_p - -15000.0) ^ 2) / 1.8e7)\n    very_low = exp(-((delta_p" ⋯ 977 bytes ⋯ "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))\n    return control_signal\nend"

将字符串格式的函数写入文件:

In [ ]:
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

函数(c::块)(t::Real,delta_p)
    include("liquid_pressure_regulator_fuzzy.jl")
    返回控制_signal
结束

接下来,启动模型并在图形上显示数据。

定义加载和运行模型的函数:

In [ ]:
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
Out[0]:
start_model_engee (generic function with 1 method)

运行模拟

In [ ]:
try
    start_model_engee() # запуск симуляции с помощью специальной функции, реализованной выше
    catch err
    end;

从simout变量中提取数据并将其写入变量:

In [ ]:
sleep(5)
result = simout;
res = collect(result)
Out[0]:
4-element Vector{WorkspaceArray}:
 WorkspaceArray("liquid_pressure_regulator_fuzzy/Датчик давления.1")
 WorkspaceArray("liquid_pressure_regulator_fuzzy/Нечёткий регулятор.1")
 WorkspaceArray("liquid_pressure_regulator_fuzzy/Датчик давления-1.1")
 WorkspaceArray("liquid_pressure_regulator_fuzzy/Chart.RefPress")

将两个电路的设定点传感器和压力传感器的信号记录到变量中:

In [ ]:
PID_регулятор = collect(res[1])
Нечёткий_регулятор = collect(res[3])
Сигнал_задатчика = collect(res[4]);

模拟结果的可视化

In [ ]:
plot(PID_регулятор[:,1], PID_регулятор[:,2], linewidth=3, label="PID-регулятор")
plot!(Нечёткий_регулятор[:,1], Нечёткий_регулятор[:,2], linewidth=3, label="Нечёткий регулятор")
plot!(Сигнал_задатчика[:,1], Сигнал_задатчика[:,2], linewidth=3, label="Сигнал задатчика")
Out[0]:

分析该图,可以看到,与PID控制器不同,模糊控制器具有显着更少的超调,以及瞬态过程的持续时间更短。 但模糊控制器也有一个缺点,它的信号往往可以从设定点信号偏移一定量,但这可以通过更精确的隶属函数和判决规则的调整来修正。

结论:

在这个例子中,我们演示了两个物理对象的仿真与自动控制系统的不同变体,特别是PID控制器和模糊控制器。 建立了模糊推理系统,然后将其转化为适合嵌入Engee功能块中的函数。 结果是模糊控制器,其特性不逊色于PID控制器。