Engee 文档
Notebook

三相正弦波发生器

在本演示中,我们将了解如何嵌入由 Engee 模型子系统生成的代码。

简介

本例的目的是建立一个三相正弦波发生器模型。发电机的输入信号是振幅 [V] 和频率 [Hz] 输出信号:瞬时相电压。正弦信号的相位差等于$- \frac{2 \cdot \pi}{3}$ 。

在示例过程中,我们将在Engee模型中进行算法建模、代码生成、嵌入和测试,并在目标设备(Arduino 兼容平台)上进行嵌入和测试。

模型描述

所开发的模型sine_generator.engee 表示子系统SinGen 中包含的三相正弦波发生器的算法。子系统的输入为振幅值Amp = 1.0 和频率值Freq = 25.0 。输出正弦信号Sin_ASin_BSin_C 。 模型计算的步长为 0.001 秒。

image.png

子系统SinGen 的内容如下图所示。

image.png

该子系统使用以下公式重现标准计算:

$$ Sin_A = Amplitude \cdot sin(2 \cdot \pi \cdot Frequency \cdot t + \varphi_A) = 1 \cdot sin(2 \cdot \pi \cdot 25 \cdot t), $$ $$ Sin_B = Amplitude \cdot sin(2 \cdot \pi \cdot Frequency \cdot t + \varphi_B) = 1 \cdot sin(2 \cdot \pi \cdot 25 \cdot t - \frac{2 \cdot \pi}{3}), $$ $$ Sin_C = Amplitude \cdot sin(2 \cdot \pi \cdot Frequency \cdot t + \varphi_C) = 1 \cdot sin(2 \cdot \pi \cdot 25 \cdot t - \frac{4 \cdot \pi}{3}). $$

建模结果

让我们加载并执行所述模型:

In [ ]:
if "sine_generator" in [m.name for m in engee.get_all_models()]
    m = engee.open( "sine_generator" );
else
    m = engee.load( "$(@__DIR__)/sine_generator.engee" );
end

data = engee.run(m);

根据获得的模型数据,我们绘制输出变量--瞬时相电压SinGen.Sin_A,SinGen.Sin_B,SinGen.Sin_C

In [ ]:
using Plots
plotlyjs()
plot(data["SinGen.Sin_A"].time, data["SinGen.Sin_A"].value,
    label="V_a", size=(900,300), lw=2, st=:step)
plot!(data["SinGen.Sin_B"].time, data["SinGen.Sin_B"].value,
    label="V_b", size=(900,300), lw=2, st=:step)
plot!(data["SinGen.Sin_C"].time, data["SinGen.Sin_C"].value,
    label="V_c", size=(900,300), lw=2, st=:step,
    legend=:topleft)
xlabel!("Время, сек")
ylabel!("Напряжение, В")
Out[0]:

建模得到的输出信号图为频率为 25 Hz、振幅为 1 V、相移为$- \frac{2 \cdot \pi}{3}$ 的三相正弦电压V_a,V_b,V_c

代码生成

要嵌入所开发的算法,必须从子系统SinGen 生成代码:

In [ ]:
engee.generate_code( "$(@__DIR__)/sine_generator.engee",
                     "$(@__DIR__)/sine_generator_SinGen_code";
                     subsystem_name="SinGen" )
[ Info: Generated code and artifacts: /user/start/examples/codegen/sine_generator/sine_generator_SinGen_code

执行代码生成命令后,在指定目录下创建了main.csine_generator_SinGen.hsine_generator_SinGen.c 文件。

image.png

现在,让我们看看并测试一下嵌入模型生成的代码的方法。

将代码嵌入 Engee 模型

为了测试生成的代码,我们将其嵌入 Engee 模型sine_generator_test.engee 中。算法的输入和输出参数与原始模型sine_generator.engee 中的参数相同。不过,算法本身是在嵌入到块C Function 中的生成 C 文件的基础上实现的。

image.png

代码嵌入包括以下内容。在程序块C Function 中,指定生成文件的位置,初始化输入和输出变量,并调用模型生成的计算函数。

程序块C Function 的选项卡</> OutputCode 的内容:

// engee-cfunction-start
//
// 生成源文件 /user/start/examples/codegen/sine_generator/sine_generator_SinGen_code/sine_generator_SinGen.c
// build include_directories /user/start/examples/codegen/sine_generator/sine_generator/sine_generator_SinGen_code
// 生成库目录
// 联编头文件 sine_generator_SinGen.h
// 联编定义
// 联编库
//
// 名称 范围 标签 类型 大小 端口
// 符号 A 输入 'A' 双 1 1
// 符号 F 输入 'F' 双 1 2
// 符号 Va 输出'Va' double 1 1
// 符号 Vb 输出'Vb' double 1 2
// 符号 Vc 输出'Vc' double 1 3
//
// 功能结束
//

// 将频率和振幅值传输到结构体中
正弦生成器_SinGen_U.频率 = F
sine_generator_SinGen_U.Amplitude = A

sine_generator_SinGen_step()

// 从结构中按相转移瞬时电压值
Va = sine_generator_SinGen_Y.Sin_A
Vb = sine_generator_SinGen_Y.Sin_B
Vc = 正弦发电机_SinGen_Y.Sin_C

C Function 的选项卡</> StartCode 的内容 :

// 调用初始化函数
sine_generator_SinGen_init()

C Function 块的</> TerminateCode 选项卡内容 :

// 调用终止函数
sine_generator_SinGen_term()

嵌入代码建模

让我们加载并执行sine_generator_test.engee 模型,以测试生成的代码:

In [ ]:
if "sine_generator_test" in [n.name for n in engee.get_all_models()]
    n = engee.open( "sine_generator_test" );
else
    n = engee.load( "$(@__DIR__)/sine_generator_test.engee" );
end

test = engee.run(m);

根据获得的模型数据,绘制输出变量 - 瞬时相电压SinGen.Sin_A,SinGen.Sin_B,SinGen.Sin_C

In [ ]:
plot(test["SinGen.Sin_A"].time, test["SinGen.Sin_A"].value,
    label="V_a", size=(900,300), lw=2, st=:step)
plot!(test["SinGen.Sin_B"].time, test["SinGen.Sin_B"].value,
    label="V_b", size=(900,300), lw=2, st=:step)
plot!(test["SinGen.Sin_C"].time, test["SinGen.Sin_C"].value,
    label="V_c", size=(900,300), lw=2, st=:step,
    legend=:topleft)
xlabel!("Время, сек")
ylabel!("Напряжение, В")
Out[0]:

从获得的图形中可以看出,测试模型中产生的电压信号也是正弦波形式,具有指定的频率、振幅和相移。

让我们再比较一下原始模型和测试模型 A 相正弦信号的建模结果:

In [ ]:
plot(data["SinGen.Sin_A"].time, data["SinGen.Sin_A"].value,
    label="V_a", size=(900,300), lw=4, st=:step)
plot!(test["SinGen.Sin_A"].time, test["SinGen.Sin_A"].value,
    label="V_a_test", size=(900,300), lw=2, st=:step,
    legend=:topleft)
xlabel!("Время, сек")
ylabel!("Напряжение, В")
Out[0]:

从上图可以看出,初始模型的模拟结果与测试代码生成结果完全一致。

将代码嵌入 Arduino 草图

为了在目标设备上测试开发的算法,有必要将生成的文件sine_generator_SinGen.csine_generator_SinGen.h 插入用户程序项目中。

本示例使用 Amperka 公司的 Iskra Neo 控制器进行演示。在目标设备上执行算法时,控制器会将生成的信号值传输到串行端口。传输值的显示将通过 Arduino IDE 工具完成。

为了嵌入算法代码,我们之前开发了一个用于 Arduino IDEsine_generator.ino 的草图,该草图也附在本演示的文件夹中。该草图的内容与 Engee 测试模型C Function 块的内容在功能上完全相同,除了与外设的工作功能和维持计算步骤的周期。代码注释中给出了草图操作的详细说明。

在 Arduino 上执行代码

成功编译并将草图加载到目标设备后,让我们打开 Arduino IDE 工具箱中的 "串行绘图仪"。带输出信号的绘图仪截图如下图所示。

image_2.png

从绘图仪绘制的图中可以看出,所得到的信号也具有指定的振幅、频率和相移。

结论

在本演示中,开发了一个具有指定振幅、频率和相移等参数的三相正弦波发生器模型。在 Engee 模型中调试了发生器算法,然后将生成的代码文件嵌入 Engee 模型中进行测试,最后将生成的 C 文件加载到自定义草图中,并在目标设备上进行测试。

这样,通过一个简单的正弦波发生器例子,我们了解了从Engee模型生成代码的可能性。

示例中使用的块