python状态机transitions库的学习小结(1)--状态机的定义和状态切换

一、学习背景

(一)为什么要用状态机

在用python做一个比较复杂的小项目,需要根据不同的输入,控制摄像头采集执行不同的任务。虽然用流程方式实现了,但阅读起来费劲,还容易出错。所以就用了状态机。至于状态机是什么,度娘上一大把。

(二)为什么用transition

一是懒,懒得自己写一个状态机。
二是已经有现成且成熟的库了,为什么不用呢。况且这个库还在持续维护。

(三)transition怎么来

按github上的手册安装就行了,https://github.com/pytransitions/transitions#threading
我的情况,直接用 pip install transitions可以装上,但import的时候说没有这个库。所以我用第二个方法装了,即克隆下来后用python setup.py install进行安装。

(四)我要做成什么样的模型

前面说了,我需要根据不同的输入,控制摄像头采集执行不同的任务。实际任务总共有3个:
1.摄像头采集一定数量的视频帧
2.对所采集到的帧进行一些分析和计算(本文不会对怎样做计算展开,仅以把帧保存为图片文件代替)
3.持续实时采集视频,分析当前帧与第2步计算的结果进行比对(本文不会对怎样做分析比对展开,仅以把当前帧保存为图片文件代替)。

二、对目标模型的思考

(一)有多少个状态

针对需求的3个功能,至少应该有3个状态。即:采集状态,基准计算状态和实时跟踪比对状态。我把这3个状态分别命名为:sample,locate和trace。
实际应用中,还有可能让这个系统空转,什么也不干。等有下一步输入的时候再重新开始,因此多设计一个空闲状态:idle。这样,总共就有4个状态了。归结如下:

状态名称 功能
idle 空闲状态,啥都不干
sample 图像采集
locate 基准定位计算
trace 实时追踪比对

(二)如何触发状态切换

根据我的需求,所要实现的目标是:
1.一开始应处于idle状态。
2.当输入接收到start的时候,进行idle->sample切换
3.sample执行完采样后,自动进入locate状态,即进行sample->locate切换
4.locate计算完后,自动进入trace状态,实时追踪比对。除非收到stop、restart信号,否则一直运行。
5.当收到restart信号时回到sample状态,接着进入locate状态和trace状态。即trace->sample->locate->trace。
6.当任何时候收到stop信号时,切换到idle状态。

根据上述需求,显示触发信号和状态切换的对应关系如下:

触发信号 原状态 目标状态
start idle sample
restart 任何状态 sample
stop 任何状态 idle

sample->locate->trace间的状态切换应为自动切换。

我将一步步实现从手工触发使其切换,到某些状态间自动切换。

三、上状态机

先上定义状态机源码,然后再做小结分析。

import transitions
class tracer_model(object): # 先定义一个类,把它作为基础模型
    pass
tracer = tracer_model() #生成一个实例,这时候它和普通的实例没有任何区别

# 定义所有状态的列表
states_lst = [
              'idle',
              'sample',
              'locate',
              'trace'
]

# 定义状态切换器
# 也就是当发生什么时从哪个状态转换到哪个状态
transitions_lst = [
    ['start','*','sample'],
    ['cal_pos', 'sample', 'locate'],
    ['live_trace', 'locate', 'trace'],
    ['stop', '*', 'idle'],
    ['restart', '*', 'sample']
]

# 生成一个状态机控制器
machine = transitions.Machine(model=tracer, # 控制哪个模型
                              states=states_lst, # 载入模型可能有的状态
                              transitions=transitions_lst, # 载入状态切换器
                              initial='idle' # 这个状态机初始状态是什么
                              )

transitions库把一个完整的状态机分为执行器控制器2部分。
执行器:就是在指定状态下分别干什么,各种算法都将装在此处
控制器:就是通过外界的动作出发来切换不同的状态。达到想让程序干啥就干啥的目的。状态切换并非状态1->状态2这么简单,还涉及到触发切换后准备阶段、退出旧状态阶段、进入新状态阶段、处于新状态阶段等等,这个放在后面再说。
刚才的代码中:

class tracer_model(object): # 先定义一个类,把它作为基础模型
    pass
tracer = tracer_model() 

相当于定义了执行器,只不过现在定义的这个执行器啥都不干。
而这段代码则是定义了控制器:


# 定义所有状态的列表
states_lst = ['idle',
          'sample',
          'locate',
          'trace'
]

# 定义状态切换器
# 也就是当发生什么时从哪个状态转换到哪个状态
transitions_lst = [
    ['start','*','sample'],
    ['cal_pos', 'sample', 'locate'],
    ['live_trace', 'locate', 'trace'],
    ['stop', '*', 'idle'],
    ['restart', '*', 'sample']
]

# 生成一个状态机控制器
machine = transitions.Machine(model=tracer, 
                              states=states_lst, 
                              transitions=transitions_lst, 
                              initial='idle' 
                              )

我的理解,一个状态机控制器最起码应包括几个内容:
1.控制器要控制哪个执行器 model=tracer
2.整个状态机都有哪些状态states=states_lst
3.状态间切换的触发条件transitions=transitions_lst

这里对状态切换器做个简单介绍。
首先transitions可以是一个列表(更多的方式请看github),列表中的每一个元素就是怎么切换。以['cal_pos', 'sample', 'locate']为例,第一位表示触发切换的触发器(怎么用,后面有说),第二位表示从哪个状态切换出去,第三位表示要切换到哪个状态。起始状态和目标状态都需要事先在状态列表中定义,否则实际执行时会出错。

那为什么['stop', '*', 'idle']中第二位是*号呢?这是transitions库其中一个牛逼的地方,这表示可以从任何当前状态切换到idle状态。

四、状态切换

(一)触发器切换法

通过激活触发器实现状态切换。在上面代码的最后加入以下:

tracer.start() # 激活start触发器
print(tracer.state)

tracer.cal_pos() # 激活cal_pos触发器
print(tracer.state)

可以看到出现这样的结果:

sample
locate

是不是和我之前的定义一致:
当start触发器被触发时,不论当前处于什么状态(此时状态机处于idle状态)都切换到sample状态。此处对应的切换条件为:['start','*','sample']

当前处于sample状态下时,若cal_pos触发器被触发,则切换到locate状态。此处对应的切换条件为['cal_pos', 'sample', 'locate']

不得不说这是transitions库另一个牛逼之处,直接把字符串型定义的触发器转化成执行器的一个方法。

问题来了,如果触发器被触发了,但当前所处状态又不是定义中的原状态,出现什么结果呢?把

#tracer.start() 
#print(tracer.state)

注释掉试一下就会发现出错了:transitions.core.MachineError: "Can't trigger event cal_pos from state idle!"

道理很简单,因为cal_pos触发器被触发时是要从sample->locate的,而状态机运行后初始状态为idle,当然出错啦。

(二)目标状态切换法

transitions同时还支持直接切换到目标状态的切换方式。

1、用执行器进行切换

把刚才的触发器切换法语句删掉,用这几句替换,看看是什么效果。

tracer.to_locate()
print(tracer.state)
locate

你可以看到,不论当前处于什么状态,状态机切换到了locate状态。transitions库根据状态定义,在初始化状态机的时候定义了为执行器tracer增加了to_locate()方法。注意观察可以看到所增加的方法为“to_<状态名>”

2、用控制器进行切换

我们还可以用machine.set_state('locate')强行切换状态。这也是不论当前处于什么状态,都将切换到你想要的目标状态。

这两种分别通过执行器和控制器进行强行切换的方法很重要,在后面的实践中将发挥作用。

本节主要总结了状态机的定义和切换,但仅仅是切换了状态而已,实际上并没有做什么卵事,充其量就是个前戏。下一节将真的来干一炮,也就是对执行器如何执行任务展开讨论。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • 有限状态机(Finite-state machine, FSM),又称有限状态自动机,简称状态机,是表示有限个状态...
    CC先生之简书阅读 22,842评论 7 9
  • 来简书快一年了,从最初的为了完成作业,挖空心思的拼字凑字到现在的有了一个小小的灵感就能写出千字,有时我都感到不可思...
    追风_f27e阅读 724评论 3 6
  • 心的一悸动 眼泪随即而流 小小的感伤, 不可阻隔的无奈! 是否不再看你、想你, 你的喜怒哀乐 便可从我心里抹灭? ...
    连煜阅读 712评论 13 17
  • 昨天就听老师说今天有雪,可能不去训练了。于是,今天早上我起床的第一件事就是――去窗外看看下雪了没有,结果是――没有...
    玲玲_3ed7阅读 663评论 2 15
  • 2019年 总结 1. 8月份 认识易灸灸 9月份决定开认真,持续,长久的做这份事业。 通过在群里和易灸灸伙伴的...
    蛇小姐出远钔阅读 159评论 0 0