ROS
python2.7
indigo
プログラム作成

ROSでのメッセージを利用したプログラム作成

はじめに

この記事ではROSのメッセージを利用したプログラムの作成を行います。
少し工夫したものをROSで作るためには必須です。

メッセージを利用することによって、
自分の理想的な目的をROSを利用することで達成しましょう。

環境

ROS : indigo
OS : Ubuntu14.04
言語 : python2.7
参考ページ : https://kazuyamashi.github.io/ros_lecture/ros_study_py.html

その他

宇都宮大学を卒業された山科さんの記事を参考にしました。
情報系の私と研究としてはまったく利用方法が違いましたが、
ROSの基礎的な技術を学ぶには非常にわかりやすい記事がまとめてあります。

ROSを勉強するにあたり多く助けていただきました。

メッセージ作成

初めに送りたい変数が何かを作成します。
これにより自分の目的に合ったもの何でも作ることができるのです。

メッセージファイル作成

まずはプロジェクト内にmsgディレクトリを作成しましょう。

$ cd ~/ros_ws/src/ros_start
$ mkdir msg
$ vim Msg.msg

好きなテキストエディタでメッセージファイルに下記を記します。

int32 x
int32 y

C言語などと同じような書き方です。
最初に変数の型、次に変数名です。
今回はint32型の変数を2個作成します。

当然変数にはいろいろあるため、自分の
目的にあったものを利用しましょう。

  • int8, int16, int32, int64
  • float32, float64
  • string
  • time, duration
  • other msg files
  • variable-length array[] and fixed-length array[C]

CMakeListsテキストファイルの変更

メッセージファイルを元にメッセージ型を定義するヘッダファイルの生成を行います。

$ cd ~/ros_ws/src/ros_start
$ vim CMakeLists.txt

変更を1行でも間違えると生成できず通信を行うことができなくなるため、メッセージを利用する際にエラーが出たらまずここを疑いましょう。

プロジェクト名とメッセージファイル名によっては変わるので気をつけてください。

今回の点でいうと

  • プロジェクト名:ros_start
  • メッセージファイル名:Msg.msg

です。

#該当意部分がはコメント解除して適宜修正
#7行目あたり
find_package(catkin REQUIRED COMPONENTS
    roscpp
    rospy
    std_msgs
+ message_generation
)

#45行目あたり
## Generate messages in the 'msg' folder
add_message_files(
     FILES
- #   Message1.msg
- #   Message2.msg
+    Msg.msg
 )

#66行目あたり
generate_messages(
     DEPENDENCIES
     std_msgs
)

#104行目あたり
catkin_package(
#  INCLUDE_DIRS include
    LIBRARIES ros_start
    CATKIN_DEPENDS roscpp rospy std_msgs
    DEPENDS system_lib
)

間違えなく書き換えられたらcatkin_make

$ cd ~/ros_ws
$ catkin_make

catkin_makeが終えると以下のディレクトリにメッセージを定義したpythonコードが生成されます。

当然ですがCMakeLists.txtの書き方を間違えていると生成されません。
最初くらいは確認で実行してみてもいいでしょう。

$ ls ~/ros_ws/devel/lib/python2.7/dist_packages/ros_start/msg/
$ _Msg.py __init__.py

プログラム作成

やっとプログラムの作成を行えます。
前回と同じように必要な点についてのみ解説していきます。
行数をつけていないのでわかりづらいかもしれませんが頑張ってください。

プログラムの内容としてはPublisherが数字をSubscriberに対してPublishし、Subscriberがその数字をSubscribeします。
次にPublishする際は、前の数字をインクリメントしたものをPublishするといったものです。

Publisher

送信側のプログラム

publisher.py
#!/usr/bin/env python
# coding: UTF-8

import rospy
from ros_start.msg import Msg

def talk():
    pub = rospy.Publisher('input_data', Msg, queue_size=100)
    print('*** Publisher ***')
    r = rospy.Rate(1)
    x = 0
    y = 2
    msg = Msg()

    while not rospy.is_shutdown():
        msg.x = x
        msg.y = y
        pub.publish(msg)
        print('publish x=%d y=%d'%(msg.x, msg.y))
        x += 1
        y += 1
        r.sleep()

def main():
    rospy.init_node('publisher', anonymous=True)
    try:
        talk()
    except rospy.ROSInterruptException: pass

if __name__ == '__main__':
    main()

解説

初めにmain()が呼ばれrospy.init_nodeによってノード名がつけられます。
今回はpublisherというノード名をつけました。
その後talk()が呼び出されます。

    rospy.init_node('publisher', anonymous=True)
    try:
        talk()
    except rospy.ROSInterruptException: pass

呼び出されるとどのTopicに何のメッセージをどのくらいのサイズなのかを宣言します。

def talk():
    pub = rospy.Publisher('input_data', Msg, queue_size=100)

そして1秒間に何回Publishするのかを指定します。
今回の場合は1秒間に1回だけにしました。

変数x,yに対して代入し、
メッセージインスタンスを作成します。

ここでのx,yはメッセージ内のx,yではないので注意してください。
つまりこのままPublishしても中身には何も入っていません。

    r = rospy.Rate(1)
    x = 0
    y = 2
    msg = Msg()

while文の中で毎回メッセージ内のx,yに対して代入します。
代入が終了したらメッセージインスタンス自体をPublish
そしてx,yをインクリメント
最後にお決まりのr.sleep()で終了です。

Subscriber

次に受信側のプログラムです。

Subscriber.py
#!/usr/bin/python
# coding: UTF-8

import rospy
from ros_start.msg import Msg

def callback(msg):
    print('subscribe x=%d y=%d'%(msg.x, msg.y))

def listen():
    rospy.Subscriber('input_data', Msg, callback)
    print('*** Subscriber ***')
    rospy.spin()

def main():
    rospy.init_node('subscriber', anonymous=True)
    listen()

if __name__ == '__main__':
    main()

解説

正直言ってほとんど前回のものと同じです。

最初にノード名を指定
今回はsubscriberという名前にしました。
そしてlisten()が呼び出されます。

def main():
    rospy.init_node('subscriber', anonymous=True)
    listen()

SubscribeするTopic、なんのメッセージをSubscribeし、どの関数を呼び出すか。
したがってcallback()呼び出し。

def listen():
    rospy.Subscriber('input_data', Msg, callback)

今回のcallback()ではsubscribeしたx,yの値をprintします。

def callback(msg):
    print('subscribe x=%d y=%d'%(msg.x, msg.y))

プログラム実行

以前解説したので別方法で起動したいと思います。
私はこっちのやり方のほうが単純で好きです。

1つ目のコンソール

Masterを起動

$ roscore

2つ目のコンソール

起動するディレクトリに移動し直接実行
最初はSubscriber

$ cd ~/ros_ws/src/ros_start/scripts/
$ python subscriber.py

3つ目のコンソール

次にPublisher

$ cd ~/ros_ws/src/ros_start/scripts/
$ python publisher.py

おわりに

メッセージを利用することができれば一方通行のROSにおけるPublisher/Subscriberモデルの通信を使い放題です。

次回は私が研究で利用した双方向通信を可能にしたプログラム作成について解説します。