MyEnigma

とある自律移動システムエンジニアのブログです。#Robotics #Programing #C++ #Python #MATLAB #Vim #Mathematics #Book #Movie #Traveling #Mac #iPhone

ロボットエンジニアのためのFinite State Machine入門


有限オートマトン入門

目次

はじめに

ロボットのソフトウェアを開発していると、

一番悩ましいのが状態の管理です。

ロボットがあるタスクを実施するには、

たくさんの状態を定義し、

正しく遷移させ、

それぞれで正しく振る舞わないといけません。

 

そんなときによく利用されるのが、

Finite State Machine:FSM (有限状態機械, 有限オートマトン)というモデルです。

ja.wikipedia.org

このモデルを使って、状態を設計し、ソフトウェアを組むと、

複雑な状態を管理しないと行けない場合でも、スッキリとしたコードが書けます。

 

今回の記事ではFSMの概要とサンプルコードを紹介したいと思います。

 

Finite State Machine:FSM 有限状態機械とは

Finite State Machine:FSM (有限状態機械、有限オートマトン)は、

有限個の状態と遷移と動作の組み合わせのモデルです。

ja.wikipedia.org

www.lancarse.co.jp

 

有限個の状態のうちの一つを必ず取り、

何らかのイベントや条件によってある状態から別の状態へと移行しながら、

状態が遷移していくというモデルです。

 

このFSMを図式化する方法として、UMLの状態遷移図があります。

www.changevision.co

plantuml.com

 

FSMを定義するには下記の2つの要素を定義する必要があります。

  • States : 状態 とりうる有限個の状態と初期状態

  • Transitions: 遷移 各状態から別の状態になるイベントと、そのイベントの前後で呼ばれるコールバック関数

加えて、オプションとして

  • 各状態のタグ(グループ)

  • 各状態のタイムアウト時間とタイムアウトコールバック

  • コンディション遷移: ある状態の時のみ遷移させる、そうでない場合はそのまま

などを定義する必要があります。

 

PythonのFSMライブラリ transitions

FSMをPythonで実装する場合は、transisionsというライブラリがおすすめです。

github.com

qiita.com

qiita.com

qiita.com

qiita.com

状態と遷移を定義することにより、

FSMオブジェクトを作ることができ、

現在の状態の管理と、遷移の実行ができます。

定義されてない遷移(許されていない状態から状態までの遷移)を実行するとエラーになります。

 

下記でもう少し細かい使い方を説明します。

状態 State

各状態に関しては下記を設定できます。

  • name: 各状態の名前
  • on_enter: その状態に入った時に呼ばれるコールバック関数リスト
  • on_exit: その状態を出た時に呼ばれるコールバック関数リスト
  • ignore_invalid_triggers: 定義されていない遷移を実施しようとしたら、エラーにするか

 

また、Stateには下記のような便利関数があります。

  • タグ(グループ)の確認関数
  • end(): 終端状態かの確認関数
  • タイムアウトコールバック: 状態がある一定時間以上続いた場合に呼ばれる関数

遷移 Transitions

各遷移には、下記を設定することができます。

  • before: 遷移の前に実行されるコールバック関数
  • after: 遷移の後に実行されるコールバック関数
  • condition関数: この関数がTrueの時に遷移させる。Falseの時はそのまま

 

作成したFSMを図にする

transitionsのすばらしいところは、

作成したFSMを下記のようにかんたんに図にすることができることです。

f:id:meison_amsl:20220331212417p:plain

 

このように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を使った方法があるようです。

www.baeldung.com

idios.hatenablog.com

 

下記のように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関数を呼び出すのもかんたんです。

 

もう少し機能的なライブラリを使う場合は、これらがあります。

projects.spring.io

github.com

 

参考資料

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com


有限オートマトン入門

MyEnigma Supporters

もしこの記事が参考になり、

ブログをサポートしたいと思われた方は、

こちらからよろしくお願いします。

myenigma.hatenablog.com