目次
- 目次
- はじめに
- Finite State Machine:FSM 有限状態機械とは
- PythonのFSMライブラリ transitions
- JavaにおけるEnumを使ったシンプルなFSM
- 参考資料
- MyEnigma Supporters
はじめに
ロボットのソフトウェアを開発していると、
一番悩ましいのが状態の管理です。
ロボットがあるタスクを実施するには、
たくさんの状態を定義し、
正しく遷移させ、
それぞれで正しく振る舞わないといけません。
そんなときによく利用されるのが、
Finite State Machine:FSM (有限状態機械, 有限オートマトン)というモデルです。
このモデルを使って、状態を設計し、ソフトウェアを組むと、
複雑な状態を管理しないと行けない場合でも、スッキリとしたコードが書けます。
今回の記事ではFSMの概要とサンプルコードを紹介したいと思います。
Finite State Machine:FSM 有限状態機械とは
Finite State Machine:FSM (有限状態機械、有限オートマトン)は、
有限個の状態と遷移と動作の組み合わせのモデルです。
有限個の状態のうちの一つを必ず取り、
何らかのイベントや条件によってある状態から別の状態へと移行しながら、
状態が遷移していくというモデルです。
このFSMを図式化する方法として、UMLの状態遷移図があります。
FSMを定義するには下記の2つの要素を定義する必要があります。
States : 状態 とりうる有限個の状態と初期状態
Transitions: 遷移 各状態から別の状態になるイベントと、そのイベントの前後で呼ばれるコールバック関数
加えて、オプションとして
各状態のタグ(グループ)
各状態のタイムアウト時間とタイムアウトコールバック
コンディション遷移: ある状態の時のみ遷移させる、そうでない場合はそのまま
などを定義する必要があります。
PythonのFSMライブラリ transitions
FSMをPythonで実装する場合は、transisionsというライブラリがおすすめです。
状態と遷移を定義することにより、
FSMオブジェクトを作ることができ、
現在の状態の管理と、遷移の実行ができます。
定義されてない遷移(許されていない状態から状態までの遷移)を実行するとエラーになります。
下記でもう少し細かい使い方を説明します。
状態 State
各状態に関しては下記を設定できます。
- name: 各状態の名前
- on_enter: その状態に入った時に呼ばれるコールバック関数リスト
- on_exit: その状態を出た時に呼ばれるコールバック関数リスト
- ignore_invalid_triggers: 定義されていない遷移を実施しようとしたら、エラーにするか
また、Stateには下記のような便利関数があります。
- タグ(グループ)の確認関数
- end(): 終端状態かの確認関数
- タイムアウトコールバック: 状態がある一定時間以上続いた場合に呼ばれる関数
遷移 Transitions
各遷移には、下記を設定することができます。
- before: 遷移の前に実行されるコールバック関数
- after: 遷移の後に実行されるコールバック関数
- condition関数: この関数がTrueの時に遷移させる。Falseの時はそのまま
作成したFSMを図にする
transitionsのすばらしいところは、
作成したFSMを下記のようにかんたんに図にすることができることです。
このようにtransitionsで作成したFSMを図にするには、まず
from transitions import Machine
で作成していたFSMオブジェクトを、
from transitions.extensions import GraphMachine
で作成するようにします。
GraphMachineはMachineクラスを拡張したものです。
あとは作成したFSMオブジェクトを
machine.get_graph().draw('my_state_diagram.png', prog='dot')
とすれば、上記のような図がpngファイルとして保存されます。
JavaにおけるEnumを使ったシンプルなFSM
JavaでFSMを実現する方法としては、Enumを使った方法があるようです。
下記のようにEnumで各stateを宣言し、
それぞれのstateへの遷移をEnumの関数として宣言して、
すべてIllegalStateExceptionを返すようにしておきます。
そして、それぞれのStateで遷移してもいい関数だけOverradeするのです。
package finite_state_machine; public enum EnumBasedFiniteStateMachine { A { @Override public EnumBasedFiniteStateMachine toB(){ System.out.println("A -> B"); return B; } @Override public EnumBasedFiniteStateMachine toC(){ System.out.println("A -> C"); return C; } }, B { @Override public EnumBasedFiniteStateMachine toC(){ System.out.println("B -> C"); return C; } }, C { @Override public EnumBasedFiniteStateMachine toA(){ System.out.println("C -> A"); return A; } }; public EnumBasedFiniteStateMachine toA(){ throw new IllegalStateException(); } public EnumBasedFiniteStateMachine toB(){ throw new IllegalStateException(); } public EnumBasedFiniteStateMachine toC(){ throw new IllegalStateException(); } }
下記のように、使うことができます。
禁止されている遷移を実施するとIllegalStateExceptionが発生します。
package finite_state_machine; public class Main { public static void main(String[] args) { //Initial state is A EnumBasedFiniteStateMachine state = EnumBasedFiniteStateMachine.A; System.out.println(state); //Change the state to B state = state.toB(); System.out.println(state); //Change the state to C state = state.toC(); System.out.println(state); //Change the state to A state = state.toA(); System.out.println(state); //Change the state to C state = state.toC(); System.out.println(state); //Change the state to B, but it is prohibited.. state = state.toB(); } }
実行すると下記のような出力が得られます。
A A -> B B B -> C C C -> A A A -> C C Exception in thread "main" java.lang.IllegalStateException at finite_state_machine.EnumBasedFiniteStateMachine.toB(EnumBasedFiniteStateMachine.java:36) at finite_state_machine.Main.main(Main.java:26)
この方法ですと、追加のライブラリも不要ですし、
各遷移でTrigger関数を呼び出すのもかんたんです。
もう少し機能的なライブラリを使う場合は、これらがあります。
参考資料
MyEnigma Supporters
もしこの記事が参考になり、
ブログをサポートしたいと思われた方は、
こちらからよろしくお願いします。