eSEATでROSノードを作る
Simple Wiki Based Contents Management System
ソフトウェア関連 >> RTコンポーネント関連 >> eSEAT2 >> eSEATでROSノードを作る

eSEATでROSノードを作る

eSEATでは、ROSのPublisherとSubscriber, Serviceを作ることができます。
下のファイルは、Pub/Subを使った簡単な例です。Joystickのトピック(/joy)をturtlesim_nodeのトピック(/turtle1/cmd_vel)に変換するROSノードを作成したものです。
これでジョイスティックで亀を操作することができます。
 <?xml version="1.0" encoding="UTF-8"?> 
 <seatml>
  <general name="Joy2TurtleSim">
    <adaptor name="/turtle1/cmd_vel" type="ros_pub" datatype="geometry_msgs/Twist" size="1" />
    <adaptor name="/joy" type="ros_sub" datatype="sensor_msgs/Joy" />
  </general>
  <state name="main_mode">
    <rule source="/joy">
      <script>
       <!--
       if rtc_in_data.axes[1] != 0:
         lv=rtc_in_data.axes[1]
       else:
         lv=0
       if rtc_in_data.axes[0] != 0:
         rv=rtc_in_data.axes[0]
       else:
         rv=0
 
       seat.ros_publish("/turtle1/cmd_vel" , [[lv,0,0], [0,0,rv]])
       -->
      </script>
    </rule>
  </state>
 </seatml>

また、eSEATで作成できるGUIを使って、ROSロボット用の簡単なGUIを作ることもできます。
なお、scriptタグの内部はコメントになっていても問題ありません。むしろ '>'を使う場合には、コメントにしなければパーザーでエラーが発生します。
以下では、eSEATにおけるROSへの対応について説明していきます。

Publish/Subscribeメッセージへの対応

eSEATでは、RTMのデータポート、サービスポート、Webアダプタもすべてadaptorというインターフェースを通してデータ交換を行っています。したがって、eSEATのROS対応に関しても adaptor を設定することで、publisherとSubscriberを作ることができます。
上の例では、
    <adaptor name="/turtle1/cmd_vel" type="ros_pub" datatype="geometry_msgs/Twist" size="1" />
    <adaptor name="/joy" type="ros_sub" datatype="sensor_msgs/Joy" />
というようにadaptorタグのtype属性を ros_pub または ros_sub を設定します。この時
属性説明
type'ros_pub'(パブリッシャー)または 'ros_sub'(サブスクライバ)のどちらかの値
nameトピック名(識別子として使用します)
datatypeパケージ名+'/'+メッセージ型
size'ros_pub'の場合に queue_sizeに設定する値 (通常 1)
callback'ros_sub'の場合に指定したメッセージを受け取った時に動作するコールバック関数。 省略時は、eSEAT_Core.onDataが呼ばれるようになています。
file'ros_pub'の場合に、インポートしたい外部Pythonプログラムファイル(file_exec関数で実行します)
datatype としては、std_msgs, geometry_msgs, sensor_msgsをデフォルトで使用することがしてできます。
その他のパッケージに定義されているメセージ型を使いたい場合には、generalタグの中にscriptタグを挿入して、そこにimportするコードを挿入する必要がある場合があります。
また、独自のパッケージでメッセージ型を定義した場合には、起動ディレクトリに ros/packages/[package-name]/msgsを作成して、その中にメッセージ型の定義を入れておきます。そして、実行前に gen_ros_msg.py を実行すれば、msgs内に定義されたメッセージ型をPythonでロード可能なコードに変換し、ros/lib/site-packages/[package-name] の下に配置します。この場合は、eSEATで自動的にimportされるはずですが、問題があれば連絡をお願いいたします。
例えば、beginner_tutorialsを作っておけば、
 <general>
   ....
   <adaptor type='ros_pub' name='/add_num' datatype='beginner_tutorials/Num' size=1 />
   ....
 </general>
のように記述することで独自型のPublisherを生成することができます。
ちなみに、roslsで表示されるノード名は、 /seat+'ランダムな数字の列' となっていると思います。
現在は、seatmlの generalタグのname属性で指定した名前になっています。また、generalタグにはanonymous属性を指定できるようにしています。anonymous属性を指定するとrospy.init_node関数へanonymous=Trueとして実行され、ノード名は、name+'_pid_'+'生成した時刻(time値)'となります。
ros_pubのアダプタへのデータ出力は、scriptタグの sendto属性で指定するか、scriptタグ内で
  seat.do_publish(topic_name, data)
で実行することができます。このあたりは、ROSのプログラムと同じです。
ros_subアダプタの場合には、callback属性を指定する場合には、外部で定義した関数を用意し、file属性で指定しておく必要があります。
上の例では、ros_subアダプタでcallback属性を指定していません。この場合rtc_inアダプタと同じ振る舞いをしますので、std_msgs/Stringの場合はruleタグでのマッチング処理、それ以外の場合には、ruleタグで source属性でアダプタを指定して使います。

Ros Serviceへの対応

ROSでは、トピック通信の他にServiceという機能があります。Serviceとよく似た機能でactionというのもありますが、eSEATでは、Serviceのみをサポートしています。
Serviceに関しては、OpenRTM-aistのサーピスポートと同じように、ros_serverとros_clientというアダプタを生成することで、サービスの呼び出しとサービス機能の提供を行っています。
Serviceを使ったサンプルとして、 examples/RosServer.seatml と examples/RosClient.seatmlを用意していますので、見ていただけば大体理解できるとは思います。
仕様変更のためサンプルを削除しました
examples/RosServer.seatmlでは、ros_serverとros_clientを下のように定義しています。
    <adaptor name="myservice0" type="ros_server" service="add_two_ints"
       service_type="beginner_tutorials.AddTwoInts" 
          file="examples/myservice.py" impl="handle_add_two_ints" />

    <adaptor name="myservice1" type="ros_client" service="add_two_ints"
       service_type="beginner_tutorials.AddTwoInts" />
ここでは、myservice0とmyservice1の2つのアダプタを定義しています。nameは、アダプタの識別子ですので異なる名前にする必要があります。
属性説明
type'ros_server'(サーバー)または 'ros_client'(クライアント)のどちらかの値
name識別子 Rosのサービス名または識別子、Rosサービス名の場合には、識別子は、Rosサービス名+'_'+ type'に変換される
serviceRosのサービス名
service_typeRosのサービスタイプ
impl'ros_server'の場合にサービスが実装された関数名を指定します
file'ros_server'の場合に、インポートしたい外部Pythonプログラムファイル(file_exec関数で実行します)
ros_serverアダプタは、生成時に rospy.Service関数を呼び出して、サービスを定義し、ros_clientアダプタでは、rospy.ServiceProxy関数を呼び出しています。このあたりは、ROSのチュートリアルとほとんど同じです。
ただし、ros_serverアダプタでimpl属性で指定するサービス関数は、ROSのチュートリアル等で記載されているものと若干異なり、サービス関数からの返り値を eSEATの内部でXXXResponse関数に引き渡しています。これによって余計なパッケージのimportを書かなくても良いようにしています。
eSEAT内で、ros_clientアダプタから外部のROSサービスを呼び出す場合には、
  retval=seat.callRosService(サービス名, サービス関数へ引き渡す引数)
としてscriptタグ内に記載します。
まだ、ROSの連携機能については、仕様が定まらないために、具体的な使い方で疑問、質問、変更してほしい仕様があれば、連絡をお願いいたします。

RosNodeを使ったサンプル