|
|
|
このページの内容
Tekkotsu は状態機械を実現するために、StateNode(状態ノード) と Transition(遷移) という2つのクラスを用意しています。これらクラスは BehaviorBaseのサブクラスです。
状態機械のプログラムは次の順番で動きます。
上図の状態遷移「伏せ → 座り → 立ち → 座り」を実行するプログラムについて説明します。
状態機械のプログラムを作成する一般的な手順は次の通りです。
例えば MyStateMotion のような名前を付けて1つの StateNode を作ります。
例: 状態機械による連続モーション実行 最初に、このファイル自体が定義されていない場合には定義するために、次の2行を記述します。
次に必要な部品ファイルをインクルードします。
#ifndef INCLUDED_MyStateMotion_h_ // このファイル INCLUDED_MyStateMotion_h_ が定義されていなければ #define INCLUDED_MyStateMotion_h_ // このファイル INCLUDED_MyStateMotion_h_ を定義する
さて、StateNode の子クラスとして、MyStateMotion を作成します。
#include "Shared/ERS7Info.h" // AIBOの機種(ERS7)に関する部品ファイル #include "Shared/WorldState.h" // 状態を監視するための部品ファイル #include "StateHeader.h" // 状態と遷移に必要な部品ファイル
class MyStateMotion : public StateNodeスタート・ノードへのポインタを作るために、protected変数 start_node を使用します。
コンストラクタ MyStateMotion() は、クラス開始時に自動的に起動する関数で、クラス開始の準備をします。
コンストラクタ起動時に、start_node を初期化するために、start_node(NULL) を記述します。
デコンストラクタ ~MyStateMotion() は、クラス終了時に自動的に起動する関数で、クラス終了の後片付けをします。
そして、setup() が実行されているか判断して、teardown()を実行する処理をデコンストラクタ内に記述します。
setup()関数は、状態機械を構築します。この関数は、MyStateMotion の DoStart()関数が最初に呼ばれるときに自動的に呼ばれます。
// クラス MyStateMotion class MyStateMotion : public StateNode { protected: StateNode * start_node; // start_nodeは、クラス全体で使用するのでココに定義する public: // コンストラクタ MyStateMotion() : StateNode("MyStateMotion"), start_node(NULL) {} // デコンストラクタ ~MyStateMotion() { if(issetup) teardown(); }StateNode::setup() は、親クラスStateNode の setup() を呼ぶ処理です。この処理は決まりごとです。
次に必要なノードを順番に作成して、追加していきます。
例えば、伏せ姿勢を実行するための lie_node を作成する場合には、次のように記述します。
// 伏せ姿勢を実行するためのlie_node を作成する
StateNode * lie_node = new SmallMotionSequenceNode("Lie","lielie.mot");
【構文】 モーション・ノードを作成する
StateNode * NodeName = new XxxxxMotionSequenceNode("StateName","FileName"); XxxxxMotionSequenceNode の部分は、モーションのポスチャ数に応じて使い分けてください。
NodeName 状態ノード名 StateName 状態名 FileName モーションファイル名(.mot)
MotionEditorで作成したモーションのポスチャ数は、キーフレームファイル(.kfm)の1行目に記されています。
60ポスチャを超える場合はモーションを分割し、1モーションのポスチャ数が60以下になるようにしてください。
ポスチャ数に応じて関数を使い分ける 関数 ポスチャ数 TinyMotionSequenceNode 4 SmallMotionSequenceNode 7 MediumMotionSequenceNode 14 LargeMotionSequenceNode 25 XLargeMotionSequenceNode 60 作成したノードを追加する場合、次のように記述します。
// lie_node を追加する
addNode(lie_node);状態から状態への遷移のうち、最も単純な遷移は、前の状態が終了したら、すぐに次の状態へ遷移させることです。
【構文】 ノードを追加する
addNode(NodeName);
NodeName 状態ノード名
そのような単純な遷移は次のようにして追加します。// 単純な遷移(lie_node → sit_node)を追加する
lie_node->addTransition(new CompletionTrans(sit_node));このプログラムでは、もう1つ別の遷移を使用しています。
【構文】 単純な遷移を追加する
PreNodeName->addTransition(new CompletionTrans(DestinationNodeName);
PreNodeName 遷移前の状態ノード名 DestinationNodeName 遷移後の状態ノード名
それは、前の状態が開始してから「ある時間」が経過したら、次の状態へ遷移させる方法です。
そのような時間経過による遷移は次のようにして追加します。// 時間経過による遷移(headneutral_node → standsit_node)を追加する
headneutral_node->addTransition(new TimeOutTrans(standsit_node,1000));
【構文】 時間経過による遷移を追加する
PreNodeName->addTransition(new TimeOutTrans(DestinationNodeName, delay);
PreNodeName 遷移前の状態ノード名 DestinationNodeName 遷移後の状態ノード名 delay 経過時間(ミリ秒)(1秒=1000ミリ秒) DoStart() は状態機械を起動したいときに呼ばれます。
virtual void setup() { // 親クラスStateNodeのsetup()関数を呼ぶ StateNode::setup(); // 伏せノード作成 StateNode * lie_node = new SmallMotionSequenceNode("Lie","lielie.mot"); addNode(lie_node); // 座り姿勢ノード StateNode * sit_node = new SmallMotionSequenceNode("Sit","sitsit.mot"); addNode(sit_node); // 立ち姿勢ノード StateNode * stand_node = new MediumMotionSequenceNode("Stand","sitwalk.mot"); addNode(stand_node); // 頭ニュートラル_ノード StateNode * headneutral_node = new SmallMotionSequenceNode("HeadNeutral","headneut.mot"); addNode(headneutral_node); // 立ち→座りモーション_ノード StateNode * standsit_node = new MediumMotionSequenceNode("StandSit","standsit.mot"); addNode(standsit_node); // スタートは伏せ姿勢 start_node = lie_node; // 遷移(伏せ→座り→立ち→頭正面) lie_node->addTransition(new CompletionTrans(sit_node)); sit_node->addTransition(new CompletionTrans(stand_node)); stand_node->addTransition(new CompletionTrans(headneutral_node)); // 頭正面の状態で1秒経過したら立ち→座り headneutral_node->addTransition(new TimeOutTrans(standsit_node,1000)); }
そして、DoStart() は最初に StateNode::DoStart() を呼びます。
それから、その子ノード start_node の DoStart() を呼んで状態機械をスタートします。DoSop() は同様に StateNode::DoStop() を呼びます。
teardown() は後処理の関数です。下記の例では、StateNode::teardown() を呼ぶだけですが、サウンドファイルを使用した場合は、それをメモリから捨てる処理を記述してください。
下の方の privateセクションは、コンパイラのwarning(警告)を避けるために必要なダミーのコードです。クラスがポインターデータの変数を持つときに書きます。ここでのポインターデータ変数は、
start_node
です。秀丸を開き、上記プログラム(背景が黄色の5つの部分)をコピー&貼り付け、次の通り保存してください。
virtual void DoStart() { StateNode::DoStart(); start_node->DoStart(); } virtual void DoStop() { StateNode::DoStop(); } virtual void teardown() { StateNode::teardown(); } private: // コンパイル時の警告を避けるためのダミー関数 MyStateMotion(const MyStateMotion&); MyStateMotion operator=(const MyStateMotion&); }; #endif保 存 先 : マイドキュメント → usXX → project
ファイル名 : MyStateMotion.h
ファイル種類: C言語ヘッダーファイル(*.h)ファイルを保存したら、次の手順で実行してください。
Telnet画面に表示されるイベントの例を示します。白字が表示されます。カラーの字は表示されません。
Event Logger の使用
- 上記の状態機械のプログラムをコンパイルして、AIBOに入れてください。
すでに入っていれば再びコンパイルする必要はありません。- AIBOを起動してください。
- Cygwin画面を開き、下記のように打ち込んで、AIBOに無線接続してください。
us0X@xxxxx ~ $ telnet AIBOのIPアドレス 59000- コントローラ画面のメニューから次の順に選んでください。
Root Control → Status Reports → Event Logger
次の2つを探してダブルクリックしてください。
[ ] stateMachineEGID
[ ] stateTransitionEGID
それらの左にチェックマーク X が表示されます。- Backボタンを押して Root Control に戻り、0. Mode Switch に進んでください。
MyStateMotion をダブルクリックして起動してください。
Telnet画面に一連のイベントが表示されるでしょう。
最初のイベントは、MyStateMotion が起動したことによるものです。
次は、lie_node の状態が発生したことが表示されるでしょう。- あとは AIBOの動きに合わせて状態遷移が表示されます。
EVENT: (stateMachineEGID,MyStateMotion,A) MyStateMotion開始 Loading: /ms/data/motion/lielie.mot ファイル lielie.mot 読み込み Loading: /ms/data/motion/liedown.pos ファイル liedown.pos 読み込み EVENT: (stateMachineEGID,Lie,A) 状態 Lie 開始 Removing expired 4 (autoprune) EVENT: (stateTransitionEGID,{Lie}--CompletionTrans-->{Sit},S) 遷移 Lie-->Sit 開始 EVENT: (stateMachineEGID,Lie,D) 状態 Lie 終了 Loading: /ms/data/motion/sitsit.mot ファイル sitsit.mot 読み込み Loading: /ms/data/motion/situp.pos ファイル situp.pos 読み込み EVENT: (stateMachineEGID,Sit,A) 状態 Sit 開始 EVENT: (stateMachineEGID,Lie,S) Removing expired 5 (autoprune) EVENT: (stateTransitionEGID,{Sit}--CompletionTrans-->{Stand},S) 遷移 Lie-->Sit 開始
上図では、「伏せ」から「座り」に遷移すると同時に、「遠吠え」の音ファイルを再生します。
また、「座り」から「立ち」に遷移すると同時に、LEDを点滅させます。
このように、動作と同時に音ファイルを再生したり、動作と同時にLEDを光らせたりするには、グループノード(GroupNode)を使用します。
下に示すのは、グループノードを使用したプログラム例です。
例: グループノードによる複数状態への遷移 グループノードの方法は、複数のノードを1つのグループにまとめる、ということです。
具体的には、次の手順になります。
1. グループノードを作成する。
- グループノードを作成する。
- グループノードに所属させたいノードを作成する。
- 個々のノードをグループノードに加える。
// グループノード sit_group を作成する例
GroupNode * sit_group = new GroupNode("SitGroup");
2. グループノードに所属させたいノードを作成する。
【構文】 グループノードを作成する
GroupNode * GroupNodeName = new GroupNode("GroupName");
GroupNodeName グループノード名 GroupName グループ名
このノードの作成方法は、上の MyStateMotion.h で示した通りなので説明を省略します。3. 個々のノードをグループノードに加える。
// sit_group に sit_node と howl_node を加える例
sit_group->addNode(sit_node);
sit_group->addNode(howl_node);
ノードからグループノードへ遷移するには、普通のノードへ遷移する場合と同様です。
【構文】 個々のノードをグループノードに加える
GroupNodeName->addNode("NodeName");
GroupNodeName グループノード名 NodeName 追加したいノード名
次行は、ノード lie_node からグループノード sit_group へ遷移する例です。
lie_node->addTransition(new CompletionTrans(sit_group));グループノードから次のノードへ遷移するには、グループのすべてのノードが完了してから次のノードへ遷移させます。
その手順は次の通りです。
1. 次ノードへの遷移を作る。
- 次ノードへの遷移を作る。
- グループに所属する個々のノードに、1で作った遷移を追加する。
// 次ノード headneutral_node への遷移 ctrans を作る例
CompletionTrans * ctrans = new CompletionTrans(headneutral_node,1);
2. グループに所属する個々のノードに、1で作った遷移を追加する。
【構文】 次ノードへの遷移を作る(グループからの遷移)
CompletionTrans * TransName = new CompletionTrans(DestinationNodeName,n);
TransName 遷移名 DestinationNodeName 遷移後の状態ノード名 n 完了を検知する状態の数(0=すべての状態) // 個々のノード(stand_node と face_node)に 遷移 ctrans を追加する例
stand_node->addTransition(ctrans);
face_node->addTransition(ctrans);
【構文】 ノードに作成した遷移を追加する
PreNodeName->addTransition(TransName);
PreNodeName 遷移前の状態ノード名 TransName 遷移名 秀丸を開き、上記プログラム(背景が黄色)をコピー&貼り付け、次の通り保存してください。
#ifndef INCLUDED_MyStateGroup_h_ #define INCLUDED_MyStateGroup_h_ #include "Shared/ERS7Info.h" #include "Shared/WorldState.h" #include "StateHeader.h" // GroupNodeを使用したサンプルプログラム class MyStateGroup : public StateNode { protected: StateNode * start_node; // start_nodeは、クラス全体で使用するのでココに定義する public: // コンストラクタ MyStateGroup() : StateNode("MyStateGroup"), start_node(NULL) {} // デコンストラクタ ~MyStateGroup() { if(issetup) teardown(); } virtual void setup() { StateNode::setup(); // 伏せノード作成 StateNode * lie_node = new SmallMotionSequenceNode("Lie","lielie.mot"); addNode(lie_node); // sit_group = グループノード(座り+遠吠え) GroupNode * sit_group = new GroupNode("SitGroup"); addNode(sit_group); // 座り姿勢ノード StateNode * sit_node = new SmallMotionSequenceNode("Sit","sitsit.mot"); sit_group->addNode(sit_node); // サウンドノード(遠吠え) SoundNode * howl_node = new SoundNode("Howl","howl.wav"); sit_group->addNode(howl_node); // stand_group = グループノード(立ち+LED顔) GroupNode * stand_group = new GroupNode("StandGroup"); addNode(stand_group); // 立ち姿勢ノード StateNode * stand_node = new MediumMotionSequenceNode("Stand","sitwalk.mot"); stand_group->addNode(stand_node); // LEDノード(顔) LedNode * face_node = new LedNode("Face"); face_node->getMC()->cycle(RobotInfo::FaceLEDMask,1500,1.0); stand_group->addNode(face_node); // 頭ニュートラル_ノード StateNode * headneutral_node = new SmallMotionSequenceNode("HeadNeutral","headneut.mot"); addNode(headneutral_node); // 立ち→座りモーション_ノード StateNode * standsit_node = new MediumMotionSequenceNode("StandSit","standsit.mot"); addNode(standsit_node); // スタートは伏せ姿勢 start_node = lie_node; // 遷移(伏せ→座り→立ち) lie_node->addTransition(new CompletionTrans(sit_group)); sit_node->addTransition(new CompletionTrans(stand_group)); // 遷移(→頭ニュートラル) CompletionTrans *ctrans = new CompletionTrans(headneutral_node,1); // 遷移(立ち→頭ニュートラル) stand_node->addTransition(ctrans); // 遷移(LED顔→頭ニュートラル) face_node->addTransition(ctrans); // 頭正面の状態で1秒経過したら立ち→座り headneutral_node->addTransition(new TimeOutTrans(standsit_node,1000)); } virtual void DoStart() { StateNode::DoStart(); start_node->DoStart(); } virtual void DoStop() { StateNode::DoStop(); } virtual void teardown() { StateNode::teardown(); } private: // コンパイル時の警告を避けるためのダミー関数 MyStateGroup(const MyStateGroup&); MyStateGroup operator=(const MyStateGroup&); }; #endif保 存 先 : マイドキュメント → usXX → project
ファイル名 : MyStateGroup.h
ファイル種類: C言語ヘッダーファイル(*.h)ファイルを保存したら、次の手順で実行してください。
下に示すのは、イベントによる遷移を含むプログラム例です。
イベント名 Event Generator ID(EGID) 説明 タイマーイベント timerEGID タイマーが設定時間に到達したら発生する テキストメッセージイベント textmsgEGID パソコン等からテキストメッセージが届いたら発生する ボタンイベント buttonEGID AIBOのボタンが押されたら発生する ビジョンオブジェクトイベント visObjEGID AIBOが物体を見たら発生する モーションマネージャイベント motmanEGID AIBOが特定の動きをしたとき、または動きを終了したとき発生する
例: イベントによる遷移 このプログラムは、上記 MyStateMotion.h と MyStationGroup.h をもとに書き換えたものです。
次のように書き換えました。この遷移を図で示すと、次のようになります。
- 「伏せ」のときに、テキストメッセージイベントで「座り」に遷移
- 「座り」のときに、ボタンイベントで「立ち」に遷移
- 「立ち」のときに、ビジョンオブジェクトイベントで「頭正面」に遷移
イベント遷移プログラムは、イベントの種類によって書き方が異なります。
それらを次に示します。
【構文】 TextMessageEventによる遷移を作成する
EventTrans * TransName = new EventTrans(DestinationNodeName,EventBase::textmsgEGID);
TransName 遷移名 DestinationNodeName 遷移後の状態ノード名
【構文】 ButtonEventによる遷移を作成する
EventTrans * TransName = new EventTrans (DestinationNodeName,EventBase::buttonEGID, SourceID, TypeID); ボタンの状態は次の通り
TransName 遷移名 DestinationNodeName 遷移後の状態ノード名 SourceID ボタンの場所 TypeID ボタンの状態
押された EventBase::activateETID
離された EventBase::deactivateETID
【構文】 VisionObjectEventによる遷移を作成する
EventTrans * TransName = new EventTrans (DestinationNodeName,EventBase::visObjEGID, SourceID); 色は次の通り
TransName 遷移名 DestinationNodeName 遷移後の状態ノード名 SourceID 色
ピンク ProjectInterface::visBluePinkSID
ブルー ProjectInterface::visBlueBallSID
アイボが色をうまく認識できないときは調整してください。
秀丸を開き、上記プログラム(背景が黄色)をコピー&貼り付け、次の通り保存してください。
#ifndef INCLUDED_MyStateEvent_h_ #define INCLUDED_MyStateEvent_h_ #include "Shared/ERS7Info.h" #include "Shared/WorldState.h" #include "StateHeader.h" // イベントによる状態遷移のサンプルプログラム class MyStateEvent : public StateNode { protected: StateNode * start_node; // start_nodeは、クラス全体で使用するのでココに定義する public: // コンストラクタ MyStateEvent() : StateNode("MyStateEvent"), start_node(NULL) {} // デコンストラクタ, check if we need to call our teardown ~MyStateEvent() { if(issetup) teardown(); } virtual void setup() { StateNode::setup(); // 伏せノード作成 StateNode * lie_node = new SmallMotionSequenceNode("Lie","lielie.mot"); addNode(lie_node); // sit_group = グループノード(座り+遠吠え) GroupNode * sit_group = new GroupNode("SitGroup"); addNode(sit_group); // 座り姿勢ノード StateNode * sit_node = new SmallMotionSequenceNode("Sit","sitsit.mot"); sit_group->addNode(sit_node); // サウンドノード(遠吠え) SoundNode * howl_node = new SoundNode("Howl","howl.wav"); sit_group->addNode(howl_node); // 立ち姿勢ノード StateNode * stand_node = new MediumMotionSequenceNode("Stand","sitwalk.mot"); addNode(stand_node); // 頭ニュートラル_ノード StateNode * headneutral_node = new SmallMotionSequenceNode("HeadNeutral","headneut.mot"); addNode(headneutral_node); // 立ち→座り姿勢ノード StateNode * standsit_node = new MediumMotionSequenceNode("StandSit","standsit.mot"); addNode(standsit_node); // スタートは伏せ姿勢 start_node = lie_node; // TextMessageEventによる遷移(伏せ→座り) EventTrans * textTrans = new EventTrans(sit_group,EventBase::textmsgEGID); lie_node->addTransition(textTrans); // ButtonEventによる遷移(座り→立ち) EventTrans * buttonTrans = new EventTrans(stand_node,EventBase::buttonEGID, RobotInfo::HeadButOffset,EventBase::activateETID); sit_node->addTransition(buttonTrans); // VisionObjectEventによる遷移(立ち→頭ニュートラル) EventTrans * visTrans = new EventTrans(headneutral_node,EventBase::visObjEGID,ProjectInterface::visBlueBallSID); stand_node->addTransition(visTrans); // 頭正面の状態で1秒経過したら立ち→座り headneutral_node->addTransition(new TimeOutTrans(standsit_node,1000)); } virtual void DoStart() { StateNode::DoStart(); start_node->DoStart(); } virtual void DoStop() { StateNode::DoStop(); } virtual void teardown() { StateNode::teardown(); } private: // コンパイル時の警告を避けるためのダミー関数 MyStateEvent(const MyStateEvent&); MyStateEvent operator=(const MyStateEvent&); }; #endif保 存 先 : マイドキュメント → usXX → project
ファイル名 : MyStateEvent.h
ファイル種類: C言語ヘッダーファイル(*.h)ファイルを保存したら、次の手順で実行してください。
StateNode *node = new StateNode (const std::string & nodename);
SoundNode *node = new SoundNode ( const std::string & nodename,
const std::string & soundfilename);
GroupNode *node = new GroupNode (const std::string & nodename);
addNode( StateNode *node );
node->addTransition ( Transition *trans );
node->addTransition ( new CompletionTrans ( StateNode *destination) );
PreNodeName->addTransition(new TimeOutTrans(DestinationNodeName, delay);
PreNodeName 遷移前の状態ノード名 DestinationNodeName 遷移後の状態ノード名 delay 経過時間(ミリ秒)(1秒=1000ミリ秒)
その他のEventTransについては、こちら。
EventTrans *trans = new EventTrans ( StateNode *destination,
EventBase::EventGeneratorID_t gid,
unsigned int sid,
EventBase::EventTypeID_t tid);
基本姿勢以外のモーションは、MotionEditorで自作してください。
基本姿勢のモーションファイル 姿勢 ファイル名 関数 伏せ → 伏せ lielie.mot SmallMotionSequenceNode 座り → 座り sitsit.mot SmallMotionSequenceNode 座り → 伏せ sitlie.mot MediumMotionSequenceNode 座り → 立ち sitstand.mot MediumMotionSequenceNode 座り → 歩行準備 sitwalk.mot MediumMotionSequenceNode 立ち → 座り standsit.mot MediumMotionSequenceNode
60ポスチャを超える場合はモーションを分割し、1モーションのポスチャ数が60以下になるようにしてください。
ポスチャ数に応じて関数を使い分ける 関数 ポスチャ数 TinyMotionSequenceNode 〜 2 SmallMotionSequenceNode 〜 6 MediumMotionSequenceNode 〜 12 LargeMotionSequenceNode 〜 24 XLargeMotionSequenceNode 〜 60
|
|
|