Генерация кода для МИК32 (Тестирование нечёткого регулятора)
В этом демонстрационном примере представлена разработка модели Engee для тестирования нечёткого регулятора на всём диапазоне входных сигналов с выводом выходного сигнала в ЦАП микроконтроллера MIK32V2.
Введение
Задача примера - протестировать работу нечёткого регулятора с двумя входами и одним выходом. Модель нечеткого регулятора собрана из отдельных блоков базовой библиотеки Engee для простоты ознакомления с функциональными особенностями регулятора. Целевое устройство, используемое в этом демонстрационном примере - отладочная плата MIK32 NUKE V0.3 на базе микроконтроллера K1948ВК018 MIK32 Amur. Компиляция и загрузка кода в микроконтроллер произведена из VS Code с расширением PlatformIO.
Описание модели
Общий вид модели приведён на рисунке ниже.

Она состоит из блоков задания двух входных, тестирующих сигналов, подсистемы нечёткого регулятора с двумя входами и одним выходом, подсистемы масштабирования выходного сигнала, и периферийных блоков МИК32 библиотеки HAL.
Входы и выходы регулятора
На входы регулятора подаются повторяющиеся переменные дискретные сигналы от блоков Repeating Sequence Stair:
# Входные сигналы:
x1_data = Vector(range(-2,2,105))
x2_data = Vector(range(-2,2,21))
# Шаг дискретизации:
Ts = 0.01
using Plots
gr()
plot([x1_data,x2_data]; label=["x1_data" "x2_data"],
st=:step, title = "Входные сигналы", aspect_ratio=10)
Такое задание входных сигналов позволяет на всём диапазоне значений одного и второго сигналов проверить результаты вычислений выходного сигнала регулятора.
Работа подсистемы scaling
заключается в изменении диапазона значений и типа данных выходного сигнала регулятора: :

Сигнал ограничивается от 0 до 4095, так как ЦАП МИК32 имеют 12-битовую разрядность, формат данных - целочисленный, беззнаковый, 2 байта.
Структура нечёткого регулятора
Нечёткий регулятор построен по алгоритму Мамдани, состоит из идентичных блоков фаззификации и агрегирования, блока активизации (функция min), блока аккумулирования (функция max), блока дефаззификации (метод центра тяжести для одноточечных множеств, COGS).

Число лингвистических терм входных сигналов - 3, выходного сигнала - 3.
Фаззификация и агрегирование
У входных переменных три лингвистических терма, принимающих ненулевые значения истинности в диапазонах - "НИЗКОЕ": , "СРЕДНЕЕ": , "ВЫСОКОЕ": . На этапе агрегирования ко входным сигналам применяются треугольные функции принадлежности (ФП), симметричные относительно нуля. В подсистемах fuzzyfication
ФП воспроизводятся блоками 1-D Lookup Table. Ниже приводится расчёт ФП для терм входных переменных.
# ФП для x1
μ_S_x1 = -x1_data;
μ_M_x1 = 1.0.-abs.(x1_data);
μ_L_x1 = copy(x1_data);
μ_x1 = [μ_S_x1, μ_M_x1, μ_L_x1];
# ФП для x2
μ_S_x2 = -x2_data;
μ_M_x2 = 1.0.-abs.(x2_data);
μ_L_x2 = copy(x2_data);
μ_x2 = [μ_S_x2, μ_M_x2, μ_L_x2];
термы = ["низкое" "среднее" "высокое"];
Функция насыщения входных сигналов:
function насыщение(μ)
for i in 1:length(μ)
if μ[i] > 1
μ[i] = 1.0
end
if μ[i] < 0
μ[i] = 0.0
end
end
end;
Применение насыщения к термам, построение функций принадлежности:
for i in 1:3
насыщение(μ_x1[i])
end
for i in 1:3
насыщение(μ_x2[i])
end
график1 = plot(x1_data, μ_x1; title = "ФП для x1", label = термы, xlabel = "x1")
график2 = plot(x2_data, μ_x2; title = "ФП для x2", label = :none, xlabel = "x2")
plot(график1, график2;
ylabel = "Степень принадлежености, μ", legend = :right, aspect_ratio=3)
Активизация
На этапе активизации в подсистеме activation
определяются подзаключения для всех комбинаций термов. Активизация осуществляется функцией минимума:
Аккумулирование
В подсистеме accumulation
воспроизводится следующая база правил:
- ЕСЛИ ( "НИЗКОЕ" И ( "НИЗКОЕ" ИЛИ "СРЕДНЕЕ")) ТО "НИЗКОЕ"
- ЕСЛИ ( "НИЗКОЕ" И "ВЫСОКОЕ") ИЛИ
( "СРЕДНЕЕ" И ( "НИЗКОЕ" ИЛИ "СРЕДНЕЕ")) ИЛИ
( "ВЫСОКОЕ" И "НИЗКОЕ") ТО "СРЕДНЕЕ" - ЕСЛИ ( "СРЕДНЕЕ" И "ВЫСОКОЕ") ИЛИ
( "ВЫСОКОЕ" И ( "СРЕДНЕЕ" ИЛИ "ВЫСОКОЕ")) ТО "ВЫСОКОЕ"
Для наглядности представим её в виде таблицы:
колонки = ["низкое", "среднее", "высокое"];
строки = copy(колонки);
база_правил = [
"низкое" "среднее" "среднее";
"низкое" "среднее" "высокое";
"среднее" "высокое" "высокое"
];
(n,m) = size(база_правил)
heatmap(база_правил, fc = cgrad([:blue; :red; :green]), leg=false, xticks=(1:3,колонки), yticks=(1:3,строки))
title!("База правил")
xlabel!("Термы x1")
ylabel!("Термы x2")
annotate!( [(j, i, база_правил[i,j]) for i in 1:n for j in 1:m])
vline!(0.5:(n+0.5), c=:black)
hline!(0.5:(m+0.5), c=:black)
Степени истинности выходных термов определяются в функции максимума из подзаключений согласно выражениям:
Дефаззификация
В подсистеме defuzzyfication
реализован метод центра тяжести для одноточечных множеств (COGS). Средние точки для функций принадлежности выходных термов: . Расчёт реализован без приведения к сумме степеней принадлежности ( ) и с приведением ( ). Для моделирования и тестирования используем далее первый способ.

Результаты моделирования
Загрузим и выполним модель примера:
# @markdown **Программное управление моделированием:**
# @markdown Требуется ввести только имя модели
имя_модели = "mik32_fuzzy_reg" # @param {type:"string"}
if имя_модели in [m.name for m in engee.get_all_models()]
модель = engee.open( имя_модели );
else
модель = engee.load( "$(@__DIR__)/"*имя_модели*".engee" );
end
данные = engee.run(модель);
Построим графики выходной переменной
plot(данные["dac_val"].time, данные["dac_val"].value;
label="y", title="Выходной сигнал", xlims=(0,1.1))
Этот сигнал в программе, загружаемой в контроллер, и будет передаваться на цифро-аналоговый преобразователь.
Периферия контроллера в модели реализована при помощи блоков C Function, как это было реализовано в ранних примерах с ЦАП для МИК32: пилообразный сигнал, логотип Engee.
Генерация кода и сборка проекта
# @markdown **Генерация кода:**
# @markdown Папка для результатов генерации кода будет создана в папке скрипта:
папка = "code" # @param {type:"string"}
# @markdown Генерация кода для подсистемы:
включить = false # @param {type:"boolean"}
if(включить)
подсистема = "" # @param {type:"string"}
engee.generate_code( "$(@__DIR__)/"*имя_модели*".engee", "$(@__DIR__)/"*папка;
subsystem_name = подсистема)
else
engee.generate_code( "$(@__DIR__)/"*имя_модели*".engee", "$(@__DIR__)/"*папка)
end
Проект с моделью собирается в IDE VS Code+PlatformIO, процесс сборки аналогичен процессу из примера МИК32: Генератор пилообразных сигналов.
Единственное исключение составляет то, что скомпилированная программа загружается не в RAM контроллера, а во flash-память по интерфейсу SPIFI. Для конфигурирования PlatformIO в примере приложен файл конфигурации platformio.ini
. Перейдём к выполнению кода на микроконтроллере.
Выполнение кода на MIK 32
После успешной сборки и компиляции проекта подключим к каналу P1.12 микроконтроллера осциллограф и отобразим осциллограмму.

Как видно из осциллограммы, в канале ЦАП воспроизводится форма смоделированного выходного сигнала тестируемого нечёткого регулятора.
Заключение
В этом примере мы рассмотрели структуру нечёткого регулятора в модели Engee, промоделировали его тестирование и провели тестирование регулятора на микроконтроллере МИК32 Amur после генерации кода из модели. Результаты моделирования и тестирования на контроллере идентичны и соответствуют заданному алгоритму.