使用C功能块将C代码集成到Engee模型中
此示例显示了使用C函数块将C代码集成到Engee模型中的各种方法。
在开始工作之前,我们将连接库以与MATLAB进行比较。:
using MATLAB
使用宏 @__DIR__ 为了找出交互脚本所在的文件夹:
demoroot = @__DIR__
使用C功能块的基础知识
让我们来看看使用C函数块的简单示例,说明将C代码集成到Engee模型中的基础知识。
1. 处理多维信号,传递参数并使用#define指令
进入模型 cCodeIntegration_basics.engee 添加了C函数块,它在其输入端接收正弦信号。 in:
假设我们要增加正弦波的振幅 in 在 gain 一次,并将其移动一个量 bias. 也就是说,输出信号应由公式确定 out = gain * in + bias.
让我们转到C函数块的设置,在选项卡中的位置 Ports 我们将熟悉块的输入和输出的定义。 这是源代码变量与C功能块的端口相关联的地方。:
- 标量变量
x像double与第一输入端口通信in; - 向量(3个元素)变量
y像double与所述第一输出端口连通out.
阵列 gain 它也在C函数块(选项卡)的设置中定义 Parameters):
值得注意的是,将变量作为参数传递后,它变成:
- 全球至
gain您可以从源代码编辑器的任何选项卡访问它。; - 用限定符
const-尝试更改值时gain将收到错误。
位移量 bias 由标识符确定 #define bias,这是在选项卡中设置 Build options:
在此示例中,偏移量为 5.
我们去弗拉卡吧 Main 设置C函数块并单击"编辑源代码"按钮。 在打开的源代码编辑器中,在选项卡上 OutputCode 给出了在模型计算的每一步执行的代码。:
在循环中 for (对于三个输出信号中的每一个)在模型计算的每个步骤中,变量的值被确定 y.
让我们为系统建模:
cCodeIntegration_basics = engee.load("$demoroot/models/cCodeIntegration_basics.engee", force = true)
simulationResults_basics = engee.run(cCodeIntegration_basics)
关闭模型:
engee.close(cCodeIntegration_basics, force = true);
导入模拟结果:
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];
让我们建立一个图表:
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!("Амплитуда")
2. 使用整数数据类型和静态变量
在模型中 cCodeIntegration_integers.engee 使用C函数块,我们实现一个简单的计数器,其输出将是一个正整数,指示当前迭代的数量。:
让我们分析一下选项卡的内容 Ports 和 Build Options 在C功能块设置:
C函数块中整数数据类型的名称根据[头文件]设置(https://ru.wikipedia.org/wiki/Stdint.h ) <stdint.h> C语言的标准库。 此文件附加在该行中 Headers 标签 Build options.
在标签中 Ports 一个无符号的64位变量绑定。 out (类型设置为 uint64_t 而不是 long,见上一段关于 <stdint.h>)的源代码与C函数块的第一个输出端口。
在标签中 Output code C函数块的源代码的编辑器显示了在模拟的每个步骤中执行的代码。:
静态变量 counter 在仿真的第一步,它被初始化为0,之后,在每次迭代中,它的值增加 1 并被赋值给一个变量 out.
让我们为系统建模:
cCodeIntegration_integers = engee.load("$demoroot/models/cCodeIntegration_integers.engee", force = true)
simulationResults_integers = engee.run(cCodeIntegration_integers)
关闭模型:
engee.close(cCodeIntegration_integers, force = true);
导入模拟结果:
cCodeIntegration_integers_t = simulationResults_integers["counter"].time;
cCodeIntegration_integers_y = simulationResults_integers["counter"].value;
让我们建立一个图表:
plot(cCodeIntegration_integers_t, cCodeIntegration_integers_y, legend = false)
title!("Выходной сигнал блока C Function <br> (Целочисленные типы и статические переменные)")
xlabel!("Время, [с]")
ylabel!("Номер итерации")
有关源代码编辑器选项卡的用途、构建功能和可用数据类型的更多详细信息,请参阅[文档](https://engee.com/helpcenter/stable/base-lib-user-defined-function/c-function.html )Engee。
集成外部源代码
设置任务
让我们考虑一个更有趣的工程问题。
假设考虑到测量噪声,我们需要控制二阶的稳定对象,该对象受外部扰动的影响。 因此,我们将需要解决过滤控制对象的输出信号的问题,幸运的是,Engee有一个非常适合这个的工具!
在档案中 alphabetafilter.c 和 alphabetafilter.h 提出了[alpha-beta过滤器]的实现(https://en.wikipedia.org/wiki/Alpha_beta_filter )在C语言中。
文件内容 alphabetafilter.c:
``'C
#包括"alphabetafilter。h"
#include<math.h>
双倍dt=0.01;
静态双xk_1,vk_1;
Parameters determineAlphaBeta(double processNoiseVariance,双测量值)
{
双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;
}
双字母过滤器(双值,参数p)
{
双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;
返回xk;
}
void initializeStaticVariables()
{
xk_1=0;
vk_1=0;
}
文件内容 alphabetafilter.h:
``'C
typedef结构{
双阿尔法;
双贝塔;
}参数;
参数determineAlphaBeta(双,双);
双字母过滤器(双,参数);
void initializeStaticVariables();
我们将此源代码集成到我们的Engee模型中。
模型分析
模型 cCodeIntegration_source.engee:
该模型由:
- 管理对象
Plant - 实现二阶PID控制器的子系统
Controller; - 块带限白噪声
ProcessNoise和MeasurementNoise模拟外部冲击和测量噪声; - C功能块
Filter,其中实现了alpha-beta过滤器。
参数 Sample Time 街区 Filter 等于模型求解器的步骤 Ts:
让我们分析一下选项卡的内容 Build options 块设置 Filter:
在此选项卡的行中:
Source files-源代码文件已连接alphabetafilter.c;Iinclude directories-包含头文件的文件夹的路径设置;Headers-头文件已连接alphabetafilter.h.
在标签中 Ports 源代码变量与块的输入和输出端口相关联。 Filter.
块的源代码中 Filter 在标签上 OutputCode 给出了循环执行的代码:
在模型计算的每个步骤中,函数按顺序调用 determineAlphaBeta() 和 alphabetafilter(),之后将过滤后的值保存到变量中 out 对应的输出端口 x 街区 Filter.
运行模拟
让我们为系统建模:
cCodeIntegration_source = engee.load("$demoroot/models/cCodeIntegration_source.engee", force = true)
simulationResults_source = engee.run(cCodeIntegration_source)
关闭模型:
engee.close(cCodeIntegration_source, force = true);
模拟结果
导入模拟结果:
cCodeIntegration_source_noisy_t = simulationResults_source["noisy"].time;
cCodeIntegration_source_noisy_y = simulationResults_source["noisy"].value;
cCodeIntegration_source_filtered_t = simulationResults_source["filtered"].time;
cCodeIntegration_source_filtered_y = simulationResults_source["filtered"].value;
让我们建立一个图表:
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!("Амплитуда")
静态和动态库的集成
设置任务
源代码通常不是显式提供的,而是作为静态或动态库的一部分提供的。 具有C API接口的库与外部源代码一样容易地集成到Engee模型中!
编译一个静态(alphabetafilter.a)和动态(alphabetafilter.so)文件库 alphabetafilter.c 和 alphabetafilter.h 使用 MakeFile.
文件内容 Makefile:
所有:alphabetafilter.一个alphabetafilter.so
字母过滤器。o:alphabetafilter。c
$(CC) -c -fPIC $^-o $@
alphabetafilter.a: alphabetafilter.o
ar rcs $@ $^
alphabetafilter.so: alphabetafilter.o
$(CC)-\text{共享} $^ -o $@
清洁:
rm-f*。o*。a*.所以
让我们开始编译库:
run(`make -C $demoroot/source`)
将静态和动态库集成到我们的Engee模型中。
静态库:模型分析
通过它的结构,模型 cCodeIntegration_library_static.engee 它与前面讨论的没有区别 cCodeIntegration_source.engee;只有选项卡上的指令不同 Build options 块设置 Filter. 让我们分析一下它的内容:
此处定义了以下构造选项:
Include directories-设置包含头文件的文件夹的路径。;Library directories-设置包含静态库的文件夹的路径。;Headers-头文件已连接alphabetafilter.h;Libraries-静态库已连接alphabetafilter.a.
静态库:运行模拟
让我们为系统建模:
cCodeIntegration_library_static = engee.load("$demoroot/models/cCodeIntegration_library_static.engee", force = true)
simulationResults_library_static = engee.run(cCodeIntegration_library_static)
关闭模型:
engee.close(cCodeIntegration_library_static, force = true);
静态库:仿真结果
导入模拟结果:
cCodeIntegration_library_static_noisy_t = simulationResults_library_static["noisy"].time;
cCodeIntegration_library_static_noisy_y = simulationResults_library_static["noisy"].value;
cCodeIntegration_library_static_filtered_t = simulationResults_library_static["filtered"].time;
cCodeIntegration_library_static_filtered_y = simulationResults_library_static["filtered"].value;
让我们建立一个图表:
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!("Амплитуда")
动态库:模型分析
通过它的结构,模型 cCodeIntegration_library_dynamic.engee 它与前面讨论的没有区别 cCodeIntegration_source.engee;只有选项卡上的指令不同 Build options 块设置 Filter. 让我们分析一下它的内容:
此处定义了以下构造选项:
Include directories-包含头文件的文件夹的路径设置;Library directories-设置包含动态库的文件夹的路径。;Headers-头文件已连接alphabetafilter.h;Libraries-启用了动态库alphabetafilter.so.
在块源代码编辑器的开始代码选项卡中 Filter 函数正在被调用 initializeStaticVariables():
功能 initializeStaticVariables() 在文件中声明 alphabetafilter.h 它用于初始化静态变量。 xk_1 和 vk_1,在动态库中使用。 这是必要的,以便在重新启动模拟时,变量不会保留其最后计算的值,而是重置为其原始值(在这种情况下,在 0).
动态库:运行模拟
让我们为系统建模:
cCodeIntegration_library_dynamic = engee.load("$demoroot/models/cCodeIntegration_library_dynamic.engee", force = true)
simulationResults_library_dynamic = engee.run(cCodeIntegration_library_dynamic)
关闭模型:
engee.close(cCodeIntegration_library_dynamic, force = true);
动态库:仿真结果
导入模拟结果:
cCodeIntegration_library_dynamic_noisy_t = simulationResults_library_dynamic["noisy"].time;
cCodeIntegration_library_dynamic_noisy_y = simulationResults_library_dynamic["noisy"].value;
cCodeIntegration_library_dynamic_filtered_t = simulationResults_library_dynamic["filtered"].time;
cCodeIntegration_library_dynamic_filtered_y = simulationResults_library_dynamic["filtered"].value;
让我们建立一个图表:
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!("Амплитуда")
集成从Simulink模型生成的代码
设置任务
从Simulink模型生成C代码 alphabetafilter.slx 并将其集成到我们的Engee模型中。 cCodeIntegration_cg_simulink.engee.
模型 alphabetafilter.slx,实现了alpha-beta滤波器,从简单的块组装在Simulink中,由子系统组成 determineAlphaBeta 和 filterInputSignal.
模型的上层:
子系统 determineAlphaBeta:
子系统 filterInputSignal:
使用Simulink嵌入式编码器生成代码:
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', '')
"""
模型分析
通过它的结构,模型 cCodeIntegration_cg_simulink.engee 它与前面讨论的没有区别 cCodeIntegration_source.engee;只有选项卡上的指令不同 Build options 块设置 Filter. 我们来分析一下它的内容:
此处定义了以下构造选项:
Source files-源代码文件已连接alphabetafilter.c和alphabetafilter_data.c;Include directories-包含头文件的文件夹的路径已设置;Headers-头文件已连接alphabetafilter.h,alphabetafilter_types.h和rtwtypes.h.
循环执行的代码显示在选项卡上 Output code 块源代码编辑器 Filter:
在模型计算的每个步骤中,执行以下操作:
*结构的初始化 alphabetafilter_U 变量 in, processNoiseVariance 和 measurementNoiseVariance (输入信号);
*函数调用 alphabetafilter_step();
*分配变量 out (输出信号)的结果 x,保存在结构中 alphabetafilter_Y.
在块源代码编辑器的开始代码选项卡中 Filter 函数正在被调用 alphabetafilter_initialize():
在块源代码编辑器的终止代码选项卡中 Filter 函数正在被调用 alphabetafilter_terminate():
运行模拟
让我们为系统建模:
cCodeIntegration_cg_simulink = engee.load("$demoroot/models/cCodeIntegration_cg_simulink.engee", force = true)
simulationResults_cg_simulink = engee.run(cCodeIntegration_cg_simulink)
关闭模型:
engee.close(cCodeIntegration_cg_simulink, force = true);
模拟结果
导入模拟结果:
cCodeIntegration_cg_simulink_noisy_t = simulationResults_cg_simulink["noisy"].time;
cCodeIntegration_cg_simulink_noisy_y = simulationResults_cg_simulink["noisy"].value;
cCodeIntegration_cg_simulink_filtered_t = simulationResults_cg_simulink["filtered"].time;
cCodeIntegration_cg_simulink_filtered_y = simulationResults_cg_simulink["filtered"].value;
让我们建立一个图表:
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!("Амплитуда")
集成从Engee模型生成的代码
设置任务
从Engee模型生成C代码 alphabetafilter.engee,由简单的块组装并实现alpha-beta过滤器,并将其集成到我们的Engee模型中 cCodeIntegration_cg_engee.engee.
模型的上层:
子系统 determineAlphaBeta:
子系统 filterInputSignal:
让我们使用Engee代码生成器生成代码:
engee.generate_code("$demoroot/cg/engee/alphabetafilter.engee", "$demoroot/cg/engee/alphabetafilter_cg")
模型分析
通过它的结构,模型 cCodeIntegration_cg_engee.engee 它与前面讨论的没有区别 cCodeIntegration_source.engee;只有选项卡上的指令不同 Build options 块设置 Filter. 让我们分析一下它的内容:
此处定义了以下构造选项:
Source files-源代码文件已连接alphabetafilter.c;Include directories-包含头文件的文件夹的路径已设置;Headers-头文件已连接alphabetafilter.h.
循环执行的代码显示在选项卡上 Output code 块源代码编辑器 Filter:
在模型计算的每个步骤中,执行以下操作:
*结构的初始化 alphabetafilter_U 变量 in, processNoiseVariance 和 measurementNoiseVariance (输入信号);
*函数调用 alphabetafilter_step();
*分配变量 out (输出信号)的结果 x,保存在结构 alphabetafilter_Y.
在块源代码编辑器的开始代码选项卡中 Filter 函数正在被调用 alphabetafilter_init():
在块源代码编辑器的终止代码选项卡中 Filter 函数正在被调用 alphabetafilter_term():
运行模拟
让我们为系统建模:
cCodeIntegration_cg_engee = engee.load("$demoroot/models/cCodeIntegration_cg_engee.engee", force = true)
simulationResults_cg_engee = engee.run(cCodeIntegration_cg_engee)
关闭模型:
engee.close(cCodeIntegration_cg_engee, force = true);
模拟结果
导入模拟结果:
cCodeIntegration_cg_engee_noisy_t = simulationResults_cg_engee["noisy"].time;
cCodeIntegration_cg_engee_noisy_y = simulationResults_cg_engee["noisy"].value;
cCodeIntegration_cg_engee_filtered_t = simulationResults_cg_engee["filtered"].time;
cCodeIntegration_cg_engee_filtered_y = simulationResults_cg_engee["filtered"].value;
让我们建立一个图表:
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!("Амплитуда")
结论
这个例子演示了使用C函数块的特性以及将C代码集成到Engee模型中的各种方法。:
- 集成外部源代码;
- 静态和动态库的集成;
- 集成从Simulink模型生成的代码;
- 从Engee模型生成的代码的集成。