Сообщество Engee

Управление конвейером

Автор
avatar-alexevsalexevs
Notebook

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

Этот пример продолжает развитие проекта моделирования конвейера. При помощи блоков Engee Function и Chart реализуем имитацию движения грузов различной массы на ленте и систему управления конвейером на базе программируемого логического контроллера (ПЛК). Физическую модель с алгоритмом управления в завершение тестируем в реальном времени на КПМ РИТМ.

Введение

В предыдущем примере проекта мы рассчитали и промоделировали электрические и механические системы конвейера в условиях изменяющейся нагрузки на ленте. Теперь настал черёд перейти к автоматизации конвейера. На этом шаге развития проекта мы расширим исходную модель:

  • добавим систему торможения двигателя,

  • разработаем блок расчёта положения и общей нагрузки грузов на ленте,

  • добавим подсистемы:

    • определения прибытия груза на край ленты,

    • размещения нового груза на ленте,

  • разработаем систему управления конвейером.

Модель примера

Модель текущего примера в итоге выглядит так, как показано на рисунке ниже:

conveyor_control_02_09_25_11_31_14.png

Рассмотрим элементы модели подробно далее.

Изменения в цепи физических блоков

В системе физических блоков добавлена цепи динамического торможения - по сигналу on_off отключается питание переменного напряжения, а на следующем шаге моделирования в две фазы двигателя подаётся постоянное напряжение от источника Uдт.

Подсистема "Грузы на конвейере"

Основной элемент подсистемы "Грузы на конвейере" - блок Engee Function, в котором кодом на Julia описано поведение грузов на ленте в процессе моделирования. Этот блок получает сигналы:

  • "размещение нового груза" (переменная in1) - длительностью в один шаг моделирования, и величиной натяжения нового размещаемого груза,

  • "перемещение ленты" (переменная in2) - пройденное лентой конвейера расстояние с момента начала моделирования,

  • "снять прибывший груз" (переменная in3) - логический сигнал, служащий командой для удаления из модели описания грузов на ленте того груза, который был доставлен к краю ленты.

На выходе блока мы получаем следующие сигналы:

  • "позиции" - вектор статического размера, несущий в себе все значения текущего расположения грузов, связанные с перемещением ленты. На выходе подсистемы из этого вектора выделяется первый элемент, соответствующий грузу с наибольшей величиной позиции - т.е. стоящий дальше других от начала ленты.

  • "нагрузки" - вектор статического размера, несущий в себе все значения нагрузки от каждого груза, соответствующие вектору "позиции".

  • "общая нагрузка" - суммарная величина нагрузки от всех грузов на ленте. На выходе подсистемы передаётся со знаком минус для формирования корректного вектора нагрузки.

image.png

Ниже приведён код из вкладки Step method Code блока Engee Function с комментариями для пояснения принципов работы подсистемы.

 if any(w -> w != 0.0, c.wght) # если на ленте есть хотя бы один груз
        idx = findfirst(w -> w == 0.0, c.wght) # находим его индекс в векторе
        c.pos[1:(idx-1)] = c.pos[1:(idx-1)] .+ (in2 - c.mov[1]) # увеличиваем перемещение каждого груза на приращение перемещения ленты
    end
    
    c.mov[1] = in2 # сохраняем текущее перемещение ленты
    
    if in3 == true # если требуется снять первый груз
        for i in 1:length(c.wght)-1 # передвигаем все грузы в векторе нагрузок на idx-1
            c.wght[i] = c.wght[i+1]
        end
        c.wght[end] = 0.0 
        for i in 1:length(c.pos)-1 # передвигаем все грузы в векторе положений на idx-1
            c.pos[i] = c.pos[i+1]
        end
        c.pos[end] = 0.0
    end

    if (in1 > 0.0) && (0.0 in c.wght) # если поступает новый груз
        idx = findfirst(w -> w == 0.0, c.wght) 
        c.wght[idx] = in1 # размещаем его нагрузку на вакантную позицию
        c.pos[idx] = 0.0 # задаём ему положение "0.0"
    end


    return c.pos, c.wght, sum(c.wght) # выводим: вектор положений, нагрузок, суммарную нагрузку

Переменные c.pos, c.wght, sum(c.wght) определены в структуре блока и инициализированы нулевыми значениями.

Подсистема "Ультразвуковой датчик"

Значение расположения груза наиболее удалённого от начала ленты, вычисленное в блоке "Грузы на конвейере", передаётся в подсистему "Ультразвуковой датчик". В случае равенства положения груза расположению датчика L формируется логический сигнал высокого уровня "груз можно снимать". Расположение датчика L в данном примере равно расстоянию между барабанами конвейера.

image.png

Подсистема "Размещение груза"

Ещё одна вспомогательная подсистема - "Размещение груза". В ней, по сигналу "разместить новый груз" от программируемого логического контроллера генерируется сигнал длительностью в один шаг моделирования и случайной величиной нагрузки в диапазоне, установленном ранее в исходном примере.

image.png

Полученный сигнал "нагрузка от нового" затем поступает в подсистему "грузы на конвейере".

Подсистема "Программируемый логический контроллер"

Алгоритм управления конвейером определяется в подсистеме "Программируемый логический контроллер" (ПЛК) при помощи блока Chart.

Принцип работы алгоритма интуитивно понятен, но дополнительно зафиксируем основные моменты:

  • в диаграмме состояний 2 основных процесса:

    • движение ленты (состояние mov),

    • обработка снятия груза.

  • состояние mov включает в себя три подсостояния:

    • start - выполняется только в начале моделирования, запускает двигатель (motor = 0), через 1 секунду после него включается следующее состояние:

    • give - формирует сигнал размещения груза (put = 1), сразу после него выполняется переход в следующее состояние:

    • wait - снимаем сигнал размещения груза (put = 1), формируем ожидание 3 секунды для размещения нового груза.

  • обработка снятия груза происходит так:

    • если срабатывает датчик наличия груза (sensor == 1), происходит переход в состояние stop,

    • в stop останавливается двигатель (motor = 1), через 1 секунду происходи переход в следующее состояние:

    • в take формируется сигнал удаления прибывшего груза (rem = 1),

    • сразу после этого сигнал удаления груза снимается (rem = 0),

    • через 1 секунду диаграмма возвращается в состояние mov.

image.png

Перейдём к моделированию в Engee.

Моделирование в Engee

Запустим модель при помощи программного управления моделированием и подготовленных заранее функций для удобства работы с моделью и результатами симуляции:

In [ ]:
(example_path = @__DIR__) |> cd; # Получаем абсолютный путь к директории, содержащей текущий скрипт и переходим в неё
include("useful_functions.jl"); # Подключаем скрипт Julia со вспомогательными функциями
In [ ]:
simout = get_sim_results("conveyor_control.engee", example_path) #запускаем моделирование
Out[0]:
SimulationResult(
    "нагрузки" => WorkspaceArray{Vector{Float64}}("conveyor_control/Грузы на конвейере/нагрузки")
,
    "старт/стоп двигаталя" => WorkspaceArray{Float64}("conveyor_control/Программируемый логический контроллер/старт/стоп двигаталя")
,
    "Перемещение ленты" => WorkspaceArray{Float64}("conveyor_control/Перемещение ленты")
,
    "позиции" => WorkspaceArray{Vector{Float64}}("conveyor_control/Грузы на конвейере/позиции")
,
    "Программируемый логический контроллер.rem" => WorkspaceArray{Float64}("conveyor_control/Программируемый логический контроллер/Программируемый логический контроллер.rem")
,
    "Ток статора" => WorkspaceArray{Float64}("conveyor_control/Ток статора")
,
    "разместить новый груз" => WorkspaceArray{Float64}("conveyor_control/Программируемый логический контроллер/разместить новый груз")
,
    "Обороты (у.е)" => WorkspaceArray{Float64}("conveyor_control/Обороты (у.е)")
,
    "Скорость ленты" => WorkspaceArray{Float64}("conveyor_control/Скорость ленты")

)

Результаты моделирования - двигатель

Получим и проредим записанные в модели сигналы, измеренные на двигателе:

In [ ]:
t = thin(get_sim_data(simout, "Ток статора", 1, "time"), 100);
Is = thin(get_sim_data(simout, "Ток статора", 1, "value"), 100);
n = thin(get_sim_data(simout, "Обороты (у.е)", 1, "value"), 100);

Построим графики этих переменных:

In [ ]:
gr(fmt=:png, size = (900,400))
I_graph = plot(t, Is; label = "I(t)", title = "Ток статора", ylabel = "I [A]", xlabel = "t [с]")
n_graph = plot(t, n; label = "n(t)", title = "Частота вращения ротора", ylabel = "n [у.е.]", xlabel = "t [с]")
plot(I_graph, n_graph)
Out[0]:
No description has been provided for this image

Если сравнить эти графики с теми, что были представлены в исходном примере, видно, что при достижении определённой нагрузке на двигателе, примерно через 50 секунд, двигатель начинает периодически останавливаться и запускаться - он останавливается для снятия груза. После запуска двигателя нагрузка продолжает изменяться за счёт добавления новых грузов на ленту.

Результаты моделирования - конвейер

Получим и проредим сигналы, записанные в модели на конвейерной ленте:

In [ ]:
 = thin(get_sim_data(simout, "Скорость ленты", 1, "value"), 100);
 = thin(get_sim_data(simout, "Перемещение ленты", 1, "value"), 100);

Построим графики этих переменных:

In [ ]:
V_graph = plot(t, ; label = "V(t)", title = "Скорость ленты", ylabel = "V [м/с]", xlabel = "t [с]")
S_graph = plot(t, ; label = "S(t)", title = "Перемещение ленты", ylabel = "S [м]", xlabel = "t [с]")
plot(V_graph, S_graph)
Out[0]:
No description has been provided for this image

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

Результаты моделирования - ПЛК

Получим и проредим сигналы, записанные в модели на входе и выходе ПЛК:

In [ ]:
start_stop = thin(get_sim_data(simout, "старт/стоп двигаталя", 1, "value"), 100);
rem = thin(get_sim_data(simout, "Программируемый логический контроллер.rem", 1, "value"), 100);
pos = map(p -> p[1], thin(get_sim_data(simout, "позиции", 1, "value"), 100)) .>= L;

Построим графики этих переменных:

In [ ]:
plot(t, start_stop; label = "Старт/стоп", width = 2, linestyle=:dot,
        title = "Сигналы контроллера", xlabel = "t [с]", legend = :outerright,
        yticks=([0, 1], ["False", "True"]), st=:steppost)
plot!(t, pos; label = "Груз на месте", width = 1)
plot!(t, rem; label = "Удалить", width = 1)
Out[0]:
No description has been provided for this image

На контроллер поступает сигнал присутствия груза, контроллер на базе этого сигнала, согласно алгоритму, формирует сигналы воздействия на объект управления.

В завершение, перед этапом тестирования модели в системе с реальным ПЛК проверим работу модели в режиме реального времени.

Моделирование в реальном времени

Моделирование в реальном времени доступно благодаря интеграции Engee и машины реального времени РИТМ.

В ранних примерах уже было показано, как провести моделирование объекта управления с оценкой его выполнения в режиме жёсткого реального времени. Мы провели моделирование нашего объекта с системой управления в Engee на КПМ РИТМ, теперь оценим TET:

In [ ]:
Pkg.add(["StatsBase", "Printf"]) # установка необходимых пакетов
In [ ]:
import StatsBase.mean, Printf.@printf # импорт необходимых функции и макроса

В результате моделирования в реальном времени были собраны данные профилирования модели, записанные в дальнейшем в файл profile.txt. Добавим эти данные в рабочую область и проредим для экономии памяти.

In [ ]:
tet = TxtToVec("$(@__DIR__)/profile.txt");  # Получаем вектор значений из файла
tet = filter(t -> t>0, tet);                    # Удаляем артефакты
thintet = thin_vector(tet, 20, 0.2);            # Прореживаем вектор на 95% с отклонением до 20%

StepTime = 1e3; # шаг расчёта модели, мкс

Проанализируем данные профилирования.

In [ ]:
@printf "Количество точек профилирования: %d\n" length(tet)
@printf "Прореживание: %d --> %d (%.2f %%)\n\n" l1 = length(tet) l2 = length(thintet) (l1-l2)/l1*100

@printf "Минимальное TET:   %.3f мс\n" tet_min = (minimum(tet)/1e6)
@printf "Среднее TET:       %.3f мс\n" tet_mean = (mean(tet)/1e6)
@printf "Максимальное TET:  %.3f мс\n\n" tet_max = (maximum(tet)/1e6)

@printf "Число превышений TET:  %d (%.2f %%)" tet_exc = count(>(StepTime*1e3), tet) tet_exc/l1*100
Количество точек профилирования: 89999
Прореживание: 89999 --> 5330 (94.08 %)

Минимальное TET:   0.021 мс
Среднее TET:       0.029 мс
Максимальное TET:  48.076 мс

Число превышений TET:  14 (0.02 %)

При оценке TET выявлено, что в некоторые моменты происходит превышение времени выполнения задач до 480% относительно шага расчёта. Построим график изменения TET, проверим - где появляются такие броски.

In [ ]:
gr(format = :png, legend=:bottomright, title="Время выполнения модели\n на КПМ РИТМ",
    xlabel="Время модели, мс", ylabel="Время выполнения, мкс")


plot(thintet./1000;  label="TET", yaxis=:log, ylims=(10,1e5))
plot!([0, length(thintet)], [StepTime,  StepTime];  label="StepTime",   color=:green)
Out[0]:
No description has been provided for this image

Как видно из графика TET, значительные броски возникают в некоторые моменты событий в электрических цепях.

Для "боевого" применения модели данного примера потребуется доработать модель для соблюдения реального времени. Можно предпринять следующие шаги:

  • оптимизировать модель,

  • отрегулировать решатель,

  • увеличить вычислительные мощности машины,

  • обратиться к разработчикам блоков, вызывающих события для модернизации блоков.

Заключение

В этом примере мы рассмотрели использование Engee Function и Chart для разработки пользовательских блоков и систем управления. Провели моделирование конвейера с системами подачи груза и учета перемещения грузов различной массы, прототипом системы управления. В завершение провели моделирование в режиме реального времени и оценили время выполнения задач.