Engee documentation
Notebook

Code generation for MIC32 (Fuzzy controller testing)

This demo presents the development of the Engee model for testing a fuzzy controller over the full range of input signals with output to the DAC of a MIK32V2 microcontroller.

Introduction

The purpose of the example is to test the operation of a fuzzy regulator with two inputs and one output. The fuzzy regulator model is built from the individual blocks of the Engee basic library for easy familiarisation with the functionality of the regulator. The target device used in this demo is a MIK32 NUKE V0.3 debug board based on K1948VK018 MIK32 Amur microcontroller. Compilation and loading of the code into the microcontroller is done from VS Code with PlatformIO extension.

Model description

The general view of the model is shown in the figure below.

mik32_fuzzy_model.png

It consists of two input setting blocks, test signals, a fuzzy controller subsystem with two inputs and one output, an output signal scaling subsystem, and MIC32 peripheral blocks of the HAL library.

Controller inputs and outputs

Repeating variable binary signals from the [Repeating Sequence Stair] units (https://engee.com/helpcenter/stable/ru/base-lib-sources/repeating-sequence-stair.html) are input to the controller inputs:

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

This setting of the input signals makes it possible to check the results of the controller output calculations over the entire value range of one and the other signal.

The work of the subsystem scaling consists in changing the range of values and data type of the regulator output signal: $dac\_val = 2048\cdot (y+1)$:

mik32_output_model.png

The signal is limited from 0 to 4095 because the MIC32 DACs are 12-bit, the data format is integer, unsigned, 2 bytes.

Structure of the fuzzy controller

The fuzzy controller is based on Mamdani's algorithm and consists of identical phasification and aggregation blocks, activation block (min function), accumulation block (max function), defuzzification block (centre of gravity method for one-point sets, COGS).

mik32_fuzzy_controller_model_2.png

The number of linguistic terms of the input signals is 3 and the output signal is 3.

Phasification and aggregation

The input variables have three linguistic terms taking non-zero truth values in the ranges - "LOW": $x_S \in(-\infty;\ 0)$, "MEDIUM": $x_M \in(-1;\ 1)$, "HIGH": $x_L \in(0;\ \infty)$. At the aggregation stage, triangular membership functions (FPs) symmetric with respect to zero are applied to the input signals. In the fuzzyfication subsystems, the FPs are reproduced by 1-D Lookup Table blocks. The calculation of TPs for the input variables terms is given below.

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];

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

Saturation function of input signals:

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

Application of saturation to terms, construction of membership functions:

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

In the activation step, subconclusions for all combinations of terms are defined in the subsystem activation. Activation is performed by the minimum function:

$$\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

The following rule base is reproduced in the accumulation subsystem:

  1. IF ($x_{1}=$ "LOW" AND ($x_{2}=$ "LOW" OR $x_{2}=$ "MEDIUM")) THEN $y =$ "LOW".
  2. IF ($x_{1}=$ "LOW" AND $x_{2}=$ "HIGH") OR
    ($x_{1}=$ "MEDIUM" AND ($x_{2}=$ "LOW" OR $x_{2}=$ "MEDIUM") )) OR
    ($x_{1}=$ "HIGH" AND $x_{2}=$ "LOW") TO $y =$ "MEDIUM".
  3. ** IF** ($x_{1}=$ "MEDIUM" AND $x_{2}=$ "HIGH") OR
    ($x_{1}=$ "HIGH" AND ($x_{2}=$ "MEDIUM" OR $x_{2}=$ "HIGH")) TO $y =$ "HIGH"

For clarity, let's present it in the form of a table:

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

The degrees of truth of the output terms are defined in the maximum function of the subconclusions according to the expressions:

$$\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})$$

Defuzzification

The subsystem defuzzyfication implements the centre of gravity method for one-point sets (COGS). Midpoints for the membership functions of the output terms: $y_S= -1;\ y_M= 0;\ y_L= 1$. The calculation is realised without and with ($y$ ) and with ($y_1$ ). For modelling and testing we use the first method.

mik32_defuzzy_model.png

Modelling results

Let's load and run the example model:

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(модель);

Plot the graphs of the output variable

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

This signal in the program loaded into the controller and will be sent to the digital to analogue converter.

The periphery of the controller in the model is implemented using C Function blocks, as it was implemented in the early DAC examples for MIC32: sawtooth signal, Engee logo.

Code generation and project assembly

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

The project with the model is built in IDE VS Code+PlatformIO, the process of building is similar to the process from the example MIC32: Sawtooth Signal Generator.
The only exception is that the compiled programme is loaded not into the RAM of the controller, but into flash-memory via SPIFI interface. To configure PlatformIO in the example, the configuration file platformio.ini is attached. Let's move on to the execution of the code on the microcontroller.

Code execution on MIK 32

After successful assembly and compilation of the project, connect an oscilloscope to channel P1.12 of the microcontroller and display the oscillogram.

mik32_fuzzy_results.gif

As can be seen from the oscillogram, the DAC channel reproduces the shape of the modelled output signal of the fuzzy controller under test.

Conclusion

In this example we have considered the structure of the fuzzy controller in the Engee model, simulated its testing and tested the controller on the MIC32 Amur microcontroller after code generation from the model. The results of the modelling and testing on the controller are identical and consistent with the specified algorithm.