Документация Engee
Notebook

Генерация кода для МИК32 (Тестирование нечёткого регулятора)

В этом демонстрационном примере представлена разработка модели Engee для тестирования нечёткого регулятора на всём диапазоне входных сигналов с выводом выходного сигнала в ЦАП микроконтроллера MIK32V2.

Введение

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

Описание модели

Общий вид модели приведён на рисунке ниже.

mik32_fuzzy_model.png

Она состоит из блоков задания двух входных, тестирующих сигналов, подсистемы нечёткого регулятора с двумя входами и одним выходом, подсистемы масштабирования выходного сигнала, и периферийных блоков МИК32 библиотеки HAL.

Входы и выходы регулятора

На входы регулятора подаются повторяющиеся переменные дискретные сигналы от блоков Repeating Sequence Stair:

In [ ]:
# Входные сигналы:
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)
Out[0]:
No description has been provided for this image

Такое задание входных сигналов позволяет на всём диапазоне значений одного и второго сигналов проверить результаты вычислений выходного сигнала регулятора.

Работа подсистемы scaling заключается в изменении диапазона значений и типа данных выходного сигнала регулятора: $dac\_val = 2048\cdot (y+1)$:

mik32_output_model.png

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

Структура нечёткого регулятора

Нечёткий регулятор построен по алгоритму Мамдани, состоит из идентичных блоков фаззификации и агрегирования, блока активизации (функция min), блока аккумулирования (функция max), блока дефаззификации (метод центра тяжести для одноточечных множеств, COGS).

mik32_fuzzy_controller_model_2.png

Число лингвистических терм входных сигналов - 3, выходного сигнала - 3.

Фаззификация и агрегирование

У входных переменных три лингвистических терма, принимающих ненулевые значения истинности в диапазонах - "НИЗКОЕ": $x_S \in(-\infty;\ 0)$, "СРЕДНЕЕ": $x_M \in(-1;\ 1)$, "ВЫСОКОЕ": $x_L \in(0;\ \infty)$. На этапе агрегирования ко входным сигналам применяются треугольные функции принадлежности (ФП), симметричные относительно нуля. В подсистемах fuzzyfication ФП воспроизводятся блоками 1-D Lookup Table. Ниже приводится расчёт ФП для терм входных переменных.

In [ ]:
# ФП для 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];

термы = ["низкое" "среднее" "высокое"];

Функция насыщения входных сигналов:

In [ ]:
function насыщение(μ)
    for i in 1:length(μ) 
        if μ[i] > 1
            μ[i] = 1.0
        end
        if μ[i] < 0
            μ[i] = 0.0
        end
    end 
end;

Применение насыщения к термам, построение функций принадлежности:

In [ ]:
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)
Out[0]:
No description has been provided for this image

Активизация

На этапе активизации в подсистеме activation определяются подзаключения для всех комбинаций термов. Активизация осуществляется функцией минимума:

$$\mu_{SS} = \min(\mu_{1S},\mu_{2S}),\ \mu_{SM} = \min(\mu_{1S},\mu_{2M}),\ \mu_{SL} = \min(\mu_{1S},\mu_{2L})$$ $$\mu_{MS} = \min(\mu_{1M},\mu_{2S}),\ \mu_{MM} = \min(\mu_{1M},\mu_{2M}),\ \mu_{ML} = \min(\mu_{1M},\mu_{2L})$$ $$\mu_{LS} = \min(\mu_{1L},\mu_{2S}),\ \mu_{LM} = \min(\mu_{1L},\mu_{2M}),\ \mu_{LL} = \min(\mu_{1L},\mu_{2L})$$

Аккумулирование

В подсистеме accumulation воспроизводится следующая база правил:

  1. ЕСЛИ ($x_{1}=$ "НИЗКОЕ" И ($x_{2}=$ "НИЗКОЕ" ИЛИ $x_{2}=$ "СРЕДНЕЕ")) ТО $y =$ "НИЗКОЕ"
  2. ЕСЛИ ($x_{1}=$ "НИЗКОЕ" И $x_{2}=$ "ВЫСОКОЕ") ИЛИ
    ($x_{1}=$ "СРЕДНЕЕ" И ($x_{2}=$ "НИЗКОЕ" ИЛИ $x_{2}=$ "СРЕДНЕЕ")) ИЛИ
    ($x_{1}=$ "ВЫСОКОЕ" И $x_{2}=$ "НИЗКОЕ") ТО $y =$ "СРЕДНЕЕ"
  3. ЕСЛИ ($x_{1}=$ "СРЕДНЕЕ" И $x_{2}=$ "ВЫСОКОЕ") ИЛИ
    ($x_{1}=$ "ВЫСОКОЕ" И ($x_{2}=$ "СРЕДНЕЕ" ИЛИ $x_{2}=$ "ВЫСОКОЕ")) ТО $y =$ "ВЫСОКОЕ"

Для наглядности представим её в виде таблицы:

In [ ]:
колонки = ["низкое", "среднее", "высокое"];
строки = 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)
Out[0]:

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

$$\mu_{y_S} = \max(\mu_{SS},\ \mu_{SM}),$$ $$\mu_{y_M} = \max(\mu_{SL},\ \mu_{MS},\ \mu_{MM},\ \mu_{LS}),$$ $$\mu_{y_L} = \max(\mu_{ML},\ \mu_{LM},\ \mu_{LL})$$

Дефаззификация

В подсистеме defuzzyfication реализован метод центра тяжести для одноточечных множеств (COGS). Средние точки для функций принадлежности выходных термов: $y_S= -1;\ y_M= 0;\ y_L= 1$. Расчёт реализован без приведения к сумме степеней принадлежности ($y$ ) и с приведением ($y_1$ ). Для моделирования и тестирования используем далее первый способ.

mik32_defuzzy_model.png

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

Загрузим и выполним модель примера:

In [ ]:
# @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(модель);

Построим графики выходной переменной

In [ ]:
plot(данные["dac_val"].time, данные["dac_val"].value;
     label="y", title="Выходной сигнал", xlims=(0,1.1))
Out[0]:

Этот сигнал в программе, загружаемой в контроллер, и будет передаваться на цифро-аналоговый преобразователь.

Периферия контроллера в модели реализована при помощи блоков C Function, как это было реализовано в ранних примерах с ЦАП для МИК32: пилообразный сигнал, логотип Engee.

Генерация кода и сборка проекта

In [ ]:
# @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
[ Info: Generated code and artifacts: /user/start/examples/codegen/mik32_fuzzy/code

Проект с моделью собирается в IDE VS Code+PlatformIO, процесс сборки аналогичен процессу из примера МИК32: Генератор пилообразных сигналов.
Единственное исключение составляет то, что скомпилированная программа загружается не в RAM контроллера, а во flash-память по интерфейсу SPIFI. Для конфигурирования PlatformIO в примере приложен файл конфигурации platformio.ini. Перейдём к выполнению кода на микроконтроллере.

Выполнение кода на MIK 32

После успешной сборки и компиляции проекта подключим к каналу P1.12 микроконтроллера осциллограф и отобразим осциллограмму.

mik32_fuzzy_results.gif

Как видно из осциллограммы, в канале ЦАП воспроизводится форма смоделированного выходного сигнала тестируемого нечёткого регулятора.

Заключение

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

Блоки, использованные в примере