Engee 文档
Notebook

电池电量减少时返还无人机

在本项目中,我们将采用几种方法来使用有限自动机计算代理系统。

为什么要使用有限自动机?

有限自动机允许你创建一个调试环境,或在抽象层面上测试决策算法。为了避免从头开始开发和维护解决方案的成本,我们将尝试其他几种方法:

  • 使用内部库 StateMachines建模,以及使用 Engee 图形语言。
  • 使用 Engee 图形语言。

最简单的有限自动机示例

首先,让我们构建一个简单的确定性有限自动机(DFSM)。

In [ ]:
]add StateMachines

让我们创建一个自动机a1 ,它定义了四旋翼飞行器在电池电量不足时返回基地的逻辑。

In [ ]:
using StateMachines

a1 = Automaton([
    Transition("landed", "flying", :takeoff),         # Взлет по команде
    Transition("flying", "returning", :low_battery),  # Авто-возврат
    Transition("flying", "returning", :return_cmd),   # Возврат по команде
    Transition("returning", "landed", :returned),     # Посадка
], start="landed")

states = []
events = [ :idle, :takeoff, :low_battery, :returned ]

for event in events
    prev_state = a1.state
    StateMachines.exec!( a1, event )
    append!( states, [a1.state] )
    println( "Переход $(String(prev_state)) -> $(String(a1.state)) по событию: $(String(event))" )
end
Переход landed -> landed по событию: idle
Переход landed -> flying по событию: takeoff
Переход flying -> returning по событию: low_battery
Переход returning -> landed по событию: returned

另一种输出方式是图形输出。对于这种输出,我们将把数组statesevents 转换为字符串形式,并赋予它们一定的顺序,这样输出就不会按字母顺序进行,而是按更合理的顺序进行。

In [ ]:
]add CategoricalArrays Measures
In [ ]:
using CategoricalArrays, Measures
gr()

# Зададим порядок событий и состояний (в противном случае они выводятся в алфавитном порядке)
events_ordered = levels!( categorical(string.(events)), ["idle", "takeoff", "low_battery", "returned"])
states_ordered = levels!( categorical(string.(states)), ["landed", "flying", "returning"])

plot(
    plot( events_ordered, 1:length(events_ordered), title="События",
          st=:steppre, yflip=true, lw=repeat([ 12; 2 ], length(events_ordered))[1:end-1] ),
    plot( states_ordered, 1:length(states_ordered), title="Состояния",
          st=:steppre, yflip=true, yaxis=nothing, c=2,
          lw=repeat([ 12; 2 ], length(states_ordered))[1:end-1] ),
    legend=false, xmirror=true, top_margin=5mm, right_margin = [10mm 0mm],
)
plot!( size=(800,300) )
Out[0]:

我们构建了一个相当典型的图,在 UML 中称为Sequence Diagram序列图)。

如果方便进行另一种解释,可以将图的轴线反转。

In [ ]:
# Зададим порядок событий и состояний (в противном случае они выводятся в алфавитном порядке)
events_ordered = levels!( categorical(string.(events)), ["idle", "takeoff", "low_battery", "returned"])
states_ordered = levels!( categorical(string.(states)), ["landed", "flying", "returning"])

plot(
    plot( 1:length(events_ordered), events_ordered, title="События",
          st=:steppre, lw=repeat([ 2; 12 ], length(events_ordered))[1:end-1] ),
    plot( 1:length(states_ordered), states_ordered, title="Состояния",
          st=:steppre, c=2, lw=repeat([ 2; 12 ], length(states_ordered))[1:end-1] ),
    legend=false, xmirror=true, top_margin=5mm, right_margin = [10mm 0mm],
)
plot!( size=(800,300) )
Out[0]:

在 Engee 环境中使用图表组件

有限自动机编程对于大型系统建模或生成场景非常有用。对于小型决策算法的表示,Engee 中的Chart (有限自动机)组件所代表的图形形式主义也非常适合。

image.png

我们定义了一个场景,在这个场景中,事件的发生是通过块Ступенчатая функция (Step) 通过块Разность (Difference) 来模拟的。因此,有限自动机Логика возврата 的输入将接收离散脉冲,而不是步长。

在块下添加了注释元素,这样,整个测试场景就可以从模型的第一眼看到。

image.png

剩下的工作就是在命令模式下运行模型并绘制图表:

In [ ]:
model_name = "drone_rtb_state_machine";
model_name in [m.name for m in engee.get_all_models()] ? engee.open(model_name) : engee.load( "$(@__DIR__)/$(model_name).engee");
res = engee.run( model_name );

plot( res["Состояние"].time, res["Состояние"].value, label="Состояние",
      yticks=((1,2,3), ("На базе","Полет","Возвращение")), lw=3, size=(600,300) )
Out[0]:

您可以根据该图生成代码(由于 C 语言不支持西里尔字母,因此最好为程序块选择一个由拉丁字母组成的名称Chart )。

结论

我们创建了一个最简单的四旋翼飞行器功能模型--电池电量不足时 "返回基地"。我们可以使用该模型作为更复杂系统的测试场景,在受控环境中检查某些状态的顺序和发生情况。

示例中使用的块