Tutorial?

エージェント間でのメッセージのやり取り

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

メッセージの送受信

メッセージ送信者のコントローラ作成

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

$ emacs SendController.cpp

SendController.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を用いて以下のように、メッセージを送る相手を指定します。

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

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

$ emacs Makefile

変更前

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

変更後

#オブジェクトファイルの指定
OBJS     = SendController.so RecvController.so

共有オブジェクトにSendController.so, RecvController.soの2つを指定しました。

$ 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++"/>

<!--メッセージ送信者のコントローラ指定-->
        <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>

人型エージェントが、ヒューマノイド型エージェントにメッセージを送信します。

起動

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

$ ./sigserver.sh -p 9001

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

mess_1.jpg

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

mess_2.jpg

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

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

テキスト文字列の送受信

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

テキスト送信者のコントローラ作成

まずはテキストを送信するコントローラを作成します。

$ emacs SendText.cpp

SendText.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(m?)にいるすべてのエージェントにテキストを送信します。テキストを送信する相手を指定したい場合は2番目の引数に指定するエージェントの名前を入力します。

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

テキスト受信者のコントローラ作成

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

$ emacs RecvText.cpp

RecvText.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;
}

先ほどと同様にテキスト受信時に手を上げます。

コンパイル

コンパイルします。

$ emacs Makefile

変更前

#オブジェクトファイルの指定
OBJS     = SendController.so RecvController.so

変更後

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

世界ファイルの編集

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

$ emacs xml/MyWorld.xml

編集前

<!--メッセージ送信者コントローラ指定-->
      <set-attr-value name="implementation"
value="./NewWorld/SendController.so"/>

<!--メッセージ受信者コントローラ指定-->
        <set-attr-value name="implementation"
value="./NewWorld/RecvController.so"/>

編集後

<!--メッセージ送信者コントローラ指定-->
      <set-attr-value name="implementation"
value="./NewWorld/SendText.so"/>

<!--メッセージ受信者コントローラ指定-->
        <set-attr-value name="implementation"
value="./NewWorld/RecvText.so"/>

起動

それでは実行してみます。

$ ./sigserver.sh -p 9001

SIGVierwerで接続して、シミュレーションを開始してみます。

mess_3.jpg

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


Front page   New List of pages Search Recent changes   Help   RSS of recent changes