# smach Tutorials
Web Site Link
---
- [smach Tutorials](http://wiki.ros.org/smach/Tutorials)
How to install
---
- smachライブラリのインストール
$ sudo apt-get install ros-kinetic-executive-smach
- smachの可視化ツールのインストール
$ sudo apt-get install ros-kinetic-executive-smach-visualization
$ sudo add-apt-repository ppa:nilarimogard/webupd8
$ sudo apt-get update
$ sudo apt-get install python-wxgtk2.8
Creating a State
---
- 基本
- smach.Stateクラスを継承したクラスを作成
- 作成したクラス内にexecuteメソッドを実装
- コード例
```python=
class HotWater(smach.State):
def __init__(self, outcomes=['cold', 'with_leaf', 'nothing']):
# このstateの初期化処理を書く
# 他の処理が割り込んではいけない
self.time_passed = false
self.add_leaf = false
def execute(self, userdate):
# このstateで行う処理を書く
# 他のstateへ遷移する際に、返り値としてoutcomesを返す。outcomesはこのstateでの処理がどのように終わったかを記述する。
if self.time_passed:
return 'cold'
elif self.add_leaf:
return 'with_leaf'
else:
# 何かしらを返さないとNoneが返り値となり、初期化の際にoutcomesにNoneを入れてない場合はエラーが吐かれるので注意。
return 'nothing'
```
Adding states to a state machine and run a state machine
---
- コード例
```python=
# このコードの前に前節のようにして、smach.Stateクラスを継承したWaterクラスとHotWaterクラスを作成したものとする。
# state machineの作成。引数としてstate machineの想定される結果を渡す。
sm_drink = smach.StateMachine(outcomes=['tea', 'coke', 'coffee'])
# 以下、state machineにそれぞれのstateを追加する。
# 最初に追加したものが状態遷移の最初のstateとなる。
# それぞれのstateは慣例として、全て大文字で記述する。
with sm_drink:
smach.StateMachine.add('HOTWATER', HotWater(),
transitions={'cold':'WATER',
'with_leaf':'tea',
'nothing':'HOTWATER'})
smach.StateMachine.add('WATER', Water(),
transitions={'hot':'HotWater',
'nothing':'WATER'})
# state machineの起動
sm_drink.execute()
```
- 上記のstate machineを可視化したもの

How to view the running state machine
---
```python=
import smach_ros
```
をした上で、可視化したいstate machine(ここではsm)を作成した後、
```python=
sis = smach_ros.IntrospectionServer('server_name', sm, '/SM_ROOT')
sis.start()
sm.execute() # state machineの起動
rospy.spin() # ctl-cが押されるまで待機
sis.stop() # 可視化終了
```
と記述すれば、
```
$ rosrun smach_viewer smach_viewer.py
```
を別ターミナルで実行することでstate machineの様子を可視化できる。
Let a state have userdata
---
- それぞれのstateにinputあるいはoutputデータを持たせることができ、それをuserdataと呼ぶ。
- 下記のコード例のように、stateの初期化時にinput_keys, output_keysを引数として渡す。
- input_keysは読み込み専用である点に注意
```python=
class Foo(smach.State):
def __init__(self, outcomes=['outcome1', 'outcome2'],
input_keys=['foo_input'],
output_keys=['foo_output'])
def execute(self, userdata):
# Do something with userdata
if userdata.foo_input == 1:
return 'outcome1'
else:
userdata.foo_output = 3
return 'outcome2'
```
Connecting user data in a state machine
---
```python=
# state machineも同様にuserdataを持てる
sm_top = smach.StateMachine(outcomes=['outcome4','outcome5'],
input_keys=['sm_input'],
output_keys=['sm_output'])
with sm_top:
smach.StateMachine.add('FOO', Foo(),
transitions={'outcome1':'BAR',
'outcome2':'outcome4'},
remapping={'foo_input':'sm_input',
'foo_output':'sm_data'})
smach.StateMachine.add('BAR', Bar(),
transitions={'outcome2':'FOO'},
remapping={'bar_input':'sm_data',
'bar_output1':'sm_output'})
```
- state間でデータを受け渡すときの例
```python=
FOO:remapping={'foo_output':'sm_user_data'}
BAR:remapping={'bar_input':'sm_user_data'}
```
- sm_user_dataという変数を介してデータを受け渡す
- これは他のstateのinput_keys, output_keysに直接アクセスできないため、state machineのuserdataを介して受け渡しを行っている
- sm_user_dataは自動的にstate machineのuserdataとなる
- state, state machine間でデータを受け渡すときの例
- stateからstate machineへ
```python=
BAR:remapping={'bar_output':'sm_output'}
```
- state machineからstateへ
```python=
FOO:remapping={'foo_input':'sm_input'}
```
- state間と異なり、state machineのuserdataへのアクセスは直接行える
- また、userdataの宣言はstateもしくはstate_machineの初期化時でなくても行うことができる
```python=
sm = smach.StateMachine(outcomes=['outcome4'])
sm.userdata.sm_counter = 0
```
Creating a hierarchical State Machine
---
- state machineもstateと同じように扱うことができ、下記のように階層的な構造をもつstate machineを作ることも可能である。
```python=
# Create the top level SMACH state machine
sm_top = smach.StateMachine(outcomes=['outcome5'])
with sm_top:
smach.StateMachine.add('BAS', Bas(),
transitions={'outcome3':'SUB'})
# Create the sub state machine
sm_sub = smach.StateMachine(outcomes=['outcome4'])
with sm_sub:
smach.StateMachine.add('FOO', Foo(),
transitions={'outcome1':'BAR',
'outcome2':'outcome4'})
smach.StateMachine.add('BAR', Bar(),
transitions={'outcome1':'FOO'})
# Add a state machine to the other state machine like a state
smach.StateMachine.add('SUB', sm_sub,
transitions={'outcome4':'outcome5'})
```
