「しかのこのこのここしたんたん」のマルコフ連鎖を ROS2 StateMachine で実装する

# 「しかのこのこのここしたんたん」とは
<center>
<img src="https://cs1.animestore.docomo.ne.jp/anime_kv/img/27/22/6/27226_1_9_8b.png?1718853605965">
</center>
<br>

　**「しかのこのこのここしたんたん」** とはアニメの題名です。ここではこのアニメの内容については語りませんが、<font color="red">**なかなかぶっ飛んだアニメです。**</font>面白いので是非視聴してみてください。

 # マルコフ連鎖とは
 　マルコフ連鎖とは簡単に説明すると、ある状態から次の状態に遷移する確率が現在の状態のみに依存する確率過程です。過去の状態は考慮されず、各遷移は定められた確率で行われる状態遷移過程を示します。
　現在の状態から次の状態へそれぞれの確率で遷移することを示す状態遷移図を用いて図式することができます。

## 「しかのこ」におけるマルコフ連鎖
　「しかのこのこのここしたんたん」が長いので以降「しかのこ」と表記します。しかのこにおけるマルコフ連鎖はすでに一部の界隈では人気のコンテンツであり、以下の Youtube の動画がとても有名です。というか以下の動画を見ればこの記事で何を話しているのかなんとなく想像できるのではないのではないでしょうか？

<iframe width="560" height="315" src="https://www.youtube.com/embed/Xkq13ZthmA0?si=K-VGJGxjppK779_S" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

　この動画で示しているのは、しかのこの OP テーマの「シカ色デイズ」の冒頭歌詞
 ```
しかのこのこのここしたんたん
```
の各文字列をマルコフ連鎖として分解したものです。以下が「シカ色デイズ」の公式 OP です。

<iframe width="560" height="315" src="https://www.youtube.com/embed/ZZvIVRQ4E7I?si=CjUlKj4MCR-eRIQf" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

　上のマルコフ連鎖の動画に示している図の通り、「し、か、の、こ、た、ん」の 6 つの文字列と1つの空白で `しかのこのこのここしたんたん` を構成することができます。空白は OP テーマで `...たんたん` を言い切ったときに発生する 1 拍を示しています。（クラッピング部分）

![markov_shikanoko.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729782/02df6c73-4bf0-bac6-6fd0-b8ccdb216733.png)

空白を含めて 7 つの文字列の状態遷移をマルコフ連鎖に変換すると上記の画像になります。この遷移図から本来の歌詞である `しかのこのこのここしたんたん` が成立する確率は、[こちらの記事で解説されている通り](https://gigazine.net/news/20240722-shikanoko-markov-chain/) **1/512** です。

# ROS2とは
　ROS2 とは Robot Operating System のことを指します。OS と書かれていますが OS ではなくミドルウェア的な立ち位置のソフトウェアです。この記事では ROS2 については詳しく解説しませんが、簡単に説明するとロボットを効率的に開発するための Python と C++ のフレームワークです。

 # StateMachine とは
 　StateMachine（ステートマシーン：状態遷移）とは ROS2、ROS で使用できるロボットの状態遷移を構築するパッケージです。例えばあるタスクが終了したら次のタスクへ遷移する〜などの作業を Python コードで実装することができます。

# shikanoko_markov_statemachine
　ステートマシーンを組めるということは **マルコフ連鎖を組めること** です。つまり **ROS2 でしかのこのマルコフ連鎖を再現できる** ということです。私は ROS2 でロボット開発をする身なので勉強として最適だと思い、以下のパッケージを作成しました。

 https://github.com/GAI-313/shikanoko_markov_statemachine

 このパッケージは先ほど解説したしかのこのマルコフ連鎖を ROS2 StateMachine で再現したパッケージです。このパッケージ内のノードを実行すると StateMachine でマルコフ連鎖が構築され、実装されます。サンプル動画はリポジトリ内に埋め込まれているのでご覧ください。

 ## 各ステートの動き
 　以下に一部ステートのコードを添付します。
```python
@smach.cb_interface(outcomes=['ka', 'ta', 'failure'])
def Shi(userdata, node):
    try:
        node.get_logger().info('SHI : し')
        video_publisher(node, 'shi')
        return random.choice(['ka', 'ta'])
    except:
        node.get_logger().error(traceback.format_exc())
        return 'failure'
```
　このステート関数でマルコフ連鎖を構築しているコア部分は
 ```python
 return random.choice(['ka', 'ta'])
 ```
 です。ここで `"ka", "ta"` のどっちかをランダムに抽出します。それを返り値とすることで次の遷移が決定します。返り値の文字列は、コード下部のこの部分で使用されます。
```python
smach.StateMachine.add('SHI', smach.CBState(Shi, cb_kwargs={'node':node}),
            transitions={'ka':'KA',
                         'ta':'TA',
                         'failure':'failure'})
```
　`transitions` 引数の部分で、返り値が `ka` だった場合、ステート `KA` に遷移し、`ta` の場合、ステート `TA` に遷移することを示しています。ステート名は `add` メソッドの第1引数に対応しており、ここで示しているコードはステート `SHI` の部分を示しています。これらのプロセスを 7 個分書くことでマルコフ連鎖を再現することができます。

 # 最後に
 　完全にネタに便乗した記事となりましたが、ROS2 や StateMachine を勉強されている方の参考になれば幸いです。