Engee 文档
Notebook

为 Arduino 生成代码(Crossroads)

在本示例中,我们将在 Arduino 兼容板上使用 Engee Automata 库在 Engee 中开发一个模型,以控制一个十字路口的两个车辆交通灯和一个行人交通灯。

简介

本案例研究的目的是为一个交叉路口建立一个控制模型,该交叉路口由两股车流和一股人流组成,分别由两个三段式交通灯和一个两段式交通灯控制。路段切换算法将根据时序图进行定义。控制算法将通过几个模块Chart 来实现,输入信号的消除和输出信号的生成将通过模块C-Function 来实现。

在控制程序算法中,可以选择运行或待机模式,模式的切换由目标设备输入的离散信号实现。

本示例是对演示示例"Arduino 代码生成(有限自动机上的交通灯) "的发展。与前一示例相比,本示例的特殊之处在于:对多个相互连接的块进行建模和代码生成Chart ,并向外围输出数据阵列。

硬件

本演示使用 Amperka 的 Iskra Neo 控制器。如下图所示,受控电路由无线电元件(LED、按钮触点、电阻和连接线)在无焊面包板上组装而成。电路模拟交通灯的运行:两个三段式汽车交通灯和一个两段式行人交通灯。

image.png

用于打开/关闭待机模式的输入信号通过按钮触点与 Arduino 电路板的数字触点 4 连接。交通信号灯路段开关的输出信号由数字触点 8 - 13 输出到相应颜色的 LED 上。行人交通灯开关的输出信号由数字触点 6、7 发出。为确保电路的正确运行,还使用了限流电阻(330 欧姆)和收缩电阻(10 千欧)。电路由 Arduino 电路板本身供电。

时序图

要实施的算法将根据下图的时序图再现十字路口交通灯的运行情况。

三股车流(两辆机动车和一名行人)受到异步控制。同时,交通许可(绿灯)每次只针对一个流向,持续时间为 10 秒。车辆通行许可结束后,绿灯部分的闪烁将被打开。交通信号灯从允许信号灯转为禁止信号灯(红灯)时,黄灯部分开启 5 秒钟,反之,红灯和黄灯部分同时开启 5 秒钟。

image_3.png

交通信号灯的两种状态描述了占空比:

  • 车辆交通灯的黄色部分开启 1 秒,行人交通灯的黄色部分关闭;
  • 在 1 秒钟内,汽车和行人交通灯的所有部分都关闭。

型号说明

本演示示例的模型如下图所示。

image_2.png

控制算法分为几个功能部分--计数器操作在Common_Counter 模块中再现,汽车交通灯控制信号light 的形成在Traffic_Lights 模块中再现,行人交通灯控制信号cross 的形成在Crosswalk 模块中再现。

向模型传输模式选择信号的是程序块Digital Input ,向 Arduino 串行端口输出信息的是程序块To Serial ,向 Arduino 数字引脚输出汽车和行人交通灯代码的分别是程序块Digital_Output_1Digital_Output_2

向串行端口输出结构化数据时,使用矢量形成块DataMux

状态图

Common_Counter 程序块的状态图中,状态Counter 以及转换23 用于重现 54 秒后复位的递增计数器的运行。状态Service 和转换1 用于在设置交叉点睡眠模式时将计数器重置为"-1 "值。

image_2.png

根据块Traffic_Lights 状态图中计数器变量cntr 的值,选择编码交通灯路段状态的变量lights 的值。在这种情况下,除ninthtenth 之外的所有状态都将再现运行模式下各部分的切换顺序。 程序块Traffic_Lights 的状态图如下图所示。

image_2.png

反过来,根据Crosswalk 块中lights 变量的值,选择编码行人交通灯状态的cross 变量的值。 块Crosswalk 的状态图如下图所示。

image.png

信号表在每个程序块状态图Chart 的设置中定义。输出变量的初始值:

  • Common_Counter, 变量mode = 0
  • Traffic_Lights, 变量light = 33
  • Crosswalk, 变量cross = 2

建模结果

让我们加载所述模型:

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

data = engee.run(m);

根据获得的模型数据,我们绘制出计数器变量counter 、汽车交通灯traffic_lights 和行人交通灯crosswalk_lights 的编码状态。

In [ ]:
using Plots
plotlyjs()
plot(data["counter"].time, data["counter"].value,
    label="Счётчик", size=(900,300), lw=2, st=:step)
plot!(data["traffic_lights"].time, data["traffic_lights"].value,
    label="Код светофоров", size=(900,300), lw=2, st=:step,
    legend=:topleft)
xlabel!("Время, сек")
ylabel!("Значения")
Out[0]:
In [ ]:
plot(data["crosswalk_lights"].time, data["crosswalk_lights"].value,
    label="Код пешеходный светофор", size=(900,300), lw=2, st=:step, color=:green,
    ylims = (0,3), xticks=:none, legend=:topleft )
    xlabel!("Время, сек")
ylabel!("Значения")
Out[0]:

图中显示了变量的变化。模型中的计算周期设定为 1 秒。交通灯和行人交通灯traffic_lightscrosswalk_lights 各段状态变量的变化值和持续时间与时间图相对应

将代码上传到 Arduino

为了将开发的模型传输到目标设备,让我们来生成 C 代码:

In [ ]:
engee.generate_code( "$(@__DIR__)/arduino_crossroad.engee",
                     "$(@__DIR__)/arduino_crossroad_code" )
[ Info: Generated code and artifacts: /user/start/examples/codegen/arduino_crossroad/arduino_crossroad_code

插件文件已在指定目录arduino_crossroad_code 中生成。此外,在演示示例的目录arduino_crossroad 中还有一个预先编写的 Arduino 草图,该目录的名称为arduino_crossroad.ino 。在该草图中,连接了代码生成过程中获得的头文件,初始化了全局变量,定义了模型计算循环,并调用了模型计算函数。代码注释中对草图进行了详细描述。

要在 Arduino 上执行代码,请下载arduino_crossroad 目录,并将草图arduino_crossroad.ino 从 Arduino IDE 加载到目标设备上。

在 Arduino 上执行代码

成功编译并将草图加载到目标设备后,调试板的数字输出将产生信号,打开交通灯部分(面包板上的 LED 灯)。为确保模型正常工作,让我们打开 Arduino IDE 中的串行端口监控器。

image_4.png

可以看出,To Serial 程序块中规定的报文以 1 秒的周期按要求的格式输出到串行端口。

当按下按钮触点时,十字路口的交通灯切换到待机模式,并向串行端口输出以下信息:

image_2.png

所需的信息以 1 秒的周期输出到串行端口。因此,待机模式下的交叉操作也是正确的,这证明了模型和代码的性能。

输出

在本演示中,我们为 Arduino 兼容平台开发了一个十字路口模型,其中有两个三段式车辆交通灯和一个两段式行人交通灯。控制算法是利用无限自动机库重新创建的。该模型再现了交通灯在两种模式下的运行,不仅确保了路段的切换,还确保了路段的闪烁。该算法已在目标设备上进行了测试,生成的代码不仅能控制 LED 部分,还能向串行端口发送有关当前模式、三个交通灯的点亮部分以及当前运行时间的信息。

示例中使用的块