Engee 文档
Notebook

使用 C 功能块将 C 代码集成到 Engee 模型中

本例展示了使用C 功能块将 C 代码集成到恩吉模型中的不同方法。

在开始之前,我们先连接必要的库:

In [ ]:
using Plots
using MATLAB
plotlyjs();

使用宏@__DIR__ 查找交互式脚本所在的文件夹:

In [ ]:
demoroot = @__DIR__
Out[0]:
"/user/start/examples/base_simulation/ccodeintegration"

使用 C 功能块的基础知识

让我们来看一些使用 C 功能块的简单示例,以说明将 C 代码集成到 **Engee ** 模型中的基础知识。

1.处理多维信号、传递参数和使用 #define 指令

在模型cCodeIntegration_basics.engee 中,我们添加了C 函数块,输入正弦信号in

basics_1.PNG

假设我们要将正弦波in 的振幅增加一个系数gain ,并将其移位一个值bias 。也就是说,输出信号应由公式out = gain * in + bias 确定。

让我们进入C 功能块的设置,在Ports 标签中,我们将熟悉块输入和输出的定义。在这里,源代码变量与 **C 功能块的端口相连:

1.将double 类型的标量变量x 与第一个输入端口in 绑定; 2.类型为double 的矢量(3 个元素)变量y 与第一个输出端口out 绑定。

image_2.png

gain 数组也在 C 功能块(Parameters 标签)的设置中定义:

image.png

值得注意的是,变量一旦作为参数传递,就会变成 1.全局变量 -gain 可以从源代码编辑器的任何选项卡中访问; 2.带有限定符const - 尝试更改gain 的值时会出现错误。

偏移值bias 由标识符#define bias 决定,该标识符在选项卡Build options 中设置:

image.png

在本例中,偏移量为5

让我们进入 C 功能块设置的Main ,点击 "编辑源代码 "按钮。在打开的源代码编辑器OutputCode 标签中,给出了模型计算每一步执行的代码:

image.png

在环路for 中(针对三个输出信号中的每一个信号),在模型计算的每一步都要确定变量y 的值。

让我们来模拟一下系统:

In [ ]:
cCodeIntegration_basics = engee.load("$demoroot/models/cCodeIntegration_basics.engee", force = true)
simulationResults_basics = engee.run(cCodeIntegration_basics)
Out[0]:
Dict{String, DataFrame} with 1 entry:
  "out" => 1001×2 DataFrame

关闭模型

In [ ]:
engee.close(cCodeIntegration_basics, force = true);

导入模拟结果

In [ ]:
cCodeIntegration_basics_t = simulationResults_basics["out"].time;

cCodeIntegration_basics_y1 = [y[1] for y in simulationResults_basics["out"].value];
cCodeIntegration_basics_y2 = [y[2] for y in simulationResults_basics["out"].value];
cCodeIntegration_basics_y3 = [y[3] for y in simulationResults_basics["out"].value];

绘制图表

In [ ]:
plot(cCodeIntegration_basics_t, cCodeIntegration_basics_y1)
plot!(cCodeIntegration_basics_t, cCodeIntegration_basics_y2)
plot!(cCodeIntegration_basics_t, cCodeIntegration_basics_y3)

title!("Выходной сигнал блока C Function <br> (Многомерные сигналы, параметры и #define)")
xlabel!("Время, [с]")
ylabel!("Амплитуда")
Out[0]:

2.使用整数数据类型和静态变量

在模型cCodeIntegration_integers.engee 中,我们使用C 函数块实现了一个简单的计数器,其输出是一个表示当前迭代次数的正整数:

ccodeintegration_integers_1.PNG

让我们分析一下 C 功能块设置中PortsBuild Options 选项卡的内容:

image.png

image.png

C 语言标准库的头文件<stdint.h> 定义了C 函数块中整数数据类型的名称。该文件连接在Headers 行的选项卡Build options 中。

选项卡Ports 将源代码中的一个不带符号的 64 位变量out (类型设置为uint64_t ,而不是long ,参见前一段关于<stdint.h> 的内容)绑定到 C 功能块的第一个输出端口。

C 功能块源代码编辑器的Output code 选项卡中,包含了仿真过程中每一步执行的代码:

image.png

在模拟的第一步,静态变量counter 初始化为 0,之后每次迭代,其值都会以1 的形式递增,并分配给变量out

让我们对系统进行模拟:

In [ ]:
cCodeIntegration_integers = engee.load("$demoroot/models/cCodeIntegration_integers.engee", force = true)
simulationResults_integers = engee.run(cCodeIntegration_integers)
Out[0]:
Dict{String, DataFrame} with 1 entry:
  "counter" => 1001×2 DataFrame

关闭模型

In [ ]:
engee.close(cCodeIntegration_integers, force = true);

导入模拟结果

In [ ]:
cCodeIntegration_integers_t = simulationResults_integers["counter"].time;
cCodeIntegration_integers_y = simulationResults_integers["counter"].value;

绘制图表

In [ ]:
plot(cCodeIntegration_integers_t, cCodeIntegration_integers_y, legend = false)

title!("Выходной сигнал блока C Function <br> (Целочисленные типы и статические переменные)")
xlabel!("Время, [с]")
ylabel!("Номер итерации")
Out[0]:

有关源代码编辑器选项卡的用途、绘图选项和可用数据类型的详细信息,请参阅 文档Engee.

整合外部源代码

任务说明

让我们考虑一个更有趣的工程问题。

假设我们需要控制一个稳定的二阶对象,在测量噪声的作用下,该对象会受到外部干扰。因此,我们需要解决对控制对象的输出信号进行滤波的问题,幸运的是,Engee有一个非常适合的工具!

alphabetafilter.calphabetafilter.h 文件提供了[alpha-beta 滤波器]的 C 语言实现(https://en.wikipedia.org/wiki/Alpha_beta_filter)。

文件alphabetafilter.c

#include "alphabetafilter.h"
#include <math.h>

double dt = 0.01
static double xk_1, vk_1

参数 determineAlphaBeta(double processNoiseVariance, double measurementNoiseVariance)
{
     lambda, r
    参数 p

    lambda = (processNoiseVariance * pow(dt, 2)) / measurementNoiseVariance
    r = (4 + lambda - sqrt(8 * lambda + pow(lambda, 2)))/ 4;

    p.alpha = 1 - pow(r, 2)
    p.beta = 2 * (2 - p.alpha) - 4 * sqrt(1 - p.alpha)

    返回 p
}

double alphaBetaFilter(double value, Parameters p)
{
    double xk, vk, rk

    xk = xk_1 + (vk_1 * dt)
    vk = vk_1

    rk =  - xk

    xk += p.alpha * rk
    vk += (p.beta * rk) / dt

    xk_1 = xk
    vk_1 = vk

    return xk
}

void initialiseStaticVariables()
{
    xk_1 = 0
    vk_1 = 0
}

文件alphabetafilter.h 的内容:

typedef struct {
    double alpha
    double beta
} 参数

参数 determineAlphaBetadouble, double);
double alphaBetaFilter(double, Parameters)
void initialiseStaticVariables()

将这些源代码整合到我们的 Engee 模型中。

分析模型

模型cCodeIntegration_source.engee

source_1.PNG

该模型包括

控制对象Plant ; 2.实现二阶 PID 控制器的子系统Controller ; 3. 限带白噪声ProcessNoiseMeasurementNoise ,模拟外部影响和测量噪声; 4.块 C 功能Filter ,实现阿尔法-贝塔滤波器。

程序块Filter 的参数Sample Time 等于模型求解器Ts 的步长:

image.png

让我们来分析一下Filter 区块设置中Build options 标签的内容:

image.png

在该选项卡的行中:

*Source files - 连接的源代码文件alphabetafilter.c ; *Iinclude directories - 设置包含头文件的文件夹路径; *Headers - 连接的头文件alphabetafilter.h

在程序块设置的Ports 标签中,源代码变量与程序块的输入和输出端口相连Filter

Filter 程序块的源代码中,OutputCode 标签包含循环可执行代码:

image.png

在模型计算的每一步中,函数determineAlphaBeta()alphabetafilter() 被依次调用,之后滤波值被存储在变量out 中,与模块Filter 的输出端口x 相对应。

模拟开始

让我们模拟系统:

In [ ]:
cCodeIntegration_source = engee.load("$demoroot/models/cCodeIntegration_source.engee", force = true)
simulationResults_source = engee.run(cCodeIntegration_source)
Out[0]:
Dict{String, DataFrame} with 2 entries:
  "filtered" => 6001×2 DataFrame…
  "noisy"    => 6001×2 DataFrame

关闭模型

In [ ]:
engee.close(cCodeIntegration_source, force = true);

模拟结果

导入模拟结果:

In [ ]:
cCodeIntegration_source_noisy_t = simulationResults_source["noisy"].time;
cCodeIntegration_source_noisy_y = simulationResults_source["noisy"].value;
In [ ]:
cCodeIntegration_source_filtered_t = simulationResults_source["filtered"].time;
cCodeIntegration_source_filtered_y = simulationResults_source["filtered"].value;

绘制图表

In [ ]:
plot(cCodeIntegration_source_noisy_t, cCodeIntegration_source_noisy_y, label = "Исходный сигнал")
plot!(cCodeIntegration_source_filtered_t, cCodeIntegration_source_filtered_y, label = "Отфильтрованный сигнал")
plot!(legend = :bottomright)

title!("Альфа-бета фильтр <br> (Интеграция внешнего исходного кода)")
xlabel!("Время, [с]")
ylabel!("Амплитуда")
Out[0]:

整合静态和动态图书馆

任务说明

源代码通常不是明确提供的,而是作为静态或动态库的一部分。C API 库与外部源代码一样,可轻松集成到Engee模型中!

让我们使用MakeFile 从文件alphabetafilter.calphabetafilter.h 编译一个静态 (alphabetafilter.a) 和动态 (alphabetafilter.so) 库。

文件Makefile 的内容:

all: alphabetafilter.a alphabetafilter.so

alphabetafilter.o: alphabetafilter.c
	$(CC) -c -fPIC $^ -o$@

alphabetafilter.a: alphabetafilter.o
	ar rcs $@$^

alphabetafilter.so: alphabetafilter.o
	$(CC) -shared$^ -o $@
    
清除:
	rm -f *.o *.a *.so

让我们开始编译库:

In [ ]:
run(`make -C $demoroot/source`)
make: Entering directory '/user/start/examples/base_simulation/ccodeintegration/source'
cc -c -fPIC alphabetafilter.c -o alphabetafilter.o
ar rcs alphabetafilter.a alphabetafilter.o
cc -shared alphabetafilter.o -o alphabetafilter.so
make: Leaving directory '/user/start/examples/base_simulation/ccodeintegration/source'
Out[0]:
Process(`make -C /user/start/examples/base_simulation/ccodeintegration/source`, ProcessExited(0))

将静态库和动态库整合到我们的 Engee 模型中。

静态库:分析模型

从结构上看,模型cCodeIntegration_library_static.engee 与之前考虑的cCodeIntegration_source.engee 没有区别;只有Filter 块设置的Build options 选项卡上的指令不同。让我们来分析一下它的内容:

image.png

这里定义了以下结构选项:

*Include directories - 指定包含头文件的文件夹路径; *Library directories - 设置包含静态库的文件夹路径; *Headers - 连接头文件alphabetafilter.h ; *Libraries - 连接静态库alphabetafilter.a

静态库:开始模拟

让我们模拟系统:

In [ ]:
cCodeIntegration_library_static = engee.load("$demoroot/models/cCodeIntegration_library_static.engee", force = true)
simulationResults_library_static = engee.run(cCodeIntegration_library_static)
Out[0]:
Dict{String, DataFrame} with 2 entries:
  "filtered" => 6001×2 DataFrame…
  "noisy"    => 6001×2 DataFrame

关闭模型

In [ ]:
engee.close(cCodeIntegration_library_static, force = true);

静态库:建模结果

导入模拟结果

In [ ]:
cCodeIntegration_library_static_noisy_t = simulationResults_library_static["noisy"].time;
cCodeIntegration_library_static_noisy_y = simulationResults_library_static["noisy"].value;
In [ ]:
cCodeIntegration_library_static_filtered_t = simulationResults_library_static["filtered"].time;
cCodeIntegration_library_static_filtered_y = simulationResults_library_static["filtered"].value;

绘制图表

In [ ]:
plot(cCodeIntegration_library_static_noisy_t, cCodeIntegration_library_static_noisy_y, label = "Исходный сигнал")
plot!(cCodeIntegration_library_static_filtered_t, cCodeIntegration_library_static_filtered_y, label = "Отфильтрованный сигнал")
plot!(legend = :bottomright)

title!("Альфа-бета фильтр <br> (Интеграция статической библиотеки)")
xlabel!("Время, [с]")
ylabel!("Амплитуда")
Out[0]:

动态库:模型分析

从结构上看,模型cCodeIntegration_library_dynamic.engee 与之前的cCodeIntegration_source.engee 没有区别;只有Filter 块设置的Build options 选项卡上的指令不同。让我们来分析一下它的内容:

image.png

这里定义了以下结构选项:

*Include directories - 指定包含头文件的文件夹路径; *Library directories - 设置包含动态链接库的文件夹路径; *Headers - 连接头文件alphabetafilter.h ; *Libraries - 连接的动态链接库alphabetafilter.so

Filter 块源代码编辑器的开始代码选项卡中,函数initializeStaticVariables() 被调用:

dynamic_2.PNG

函数initializeStaticVariables() 在文件alphabetafilter.h 中声明,用于初始化动态链接库中使用的静态变量xk_1vk_1 。这是必要的,这样在重新启动仿真时,变量不会保存上次计算的值,而是重置为初始值 (在本例中为0 )。

动态库:运行模拟

让我们模拟一下系统:

In [ ]:
cCodeIntegration_library_dynamic = engee.load("$demoroot/models/cCodeIntegration_library_dynamic.engee", force = true)
simulationResults_library_dynamic = engee.run(cCodeIntegration_library_dynamic)
Out[0]:
Dict{String, DataFrame} with 2 entries:
  "filtered" => 6001×2 DataFrame…
  "noisy"    => 6001×2 DataFrame

关闭模型

In [ ]:
engee.close(cCodeIntegration_library_dynamic, force = true);

动态图书馆:建模结果

导入模拟结果

In [ ]:
cCodeIntegration_library_dynamic_noisy_t = simulationResults_library_dynamic["noisy"].time;
cCodeIntegration_library_dynamic_noisy_y = simulationResults_library_dynamic["noisy"].value;
In [ ]:
cCodeIntegration_library_dynamic_filtered_t = simulationResults_library_dynamic["filtered"].time;
cCodeIntegration_library_dynamic_filtered_y = simulationResults_library_dynamic["filtered"].value;

绘制图表

In [ ]:
plot(cCodeIntegration_library_dynamic_noisy_t, cCodeIntegration_library_dynamic_noisy_y, label = "Исходный сигнал")
plot!(cCodeIntegration_library_dynamic_filtered_t, cCodeIntegration_library_dynamic_filtered_y, label = "Отфильтрованный сигнал")
plot!(legend = :bottomright)

title!("Альфа-бета фильтр <br> (Интеграция динамической библиотеки)")
xlabel!("Время, [с]")
ylabel!("Амплитуда")
Out[0]:

问题陈述

让我们从 Simulink 模型alphabetafilter.slx 生成 C 代码,并将其集成到我们的 Engee 模型cCodeIntegration_cg_simulink.engee 中。

实现阿尔法-贝塔滤波器的模型alphabetafilter.slx 是在Simulink中由简单模块构建的,由子系统determineAlphaBetafilterInputSignal 组成。

模型的顶层:

codegen_simulink_2.png

子系统determineAlphaBeta

codegen_simulink_3.png

子系统filterInputSignal

codegen_simulink_4.png

让我们使用Simulink 嵌入式编码器生成代码:

In [ ]:
cgPath = joinpath(demoroot,"cg/simulink")
cgModel = joinpath(demoroot,"cg/simulink/alphabetafilter")

mat"""
model = load_system($cgModel);
path = $cgPath;

set_param(0, 'CacheFolder', path)
set_param(0, 'CodeGenFolder', path)

slbuild(model)

set_param(0, 'CacheFolder', '')
set_param(0, 'CodeGenFolder', '')
"""
>> >> >> >> >> >> >> >> >> ### Starting build procedure for: alphabetafilter
### Generating code and artifacts to 'Model specific' folder structure
### Generating code into build folder: /user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter_ert_rtw
### Invoking Target Language Compiler on alphabetafilter.rtw
### Using System Target File: /matlab/rtw/c/ert/ert.tlc
### Loading TLC function libraries
.......
### Generating TLC interface API for custom data
.
### Initial pass through model to cache user defined code
### Caching model source code
...................................
### Writing header file alphabetafilter_types.h
.
### Writing header file alphabetafilter.h
### Writing header file rtwtypes.h
### Writing source file alphabetafilter.c
### Writing header file alphabetafilter_private.h
### Writing source file alphabetafilter_data.c
.
### Writing source file ert_main.c
### TLC code generation complete (took 18.076s).
[Warning: P-code file /matlab/toolbox/coder/simulinkcoder_core/addStandardInfo.p
is older than source code file
/matlab/toolbox/coder/simulinkcoder_core/addStandardInfo.m.
/matlab/toolbox/coder/simulinkcoder_core/addStandardInfo.p may be obsolete and
may need to be regenerated.
Type "help pcode" for information about generating P-code.] 
### Saving binary information cache.
### Using toolchain: GNU gcc/g++ | gmake (64-bit Linux)
### Creating '/user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter_ert_rtw/alphabetafilter.mk' ...
### Building 'alphabetafilter': "/matlab/bin/glnxa64/gmake"  -f alphabetafilter.mk all
gcc -c -fwrapv -fPIC -O0 -msse2 -DCLASSIC_INTERFACE=0 -DALLOCATIONFCN=0 -DTERMFCN=1 -DONESTEPFCN=1 -DMAT_FILE=0 -DMULTI_INSTANCE_CODE=0 -DINTEGER_CODE=0 -DMT=0  -DTID01EQ=0 -DMODEL=alphabetafilter -DNUMST=1 -DNCSTATES=0 -DHAVESTDIO -DMODEL_HAS_DYNAMICALLY_LOADED_SFCNS=0 -I/user/start/examples/base_simulation/ccodeintegration/cg/simulink -I/user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter_ert_rtw -I/matlab/extern/include -I/matlab/simulink/include -I/matlab/rtw/c/src -I/matlab/rtw/c/src/ext_mode/common -I/matlab/rtw/c/ert -o "alphabetafilter.o" "/user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter_ert_rtw/alphabetafilter.c"
gcc -c -fwrapv -fPIC -O0 -msse2 -DCLASSIC_INTERFACE=0 -DALLOCATIONFCN=0 -DTERMFCN=1 -DONESTEPFCN=1 -DMAT_FILE=0 -DMULTI_INSTANCE_CODE=0 -DINTEGER_CODE=0 -DMT=0  -DTID01EQ=0 -DMODEL=alphabetafilter -DNUMST=1 -DNCSTATES=0 -DHAVESTDIO -DMODEL_HAS_DYNAMICALLY_LOADED_SFCNS=0 -I/user/start/examples/base_simulation/ccodeintegration/cg/simulink -I/user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter_ert_rtw -I/matlab/extern/include -I/matlab/simulink/include -I/matlab/rtw/c/src -I/matlab/rtw/c/src/ext_mode/common -I/matlab/rtw/c/ert -o "alphabetafilter_data.o" "/user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter_ert_rtw/alphabetafilter_data.c"
gcc -c -fwrapv -fPIC -O0 -msse2 -DCLASSIC_INTERFACE=0 -DALLOCATIONFCN=0 -DTERMFCN=1 -DONESTEPFCN=1 -DMAT_FILE=0 -DMULTI_INSTANCE_CODE=0 -DINTEGER_CODE=0 -DMT=0  -DTID01EQ=0 -DMODEL=alphabetafilter -DNUMST=1 -DNCSTATES=0 -DHAVESTDIO -DMODEL_HAS_DYNAMICALLY_LOADED_SFCNS=0 -I/user/start/examples/base_simulation/ccodeintegration/cg/simulink -I/user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter_ert_rtw -I/matlab/extern/include -I/matlab/simulink/include -I/matlab/rtw/c/src -I/matlab/rtw/c/src/ext_mode/common -I/matlab/rtw/c/ert -o "ert_main.o" "/user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter_ert_rtw/ert_main.c"
### Creating standalone executable ../alphabetafilter ...
g++  -o ../alphabetafilter alphabetafilter.o alphabetafilter_data.o ert_main.o  
### Created: ../alphabetafilter
### Successfully generated all binary outputs.
gmake: Nothing to be done for `all'.
### Successful completion of build procedure for: alphabetafilter
### Simulink cache artifacts for 'alphabetafilter' were created in '/user/start/examples/base_simulation/ccodeintegration/cg/simulink/alphabetafilter.slxc'.

Build Summary

Top model targets built:

Model            Action                        Rebuild Reason                                    
=================================================================================================
alphabetafilter  Code generated and compiled.  Code generation information file does not exist.  

1 of 1 models built (0 models already up to date)
Build duration: 0h 0m 46.583s

模型分析

从结构上看,模型cCodeIntegration_cg_simulink.engee 与之前考虑的cCodeIntegration_source.engee 并无不同;只有Filter 块设置的Build options 选项卡上的指令不同。让我们来分析一下它的内容:

image.png

这里定义了以下结构选项:

*Source files - 连接源代码文件alphabetafilter.calphabetafilter_data.c ; *Include directories - 设置包含头文件的文件夹路径; *Headers - 连接头文件alphabetafilter.halphabetafilter_types.hrtwtypes.h

可循环执行的代码位于Filter 块源代码编辑器的Output code 选项卡上:

image.png

模型的每一步都要进行计算:

  • 使用变量inprocessNoiseVariancemeasurementNoiseVariance (输入信号)对结构alphabetafilter_U 进行初始化;

  • 调用函数alphabetafilter_step()

  • 将存储在结构alphabetafilter_Y 中的结果x (输出信号)赋值给变量out

Filter 块源代码编辑器的开始代码选项卡中,函数alphabetafilter_initialize() 被调用:

codegen_simulink_6.png

Filter 程序块源代码编辑器的终止代码选项卡中,函数alphabetafilter_terminate() 被调用:

codegen_simulink_7.png

模拟开始

让我们模拟系统:

In [ ]:
cCodeIntegration_cg_simulink = engee.load("$demoroot/models/cCodeIntegration_cg_simulink.engee", force = true)
simulationResults_cg_simulink = engee.run(cCodeIntegration_cg_simulink)
Out[0]:
Dict{String, DataFrame} with 2 entries:
  "filtered" => 6001×2 DataFrame…
  "noisy"    => 6001×2 DataFrame

关闭模型

In [ ]:
engee.close(cCodeIntegration_cg_simulink, force = true);

模拟结果

导入模拟结果:

In [ ]:
cCodeIntegration_cg_simulink_noisy_t = simulationResults_cg_simulink["noisy"].time;
cCodeIntegration_cg_simulink_noisy_y = simulationResults_cg_simulink["noisy"].value;
In [ ]:
cCodeIntegration_cg_simulink_filtered_t = simulationResults_cg_simulink["filtered"].time;
cCodeIntegration_cg_simulink_filtered_y = simulationResults_cg_simulink["filtered"].value;

绘制图表

In [ ]:
plot(cCodeIntegration_cg_simulink_noisy_t, cCodeIntegration_cg_simulink_noisy_y, label = "Исходный сигнал")
plot!(cCodeIntegration_cg_simulink_filtered_t, cCodeIntegration_cg_simulink_filtered_y, label = "Отфильтрованный сигнал")
plot!(legend = :bottomright)

title!("Альфа-бета фильтр <br> (Интеграция кода, сгенерированного из модели Simulink)")
xlabel!("Время, [с]")
ylabel!("Амплитуда")
Out[0]:

整合 Engee 模型生成的代码

任务声明

让我们从模型 Engeealphabetafilter.engee 生成 C 代码,该代码由简单的块构建,实现了阿尔法-贝塔滤波器,并将其集成到我们的模型 EngeecCodeIntegration_cg_engee.engee 中。

模型的顶层:

codegen_engee_2.PNG

子系统determineAlphaBeta

codegen_engee_3.PNG

子系统filterInputSignal

codegen_engee_4.PNG

让我们使用 Engee 代码生成器生成代码:

In [ ]:
engee.generate_code("$demoroot/cg/engee/alphabetafilter.engee", "$demoroot/cg/engee/alphabetafilter_cg")
[ Info: Generated code and artifacts: /user/start/examples/base_simulation/ccodeintegration/cg/engee/alphabetafilter_cg

模型分析

从结构上看,模型cCodeIntegration_cg_engee.engee 与之前考虑的cCodeIntegration_source.engee 没有区别;只有Filter 块设置的Build options 选项卡上的指令不同。让我们来分析一下它的内容:

image.png

这里定义了以下结构选项:

*Source files - 连接源代码文件alphabetafilter.c ; *Include directories - 设置包含头文件的文件夹路径; *Headers - 连接的头文件alphabetafilter.h

可循环执行的代码位于Filter 块源代码编辑器的Output code 选项卡上:

image.png

模型的每一步都要进行计算:

  • 使用变量inprocessNoiseVariancemeasurementNoiseVariance (输入信号)对结构alphabetafilter_U 进行初始化;

  • 调用函数alphabetafilter_step()

  • 将存储在结构alphabetafilter_Y 中的结果x (输出信号)赋值给变量out

Filter 块源代码编辑器的开始代码选项卡中,函数alphabetafilter_init() 被调用:

codegen_engee_6.PNG

Filter 程序块源代码编辑器的终止代码选项卡中,函数alphabetafilter_term() 被调用:

codegen_engee_7.PNG

模拟开始

让我们模拟系统:

In [ ]:
cCodeIntegration_cg_engee = engee.load("$demoroot/models/cCodeIntegration_cg_engee.engee", force = true)
simulationResults_cg_engee = engee.run(cCodeIntegration_cg_engee)
Out[0]:
Dict{String, DataFrame} with 2 entries:
  "filtered" => 6001×2 DataFrame…
  "noisy"    => 6001×2 DataFrame

关闭模型

In [ ]:
engee.close(cCodeIntegration_cg_engee, force = true);

模拟结果

导入模拟结果:

In [ ]:
cCodeIntegration_cg_engee_noisy_t = simulationResults_cg_engee["noisy"].time;
cCodeIntegration_cg_engee_noisy_y = simulationResults_cg_engee["noisy"].value;
In [ ]:
cCodeIntegration_cg_engee_filtered_t = simulationResults_cg_engee["filtered"].time;
cCodeIntegration_cg_engee_filtered_y = simulationResults_cg_engee["filtered"].value;

绘制图表

In [ ]:
plot(cCodeIntegration_cg_engee_noisy_t, cCodeIntegration_cg_engee_noisy_y, label = "Исходный сигнал")
plot!(cCodeIntegration_cg_engee_filtered_t, cCodeIntegration_cg_engee_filtered_y, label = "Отфильтрованный сигнал")
plot!(legend = :bottomright)

title!("Альфа-бета фильтр <br> (Интеграция кода, сгенерированного из модели Engee)")
xlabel!("Время, [с]")
ylabel!("Амплитуда")
Out[0]:

结论

本例演示了使用C 功能块的功能,以及将 C 代码集成到Engee模型中的不同方法:

  1. 集成外部源代码; 2;
  2. 集成静态和动态库; 3;
  3. 集成从Simulink模型生成的代码;
  4. 整合从Engee模型生成的代码。

示例中使用的块