为 Arduino 生成代码(Crossroads)
在本示例中,我们将在 Arduino 兼容板上使用 Engee Automata 库在 Engee 中开发一个模型,以控制一个十字路口的两个车辆交通灯和一个行人交通灯。
简介
本案例研究的目的是为一个交叉路口建立一个控制模型,该交叉路口由两股车流和一股人流组成,分别由两个三段式交通灯和一个两段式交通灯控制。路段切换算法将根据时序图进行定义。控制算法将通过几个模块Chart
来实现,输入信号的消除和输出信号的生成将通过模块C-Function
来实现。
在控制程序算法中,可以选择运行或待机模式,模式的切换由目标设备输入的离散信号实现。
本示例是对演示示例"Arduino 代码生成(有限自动机上的交通灯) "的发展。与前一示例相比,本示例的特殊之处在于:对多个相互连接的块进行建模和代码生成Chart
,并向外围输出数据阵列。
硬件
本演示使用 Amperka 的 Iskra Neo 控制器。如下图所示,受控电路由无线电元件(LED、按钮触点、电阻和连接线)在无焊面包板上组装而成。电路模拟交通灯的运行:两个三段式汽车交通灯和一个两段式行人交通灯。

用于打开/关闭待机模式的输入信号通过按钮触点与 Arduino 电路板的数字触点 4 连接。交通信号灯路段开关的输出信号由数字触点 8 - 13 输出到相应颜色的 LED 上。行人交通灯开关的输出信号由数字触点 6、7 发出。为确保电路的正确运行,还使用了限流电阻(330 欧姆)和收缩电阻(10 千欧)。电路由 Arduino 电路板本身供电。
时序图
要实施的算法将根据下图的时序图再现十字路口交通灯的运行情况。
三股车流(两辆机动车和一名行人)受到异步控制。同时,交通许可(绿灯)每次只针对一个流向,持续时间为 10 秒。车辆通行许可结束后,绿灯部分的闪烁将被打开。交通信号灯从允许信号灯转为禁止信号灯(红灯)时,黄灯部分开启 5 秒钟,反之,红灯和黄灯部分同时开启 5 秒钟。

交通信号灯的两种状态描述了占空比:
- 车辆交通灯的黄色部分开启 1 秒,行人交通灯的黄色部分关闭;
- 在 1 秒钟内,汽车和行人交通灯的所有部分都关闭。
型号说明
本演示示例的模型如下图所示。

控制算法分为几个功能部分--计数器操作在Common_Counter
模块中再现,汽车交通灯控制信号light
的形成在Traffic_Lights
模块中再现,行人交通灯控制信号cross
的形成在Crosswalk
模块中再现。
向模型传输模式选择信号的是程序块Digital Input
,向 Arduino 串行端口输出信息的是程序块To Serial
,向 Arduino 数字引脚输出汽车和行人交通灯代码的分别是程序块Digital_Output_1
和Digital_Output_2
。
向串行端口输出结构化数据时,使用矢量形成块DataMux
。
状态图
在Common_Counter
程序块的状态图中,状态Counter
以及转换2
和3
用于重现 54 秒后复位的递增计数器的运行。状态Service
和转换1
用于在设置交叉点睡眠模式时将计数器重置为"-1 "值。

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

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

信号表在每个程序块状态图Chart
的设置中定义。输出变量的初始值:
- 块
Common_Counter
, 变量mode = 0
; - 块
Traffic_Lights
, 变量light = 33
; - 块
Crosswalk
, 变量cross = 2
。
建模结果
让我们加载所述模型:
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
的编码状态。
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!("Значения")
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!("Значения")
图中显示了变量的变化。模型中的计算周期设定为 1 秒。交通灯和行人交通灯traffic_lights
和crosswalk_lights
各段状态变量的变化值和持续时间与时间图相对应
将代码上传到 Arduino
为了将开发的模型传输到目标设备,让我们来生成 C 代码:
engee.generate_code( "$(@__DIR__)/arduino_crossroad.engee",
"$(@__DIR__)/arduino_crossroad_code" )
插件文件已在指定目录arduino_crossroad_code
中生成。此外,在演示示例的目录arduino_crossroad
中还有一个预先编写的 Arduino 草图,该目录的名称为arduino_crossroad.ino
。在该草图中,连接了代码生成过程中获得的头文件,初始化了全局变量,定义了模型计算循环,并调用了模型计算函数。代码注释中对草图进行了详细描述。
要在 Arduino 上执行代码,请下载arduino_crossroad
目录,并将草图arduino_crossroad.ino
从 Arduino IDE 加载到目标设备上。
在 Arduino 上执行代码
成功编译并将草图加载到目标设备后,调试板的数字输出将产生信号,打开交通灯部分(面包板上的 LED 灯)。为确保模型正常工作,让我们打开 Arduino IDE 中的串行端口监控器。

可以看出,To Serial
程序块中规定的报文以 1 秒的周期按要求的格式输出到串行端口。
当按下按钮触点时,十字路口的交通灯切换到待机模式,并向串行端口输出以下信息:

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