ROSで簡単なpythonパッケージを作成する(初心者向け)

[ROSで簡単なc++パッケージを作成する(初心者向け)](https://qiita.com/hiro_han/items/6f76ea77b5f1388b7207)でc++のパッケージの作成方法を説明しましたが、今回はpythonでの作成方法を説明します。
作成する内容は上記の記事と同じなので、ここでは違いがあるところだけ説明します。
最も大きな違いは`src`フォルダではなく`scripts`フォルダを作成し、そこにpythonのソースコードをおくところです。

# 今回作成するもの  
|                      | 説明                         |
|:---------------------|:----------------------------|
| sample_py_publisher  | データを送信するノード         |
| sample_py_subscriber | データを受信するノード         |
| sample_message       | ノード間でやりとりするメッセージ |

今回作成するファイルおよび編集するファイルは以下になります。(自動生成されるファイルの一部は省略しています)

```
catkin_ws
`- src
   `- sample_c
      |- msg
      |  `- sample_message.msg
      |- scripts
      |  |- sample_py_publisher.py
      |  `- sample_py_subscriber.py
      |- package.xml
      `- CMakeLists.txt
```

# パッケージを作成する
```
$ cd ~/catkin_ws/src
$ catkin_create_pkg sample_py rospy std_msgs
```
このコマンドを実行すると、`sample_py`というディレクトリとその中に`package.xml`、`CMakeLists.txt`が生成されます

# メッセージを作成する
メッセージファイルを作成します。  

```
$ cd ~/catkin_ws/src/sample_py
$ mkdir msg
$ cd msg
$ touch sample_message.msg
```
作成したファイルを編集し内部に含むデータを定義します。今回はstringと32byte unsigned intを含むこととします。  

```sample_message.msg
string message
uint32 count
```

このファイルよりメッセージを定義したpythonファイルをする必要があります。そのために、`CMakeLists.txt`を編集します。
`CMakeLists.txt`を開くと、様々な設定が記載されており一部がコメントアウトされています。今回は以下の箇所だけ編集すればよいです。  

```cmake:CMakeLists.txt

find_package(catkin REQUIRED COMPONENTS
   rospy
   std_msgs
   message_generation
)

add_message_files(
  FILES
  sample_message.msg
)

generate_messages(
  DEPENDENCIES
  std_msgs
)

catkin_package(
  CATKIN_DEPENDS message_runtime rospy std_msgs
)
```
次に`package.xml`を開いて編集します。以下2行がコメントアウトされているのでコメントタグを外します。  

```package.xml
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
```
最後にビルドし、pythonファイルを生成します。

```
$ cd ~/catkin_ws
$ catkin_make
```
ビルドが成功すると`catkin_ws/devel/lib/python2.7/dist-packages/sample_py/msg/_sample_message.py`というファイルが生成されます。

# 送信側(publisher)を作成する  
ファイルを生成し、以下のように実装します。

```
$ cd ~/catkin_ws/src/sample_py
$ mkdir scripts
$ cd scripts
$ touch sample_py_publisher.py
$ chmod +x sample_py_publisher.py
```
```python:sample_py_publisher.py
#!/usr/bin/env python
# license removed for brevity

import rospy
# 生成したメッセージのヘッダファイル
from sample_py.msg import sample_message


def publisher():
    # 初期化し、ノードの名前を"sample_c_publisher"とする
    rospy.init_node('sample_py_publisher', anonymous=True)
    # sample_messageというメッセージを"sample_topic"というトピックに送信する
    pub = rospy.Publisher('sample_topic', sample_message, queue_size=10)
    # 1秒間に2回データを送信する
    rate = rospy.Rate(2)

    count = 0
    while not rospy.is_shutdown():
        str = "hello world"
        rospy.loginfo("message = %s, count = %d" %(str, count))

        # 送信するメッセージの作成
        msg = sample_message()
        msg.message = str
        msg.count = count

        # 送信
        pub.publish(msg)
        rate.sleep()
        count += 1


if __name__ == '__main__':
    try:
        publisher()
    except rospy.ROSInterruptException: pass
```

# 受信側(subscriber)を作成する
ファイルを生成し、以下のように実装します。

```
$ cd ~/catkin_ws/src/sample_py/scripts
$ touch sample_py_subscriber.py
$ chmod +x sample_py_subscriber.py
```
```python:sample_py_subscriber.py
#!/usr/bin/python
# -*- coding: utf-8 -*-

import rospy
from sample_py.msg import sample_message


def callback(msg):
    # 受信したデータを出力する
    rospy.loginfo("I heard: message = [%s], count = [%d]" % (msg.message, msg.count));


def subscriber():
    # 初期化し、ノードの名前を"sample_py_subscriber"とする
    rospy.init_node('sample_py_subscriber', anonymous=True)
    # "sample_topic"というトピックからsample_messageというメッセージを受信する
    rospy.Subscriber('sample_topic', sample_message, callback)

    rospy.spin()

if __name__ == '__main__':
    subscriber()
```

# 作成したノードをビルド
```
$ cd ~/catkin_ws
$ catkin_make
```

# ROSを実行する
コンソールを３つ立ち上げ、それぞれ以下を実行します

```
$ roscore
```
```
$ rosrun sample_py sample_py_publisher.py
```
```
$ rosrun sample_py sample_c_subscriber.py
```

コンソールに以下の内容が出力されたら成功です。

```sample_c_publisher
[INFO] [1550978255.110806]: message = hello world, count = 0
[INFO] [1550978255.611362]: message = hello world, count = 1
[INFO] [1550978256.111434]: message = hello world, count = 2
...
```
```sample_c_subscriber
[INFO] [1550978260.112100]: I heard: message = [hello world], count = [10]
[INFO] [1550978260.612253]: I heard: message = [hello world], count = [11]
[INFO] [1550978261.112563]: I heard: message = [hello world], count = [12]
...
```
sample_py_subscriber.pyの実行タイミングによってカウンタの値がずれます。
また、[こちら](https://qiita.com/hiro_han/items/6f76ea77b5f1388b7207)で作成したc++のパッケージとメッセージの内容が同じなのでc++との通信もできます。  

```
$ rosrun sample_c sample_c_publisher
```
```
$ rosrun sample_py sample_c_subscriber.py
```




# 参考
- [公式チュートリアル](http://wiki.ros.org/ja/ROS/Tutorials)
 - [パッケージの作成](http://wiki.ros.org/ja/ROS/Tutorials/CreatingPackage)
 - [メッセージとサービスの作成](http://wiki.ros.org/ja/ROS/Tutorials/CreatingMsgAndSrv)
 - [パブリッシャとサブスクライバの作成(python)](http://wiki.ros.org/ja/ROS/Tutorials/WritingPublisherSubscriber%28python%29)