Tutorial?
前回はSIGViewerからエージェントにメッセージを送信して受信したエージェントはお辞儀をするというサンプルを紹介しました。今回はエージェント間でのメッセージのやりとりを行う、サンプルコードを紹介します。
まずメッセージの送信を行うコントローラを作成します。
$ emacs SendController.cpp
SendController.cpp
#include "Controller.h" #include "Logger.h" #include "ControllerEvent.h" #define ARY_SIZE(ARY) ( (int)(sizeof(ARY)/sizeof(ARY[0])) ) class SendController : public Controller { public: //シミュレーション開始時に一度だけ呼出される関数onInitの利用を宣言します。 void onInit(InitEvent &evt); double onAction(ActionEvent &evt); }; void SendController::onInit(InitEvent &evt) { SimObj *my = getObj(myname()); if (!my->dynamics()) { //右手、左手を下に下げ、180°後ろに回転します。 my->setJointAngle("LARM_JOINT2", DEG2RAD(-90)); my->setJointAngle("RARM_JOINT2", DEG2RAD(90)); my->setAxisAndAngle(0, 1.0, 0, DEG2RAD(180)); } } double SendController::onAction(ActionEvent &evt) { //メッセージを作成します。 char *strs[] = { "Hello!!", }; //メッセージを送信します。 broadcastMessage(ARY_SIZE(strs),strs); //5秒置きにメッセージを送信します。 return 5.0; } extern "C" Controller * createController () { return new SendController; }
これは5秒置きにメッセージ"Hello!"を送信するコントローラのサンプルです。broadcastMessageではエージェント全員にメッセージを送信します。
メッセージを送る相手を指定したいときはbroadcastMessageの代わりにsendMessageを用いて以下のように、メッセージを送る相手を指定します。
broadcastMessage(ARY_SIZE(strs),strs);
↓
sendMessaage("robot_000",ARY_SIZE(strs), strs);
次にメッセージの受信を行うエージェントコントローラを作成します。
$ emacs RecvController.cpp
RecvController.cpp
#include "Controller.h" #include "Logger.h" #include "ControllerEvent.h" #define PI 3.141592 #define DEG2RAD(DEG) ( (PI) * (DEG) / 180.0 ) class RecvController : public Controller { public: double onAction(ActionEvent &evt); //メッセージ受信時に呼び出される関数onRecvMessageの利用を宣言します。 void onRecvMessage(RecvMessageEvent &evt); }; double RecvController::onAction(ActionEvent &evt) { SimObj *my = getObj(myname()); if (!my->dynamics()) { //上がった手を下げます。 my->setJointAngle("LARM_JOINT2", DEG2RAD(0)); } return 1.0; } //メッセージ受信時 void RecvController::onRecvMessage(RecvMessageEvent &evt) { //自分自身の取得 SimObj *my = getObj(myname()); // メッセージの送り主表示 LOG_MSG(("sender : %s", evt.getSender())); // getSize で送られてきた文字列数を取得する int n = evt.getSize(); LOG_MSG(("# of string : %d", n)); for (int i=0; i<n; i++) { // 文字列を順に取得する LOG_MSG(("string[%d] : %s", i, evt.getString(i))); //手を上げます。 if (!my->dynamics() ) { my->setJointAngle("LARM_JOINT2", DEG2RAD(180)); } } } extern "C" Controller * createController () { return new RecvController; }
これはメッセージを受信したら手を上げるというサンプルです。また、onActionで1秒ごとに上がった手を下げようとします。
それではコンパイルします。
$ make
次に世界ファイルの作成をします。
$ cd .. $ emacs xml/MyWorld.xml
<?xml version="1.0" encoding="utf8"?> <world name="VisTest2"> <gravity x="0.0" y="-9.8" z="0.0"/> <!--メッセージ送信エージェントMan-niiの設定--> <instanciate class="Man-nii.xml"> <set-attr-value name="name" value="man_000"/> <set-attr-value name="dynamics" value="false"/> <set-attr-value name="language" value="c++"/> <!--オブジェクトファイルSendController.soの指定--> <set-attr-value name="implementation" value="./NewWorld/SendController.so"/> <!--エージェントの位置(x,y,z)--> <set-attr-value name="x" value="0.0"/> <set-attr-value name="y" value="60.0"/> <set-attr-value name="z" value="60.0"/> </instanciate> <!--メッセージ受信エージェントRobot-niiの設定--> <instanciate class="Robot-nii.xml"> <set-attr-value name="name" value="robot_000"/> <set-attr-value name="language" value="c++"/> <!--オブジェクトファイルRecvController.soの指定--> <set-attr-value name="implementation" value="./NewWorld/RecvController.so"/> <!--動力学演算をfalseに設定--> <set-attr-value name="dynamics" value="false"/> <!--エージェントの位置(x,y,z)--> <set-attr-value name="x" value="0.0"/> <set-attr-value name="y" value="60.0"/> <set-attr-value name="z" value="-60.0"/> </instanciate>
人型エージェントが、ヒューマノイド型エージェントにメッセージを送信します。
起動して見てみましょう。
$ ./sigserver.sh -p 9001
SIGViewerで接続して、シミュレーションを開始するとonInitにより、人型エージェント(メッセージ送信者)が手を下して、後ろを振り返ります。そして5秒に一回ロボット(メッセージ受信者)がメッセージに反応して手を挙げているのがわかります。
SIGViewerの下のほうにあるmessagesタブをクリックします。するとロボットがメッセージに反応して送信者と、文字列数、とその内容が表示されます。
文字列は同時に複数送ることができます。送信者コントローラのメッセージ作成時に以下のように記述します。
//メッセージを作成します。 char *strs[] = { "Hello!!", "How are you", };
次にエージェント間でのテキスト文字列の送受信について説明します。テキスト文字列は人間が声を出すのと同じように、自分の声が届く範囲を指定することができます。