Сообщество Engee

Система управления теплицей

Автор
avatar-akhmedakhmed
Notebook

Система управления теплицей

Задача системы управления теплицей состоит в поддержании в теплице заданных значений температуры и влажности. Система управления состоит из регуляторов и объекта управления. В данной модели регуляторы представлены двумя нечеткими логическими контроллерами — по одному для температуры (Heater Control) и влажности (Humidity Control), а объект управления — математической моделью теплицы (Greenhouse).

Схема модели в Engee:

image_2.png

Объект управления — подсистема Greenhouse

Поскольку предполагается управление двумя показателями — температурой и влажностью — подсистема теплицы разделена на две подсистемы: Termal Model и Humidity Model соответственно.

image.png

Математическая модель изменения температуры — подсистема Termal Model

Подсистема принимает два входных сигнала: Heater и Ventilation, обозначающие воздействие системы отопления и воздействие системы вентиляции соответственно, — и возвращает два выходных сигнала: OutsideTemperature и InsideTemperature, обозначающие значение температуры снаружи и внутри теплицы соответственно.

Дифференциальное уравнение, описывающее изменение температуры в теплице, имеет следующий вид:

где:

  • — Плотность воздуха внутри, ;
  • — Удельная теплоемкость воздуха, ;
  • — Объем теплицы, ;
  • — Температура внутри, ℃;
  • — Тепло, полученное от коротковолнового излучения;
  • — Конвекционные и конденсационные тепловые потери;
  • — Тепловые потери от проникновения тепла через покрытие наружу;
  • — Длинноволновые тепловые потери;
  • — Тепло, полученное от системы отопления;
  • — Тепловые потери от системы вентиляции.

Схема модели в Engee:
image_2.png

Рамкой на схеме выделены настраиваемые параметры системы:

  • V — Объем теплицы, ;
  • L — Длина теплицы, ;
  • S — Площадь поверхности, ;
  • alpha_c — Поглощающая способность покрытия;
  • tau_c — Пропускная способность покрытия;
  • L_c — Толщина покрытия, ;
  • K_c — Коэффициент покрытия; ;
  • S_c — Площадь теплицы, ;
  • R — Воздухообмен в час, ;
  • p_out — Давление насыщенного пара снаружи;
  • p_in — Давление насыщенного пара внутри;
  • C_e — Коэффициент переноса водяного пара;
  • rho_a — Плотность воздуха внутри, ;
  • C_a — Удельная теплоемкость воздуха, ;
  • N_h — Число обогревателей.

Математическая модель изменения влажности — подсистема Humidity Model

Подсистема принимает три входных сигнала: Ventilation, Humidifying, Dehumidifying обозначающие воздействие системы вентиляции, воздействие увлажнения и осушения системы контроля уровня влажности соответственно, — и возвращает два выходных сигнала: OutsideHumidity и InsideHumidity, обозначающие значение уровня влажности воздуха снаружи и внутри теплицы соответственно.

Дифференциальное уравнение, описывающее изменение температуры в теплице, имеет следующий вид:

где:

  • — Плотность воздуха внутри, ;
  • — Объем теплицы, ;
  • — Относительная влажность воздуха внутри;
  • — Пар, переносимый из почвы в воздух внутри помещения в результате испарения;
  • — Инфильтрационные потери;
  • — Увлажнение;
  • — Осушение.

Схема модели в Engee:
image_2.png

Рамкой на схеме выделены настраиваемые параметры системы:

  • C_e — Коэффициент переноса водяного пара, ;
  • p_out — Давление насыщенного пара снаружи, ;
  • p_in — Давление насыщенного пара внутри, ;
  • rho_a — Плотность воздуха внутри, ;
  • V — Объем теплицы, .

Регуляторы — контроллеры на основе нечеткой логики

Другим элементом системы управления являются контроллеры на основе нечеткой логики — они реализованы с помощью блока Engee Function.

Для создания регуляторов на основе нечеткой логики используется библиотека FuzzyLogic:

In [ ]:
# Установка библиотеки FuzzyLogic и Plots
Pkg.add(["FuzzyLogic", "Plots"])

# Подключение библиотек FuzzyLogic и Plots
using FuzzyLogic
using Plots

Настройка регулятора температуры

Далее осуществляется построение системы нечёткого вывода Мамдани (Mamdani fuzzy inference system) с помощью макроса @mamfis. Для этого необходимо определить функций принадлежности (Membership functions).

В случае регулятора температуры объявляются функции controller_heater и controller_ventilation, принимающие на вход аргумент Error_T, который представляет собой ошибку между заданным и текущим значениями температуры внутри теплицы.

Функции принадлежности включают в себя:

  • треугольную функцию принадлежности TriangularMF(x, y, z), где x - левый угол треугольника, z - правый, а y - пик треугольника;
  • трапециевидную функцию принадлежности TrapezoidalMF(x, y, z, k), где x - левый нижний угол трапеции, k - правый нижний угол, y и z - левый верхний и правый верхний углы соответственно.

В переменной domain определяются диапазоны в которых будут определены функции принадлежности. В последнем блоке задаются правила нечёткой логики, которые устанавливают зависимость входных и выходных переменных.

In [ ]:
fis_heater = @mamfis function controller_heater(Error_T)::Heater
    
    # Задание функций принадлежности

    Error_T := begin
        domain = -10.0:10.0
        NNN = TrapezoidalMF(-30.0, -11.0, -2.0, -0.6)
        NN = TriangularMF(-2.0, -1.0, -0.05)
        Z = TriangularMF(-0.3, 0.0, 0.3)
        PP = TriangularMF(0.05, 1.0, 2.0)
        PPP = TrapezoidalMF(0.5, 2.0, 11.0, 30.0)
    end

    Heater := begin
        domain = 0.0:1000.0
        Z = TrapezoidalMF(-0.5, 0.0, 86.5, 232.0)
        M = TrapezoidalMF(20.0, 200.0, 303.0, 394.0)
        H = TrapezoidalMF(303.0, 507.0, 1000.0, 1000.5)
    end

    # Задание правил

    Error_T == NNN --> Heater == Z
    Error_T == NN --> Heater == Z
    Error_T == Z --> Heater == Z
    Error_T == PP --> Heater == M
    Error_T == PPP --> Heater == H
end
In [ ]:
fis_ventilation = @mamfis function controller_ventilation(Error_T)::Ventilation
    
    # Задание функций принадлежности

    Error_T := begin
        domain = -10.0:10.0
        NNN = TrapezoidalMF(-30.0, -11.0, -2.0, -0.56)
        NN = TriangularMF(-2.0, -1.0, -0.05)
        Z = TriangularMF(-0.3, 0.0, 0.3)
        PP = TriangularMF(0.05, 1.0, 2.0)
        PPP = TrapezoidalMF(0.5, 2.0, 11.0, 30.0)
    end

    Ventilation := begin
        domain = 0.0:3.5
        Z = TrapezoidalMF(-1.5, -1.0, 0.1, 0.3)
        M = TrapezoidalMF(0.0, 0.2, 0.5, 1.0)
        H = TrapezoidalMF(0.5, 1.0, 4.0, 4.5)
    end

    # Задание правил

    Error_T == NNN --> Ventilation == H
    Error_T == NN --> Ventilation == M
    Error_T == Z --> Ventilation == Z
    Error_T == PP --> Ventilation == Z
    Error_T == PPP --> Ventilation == Z
end

Построение графика функции принадлежности для входной переменной Error_T:

In [ ]:
plot(fis_heater, :Error_T)
Out[0]:

Построение графика функции принадлежности для выходной переменной Heater:

In [ ]:
plot(fis_heater, :Heater)
Out[0]:

Построение графика функции принадлежности для выходной переменной Ventillation:

In [ ]:
plot(fis_ventilation, :Ventilation)
Out[0]:

Настройка регулятора влажности

Аналогичным образом осуществляется построение системы нечеткого вывода Мамдани для регулятора влажности. В этом случае объявляются функции controller_humidifying и controller_dehumidifying, принимающие на вход аргумент Error_H, который представляет собой ошибку между заданным и текущим значениями влажности внутри теплицы.

In [ ]:
fis_humidifying = @mamfis function controller_humidifying(Error_H)::Humidifying
    
    # Задание функций принадлежности

    Error_H := begin
        domain = -50.0:50.0
        NNN = TrapezoidalMF(-60.5, -60.0, -7.0, -5.0)
        NN = TrapezoidalMF(-7.0, -5.0, -3.0, -1.0)
        Z = TriangularMF(-2.0, 0.0, 2.0)
        PP = TrapezoidalMF(1.0, 3.0, 5.0, 7.0)
        PPP = TrapezoidalMF(5.0, 7.0, 60.0, 60.5)
    end

    Humidifying := begin
        domain = 0.0:50.0
        Z = TrapezoidalMF(-0.5, 0.0, 5.0, 10.0)
        M = TrapezoidalMF(5.0, 10.0, 30.0, 35.0)
        H = TrapezoidalMF(26.0, 35.0, 51.0, 51.5)
    end

    # Задание правил

    Error_H == NNN --> Humidifying == Z
    Error_H == NN --> Humidifying == Z
    Error_H == Z --> Humidifying == Z
    Error_H == PP --> Humidifying == M
    Error_H == PPP --> Humidifying == H
end
In [ ]:
fis_dehumidifying = @mamfis function controller_dehumidifying(Error_H)::Dehumidifying
    
    # Задание функций принадлежности

    Error_H := begin
        domain = -50.0:50.0
        NNN = TrapezoidalMF(-60.5, -60.0, -7.0, -5.0)
        NN = TrapezoidalMF(-7.0, -5.0, -3.0, -1.0)
        Z = TriangularMF(-2.0, 0.0, 2.0)
        PP = TrapezoidalMF(1.0, 3.0, 5.0, 7.0)
        PPP = TrapezoidalMF(5.0, 7.0, 60.0, 60.5)
    end

    Dehumidifying := begin
        domain = 0.0:50.0
        Z = TrapezoidalMF(-0.5, 0.0, 5.0, 10.0)
        M = TrapezoidalMF(5.0, 10.0, 30.0, 35.0)
        H = TrapezoidalMF(26.0, 35.0, 51.0, 51.5)
    end

    # Задание правил

    Error_H == NNN --> Dehumidifying == H
    Error_H == NN --> Dehumidifying == M
    Error_H == Z --> Dehumidifying == Z
    Error_H == PP --> Dehumidifying == Z
    Error_H == PPP --> Dehumidifying == Z
end

Построение графика функции принадлежности для входной переменной Error_H:

In [ ]:
plot(fis_humidifying, :Error_H)
Out[0]:

Построение графика функции принадлежности для выходной переменной Humidifying:

In [ ]:
plot(fis_humidifying, :Humidifying)
Out[0]:

Построение графика функции принадлежности для выходной переменной Deumidifying:

In [ ]:
plot(fis_dehumidifying, :Dehumidifying)
Out[0]:

Компиляция систем нечеткого вывода и сохранение результата

Далее настроенные системы нечеткого вывода компилируются с помощью функции compilefis. Аргументами функции являются файл, в который сохраняется результат компиляции, и система нечеткого вывода.

In [ ]:
asd_heater = compilefis("$(@__DIR__)/asd_heater.jl", fis_heater)
asd_ventilation = compilefis("$(@__DIR__)/asd_ventilation.jl", fis_ventilation)
asd_humidifying = compilefis("$(@__DIR__)/asd_humidifying.jl", fis_humidifying)
asd_dehumidifying = compilefis("$(@__DIR__)/asd_dehumidifying.jl", fis_dehumidifying)

Теперь функции, полученные в результате компиляции, могут быть использованы в блоках Engee Function.

Определение параметров системы и начальных условий

Параметры системы и начальные условия задаются в кодовой ячейке:

In [ ]:
alpha_c = 0.1       # Поглощающая способность покрытия
rho_a = 1.137       # Плотность воздуха внутри
R = 2.0             # Длина теплицы
L_c = 0.0001        # Толщина покрытия
K_c = 0.028         # Коэффициент k покрытия
S_c = 10.0          # Площадь теплицы
L = 3.0             # Длина теплицы
tau_c = 0.85        # Пропускная способность покрытия
N_h = 10.0          # Число обогревателей
S = 26.3            # Площадь поверхности
C_a = 1005.0        # Удельная теплоемкость воздуха
V = 12.6            # Объем теплицы
C_e = 0.05          # Коэффициент переноса водяного пара
p_out = 1010        # Давление насыщенного пара снаружи
p_in = 1050         # Давление насыщенного пара внутри

FixedStep = 0.5             # Шаг расчета
StartTime = 0.0             # Начало симуляции
StopTime = 172800.0         # Конец симуляции
SolverType = "fixed-step"   # Тип решателя
SolverName = "Euler"        # Название решателя

Затем преобразуем CSV-файлы с данными, необходимыми в ходе симуляции, в структуры WorkspaceArray

In [ ]:
# Установка библиотеки CSV и DataFrames
Pkg.add(["CSV", "DataFrames"])

# Подключение библиотек CSV и DataFrames
using CSV
using DataFrames
In [ ]:
# Импорт данных из CSV в виде DataFrame
T_out = CSV.read("T_out.csv", DataFrame)
H_out = CSV.read("H_out.csv", DataFrame)
V_w = CSV.read("V_w.csv", DataFrame)
I = CSV.read("I.csv", DataFrame)

# Переименуем поля структур DataFrame
rename!(T_out, :T_out_time => :time, :T_out_values => :value)
rename!(H_out, :H_out_time => :time, :H_out_values => :value)
rename!(V_w, :V_w_time => :time, :V_w_values => :value)
rename!(I, :I_time => :time, :I_values => :value)

# Преобразуем тип данных полей в Float64
T_out.time = Float64.(T_out.time)
H_out.time = Float64.(H_out.time)
V_w.time = Float64.(V_w.time)
I.time = Float64.(I.time)

# Преобразуем данные в структуры WorkspaceArray
T_out_wa = WorkspaceArray(string(rand()), T_out)
H_out_wa = WorkspaceArray(string(rand()), H_out)
V_w_wa = WorkspaceArray(string(rand()), V_w)
I_wa = WorkspaceArray(string(rand()), I)

Запуск и загрузка модели

В данной ячейке с кодом применяются методы для взаимодействия с потоком управления программы, try/catch:

In [ ]:
try
    engee.close("GreenhouseControl", force=true)                # Закрытие модели 
    catch err                                                   # В случае, если нет модели, которую нужно закрыть и engee.close() не выполняется, то будет выполнена её загрузка после catch
        m = engee.load("$(@__DIR__)/GreenhouseControl.engee")   # Загрузка модели
    end;
In [ ]:
engee.set_param!(m, "StartTime" => StartTime, "StopTime" => StopTime, "SolverType" => SolverType, "SolverName" => SolverName, "FixedStep" => FixedStep)    # Настройка параметров симуляции

try
    engee.run(m, verbose=true)                                  # Запуск модели
    catch err                                                   # В случае, если модель не загружена и engee.run() не выполняется, то будут выполнены две нижние строки после catch
        m = engee.load("$(@__DIR__)/GreenhouseControl.engee")   # Загрузка модели
        engee.run(m, verbose=true)                              # Запуск модели
    end

Загрузка и визуализация данных, полученных в ходе симуляции

Чтение CSV-файлов с данными об изменении температуры и влажности:

In [ ]:
ref_InsideTemperature = Matrix(CSV.read("ref_InsideTemperature.csv", DataFrame));   # Загрузка данных об изменении заданного значения температуры
OutsideTemperature = Matrix(CSV.read("OutsideTemperature.csv", DataFrame));         # Загрузка данных об изменении значения температуры внутри теплицы
InsideTemperature = Matrix(CSV.read("InsideTemperature.csv", DataFrame));           # Загрузка данных об изменении значения температуры снаружи теплицы

ref_InsideHumidity = Matrix(CSV.read("ref_InsideHumidity.csv", DataFrame));     # Загрузка данных об изменении заданного значения влажности
OutsideHumidity = Matrix(CSV.read("OutsideHumidity.csv", DataFrame));           # Загрузка данных об изменении значения влажности снаружи теплицы
InsideHumidity = Matrix(CSV.read("InsideHumidity.csv", DataFrame));             # Загрузка данных об изменении значения влажности внутри теплицы

Подключение бэкэнда — метода отображения графики:

In [ ]:
gr()

Построение графиков изменения температуры

In [ ]:
plot(ref_InsideTemperature[:,1], ref_InsideTemperature[:,2], label="Заданное значение температуры")
plot!(OutsideTemperature[:,1], OutsideTemperature[:,2], label="Значение температуры снаружи")
plot!(InsideTemperature[:,1], InsideTemperature[:,2], label="Значение температуры внутри")
Out[0]:

Построение графиков изменения уровня влажности

In [ ]:
plot(ref_InsideHumidity[:,1], ref_InsideHumidity[:,2], label="Заданное значение уровня влажности")
plot!(OutsideHumidity[:,1], OutsideHumidity[:,2], label="Значение уровня влажности снаружи")
plot!(InsideHumidity[:,1], InsideHumidity[:,2], label="Значение уровня влажности внутри")
Out[0]: