Up:[[Tutorial]]     Previous:[[人間型エージェントの操作]]     Next:[[音声認識]]

#contents

*エージェント間でのメッセージのやり取り [#he80bd53]
前回はクライアント(SIGViewer)からエージェントにメッセージを送信して受信したエージェントはお辞儀をするというサンプルを紹介しました。今回はエージェント間でのやり取りを行うサンプルコードを紹介します。

**メッセージの送受信 [#v4c148e5]

***メッセージ送信者のコントローラ作成 [#pd6017d2]

まずメッセージの送信を行うコントローラを作成します。

 $ cd ~/sigverse-<version>/bin/NewWorld
 $ emacs SendController.cpp

SendController.cpp
#highlight(cpp){{
 #include "Controller.h"
 #include "Logger.h"
 #include "ControllerEvent.h"
 
 #define PI 3.141592
 #define DEG2RAD(DEG) ( (PI) * (DEG) / 180.0 )
 
 #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を用いて以下のように、メッセージを送る相手を指定します。

#highlight(cpp:firstline[40]){{
 broadcastMessage(ARY_SIZE(strs),strs);
}}
         ↓
#highlight(cpp:firstline[40]){{
 sendMessaage("robot_000",ARY_SIZE(strs), strs);
}}
メッセージを送信する相手に"robot_000"を指定しました。
***メッセージ受信者のコントローラ作成 [#sb6efc03]
次にメッセージの受信を行うエージェントコントローラを作成します。

 $ emacs RecvController.cpp

RecvController.cpp

#highlight(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秒ごとに上がった手を下げようとします。

それではコンパイルします。
まずMakefileを修正します。
 $ emacs Makefile

変更前
 #オブジェクトファイルの指定
 OBJS     = AgentController.so

変更後
 #オブジェクトファイルの指定
 OBJS     = SendController.so RecvController.so
共有オブジェクトにSendController.so, RecvController.soの2つを指定しました。

Makefile
 #SIGVerseソースの場所指定
 SIG_SRC  = /home/<username>/sigverse-<version>/include/sigverse
 
 #オブジェクトファイルの指定
 OBJS     = SendController.so RecvController.so 
 
 all: $(OBJS)
 
 #コンパイルを行います。
 ./%.so: ./%.cpp
         g++ -DCONTROLLER -DNDEBUG -DUSE_ODE -DdDOUBLE -I$(SIG_SRC) -I$(SIG_SRC)/comm/controller  -fPIC -shared -o $@   $< 


 $ make

***世界ファイルの作成 [#me49924f]

次に世界ファイルを作成します。

 $ cd ..
 $ emacs xml/MessageWorld.xml

MessageWorld.xml
#highlight(xml){{
<?xml version="1.0" encoding="utf8"?>
 <world name="myworld3">
 
   <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++"/>
 
 <!--メッセージ送信者のコントローラ指定-->
         <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++"/>
 
 <!--メッセージ受信者のコントローラ指定-->
         <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>
</world>
}}
人型エージェントが、ヒューマノイド型エージェントにメッセージを送信します。
***起動 [#k2bccc17]

起動して見てみましょう。

 $ ./sigserver.sh -w MessageWorld.xml -p 9001

SIGViewerで接続して、シミュレーションを開始するとonInitにより、人型エージェント(メッセージ送信者)が手を下して、後ろを振り返ります。そして5秒に一回ロボット(メッセージ受信者)がメッセージに反応して手を挙げているのがわかります。

#ref(mess_1.jpg)

SIGViewerの下のほうにあるmessagesタブをクリックします。するとロボットがメッセージに反応して送信者と、文字列数、とその内容を表示します。

#ref(mess_2.jpg)

文字列の数(# of string)は1と表示されましたが、コントローラでメッセージ作成するときに以下のようにすることで、文字列は同時に複数送ることもできます。

   //メッセージを作成します。
   char *strs[] = {
     "Hello!!",
     "How are you",
   };

**テキスト文字列の送受信 [#a561e72f]

次にエージェント間でのテキスト文字列の送受信について説明します。テキスト文字列は人間が声を出すのと同じように、自分の声が届く範囲を指定することができます。

***テキスト送信者のコントローラ作成 [#wb648f75]
まずはテキストを送信するコントローラを作成します。

 $ cd NewWorld
 $ emacs SendText.cpp

SendText.cpp
#highlight(cpp){{
 #include "Controller.h"
 #include "Logger.h"
 #include "ControllerEvent.h"
 
 #define PI 3.141592
 #define DEG2RAD(DEG) ( (PI) * (DEG) / 180.0 )
 
 
 class SendController : public Controller
 {
 public:
   void onInit(InitEvent &evt);
   double onAction(ActionEvent &evt);
 };
 
 void SendController::onInit(InitEvent &evt)
 {
   SimObj *my = getObj(myname());
   if (!my->dynamics()) {
     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)
 {
 
   try
   {
     SimObj *my = getObj(myname());
 
     //自分の位置を得ます。
      double x = my->x();
     double y = my->y();
     double z = my->z();
 
     //z方向に20移動します。
     my->setPosition( x, y, z + 20 ); 
 
     //半径300.0以内のすべてのエージェントに対してテキスト"Hello!"を送ります。
     sendText(evt.time(), NULL, "Hello!", 300.0); 
 
   }catch(SimObj::Exception &) {
     ;
   }
   //3秒後にonActionが呼び出されます。
   return 3.0;
 } 
 
 
 extern "C"  Controller * createController ()
 {
   return new SendController;
 }
}}
これは3秒に1回テキスト"Hello!"を送信するコントローラです。sendText関数でテキストを送信します。sendTextの2番目の引数がNULL、4番目の引数が300.0となっています。こうすることにより、テキストを送信する相手を指定せず、周囲300(cm)にいるすべてのエージェントにテキストを送信します。テキストを送信する相手を指定したい場合は2番目の引数に指定するエージェントの名前を入力し、最後の引数は省略します。
これは3秒に1回テキスト"Hello!"を送信するコントローラです。sendText関数でテキストを送信します。sendTextの2番目の引数がNULL、4番目の引数が300.0となっています。こうすることにより、テキストを送信する相手を指定せず、周囲300(m)にいるすべてのエージェントにテキストを送信します。テキストを送信する相手を指定したい場合は2番目の引数に指定するエージェントの名前を入力し、最後の引数は省略します。

このエージェントはz方向にどんどん移動しながらテキストを送信します。つまりテキストを受信するエージェントからはどんどん遠ざかりながらテキストを送信します。

***テキスト受信者のコントローラ作成 [#jfd5b8e7]
テキスト受信者のコントローラを作成します。

 $ emacs RecvText.cpp

RecvText.cpp
#highlight(cpp){{
 #include <string>
 #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);
 
   //テキスト受信時に呼出される関数onRecvTextの利用を宣言します。
   void onRecvText(RecvTextEvent &evt);
 };
 
 
 double RecvController::onAction(ActionEvent &evt)
 {
   SimObj *my = getObj(myname());
   if (!my->dynamics()) {
      my->setJointAngle("LARM_JOINT2", DEG2RAD(0));
   }
   return 1.0;
 }
 
 //テキスト受信時
 void RecvController::onRecvText(RecvTextEvent &evt)
 {
   //自分自身を取得します。
   SimObj *my = getObj(myname());
 
   // テキストの送り主を取得します。
   const char *caller = evt.getCaller();
 
   //テキストの内容を取得します。
   const char *text = evt.getText();
 
   //送り主と内容を表示します。
   LOG_MSG(("receive from %s : \"%s\"", caller, text));
 
   //手を挙げます。
   if (!my->dynamics()) {
     my->setJointAngle("LARM_JOINT2", DEG2RAD(180));
   }
 
 }
 
 extern "C"  Controller * createController ()
 {
   return new RecvController;
 }
}}
先ほどと同様にテキスト受信時に手を上げます。

***コンパイル [#bbf536df]

コンパイルします。

 $ emacs Makefile
 
変更前
 #オブジェクトファイルの指定
 OBJS     = SendController.so RecvController.so

変更後
 #オブジェクトファイルの指定
 OBJS     = SendText.so RecvText.so




 $ make

***世界ファイルの編集 [#m7f88908]

次に世界ファイルを変更します。

 $ cd ..
 $ emacs xml/MessageWorld.xml

MessageWorld.xml

メッセージ送信者コントローラの修正
#highlight(xml:firstline[13]){{
 <!--メッセージ送信者コントローラ指定-->
       <set-attr-value name="implementation"
 value="./NewWorld/SendController.so"/>
}}
    ↓
#highlight(xml:firstline[13]){{        
 <!--メッセージ送信者コントローラ指定-->
       <set-attr-value name="implementation"
 value="./NewWorld/SendText.so"/>
}}

メッセージ受信者コントローラの修正
#highlight(xml:firstline[31]){{
 <!--メッセージ受信者コントローラ指定-->
         <set-attr-value name="implementation"
 value="./NewWorld/RecvController.so"/>
}}
    ↓
#highlight(xml:firstline[31]){{
 <!--メッセージ受信者コントローラ指定-->
         <set-attr-value name="implementation"
 value="./NewWorld/RecvText.so"/>
}}
 
***起動 [#o073f136]
それでは実行してみます。

 $ ./sigserver.sh -w MessageWorld.xml -p 9001

SIGVierwerで接続して、シミュレーションを開始してみます。
#ref(mess_3.jpg)

テキスト送信者が遠ざかりながらテキストを送信しているのがわかります。
エージェントの声がどこまで届くのか確認してみてください。

**テロップ表示 [#bb25270a]
***コントローラ修正 [#o04e7c0c]
送信したテキストデータをSIGViewerでテロップ表示させることができます。SendText.cppのsendText関数を以下のように修正します。
 $ cd NewWorld
 $ emacs SendText.cpp

SendText.cpp
#highlight(cpp:firstline[42]){{
     sendText(evt.time(), NULL, "Hello!", 300.0); 
}}
      ↓
#highlight(cpp:firstline[42]){{
     sendDisplayText(evt.time(), NULL, "Hello!", 64, "red", 300.0);
}}
sendDisplayTextは送信したテキストデータをテロップで表示させます。4番目の引数にはフォントサイズ、5番目の引数には色を指定することができます。ここではフォントサイズ64、色は赤色を指定しました。

色は"red","green","blue","purple","yellow","brown","gray"が指定できます。

**実行 [#s6f3180e]
それではコンパイルして実行してみましょう。
 $ make
 $ cd ../
 $ ./sigserver.sh -p 9001 -w MessageWorld.xml
#ref(telop.jpg)
テロップが表示されました。このテロップはテキストデータの届く範囲に関係なく表示されます。またテキストデータを受け取るエージェントはsendTextのときと同様にonRecvTextが呼び出されます。

sendDisplayTextと同様にsendMessage, broadcastMessageにもテロップを表示させる関数sendDisplayMessage, broadcastDisplayMessageがあります。これらのフォントサイズや色の指定方法はsendTextと同じです。また複数のメッセージを同時に送信するときには最初のメッセージのみがテロップ表示されるので注意が必要です。
#highlight(end)

Up:[[Tutorial]]     Previous:[[人間型エージェントの操作]]     Next:[[音声認識]]

[[English version>Samples/Communication between agents]]

Front page   Edit Diff Backup Upload Copy Rename Reload   New List of pages Search Recent changes   Help   RSS of recent changes