ROS2入門[Python3] - Subscription

# 序章
前回はPublisherについてアルゴリズムを学び、基本的な動きについて理解した。
※https://qiita.com/l1sum/items/6acabc94b040f8b0f7cd

今回はSubscriberのアルゴリズムを理解し、最後に前回作成したPublisherから文字データを受け取ることをする。

前回同様に環境は、
Python3 -- 3.6.5
macOS -- High Sierra 10.13.4
である。

# ROS2(Subscriber)
## 〜アルゴリズム〜

1.　rclpy.init()<font color="red">でRCLの初期化?を実行</font>
2.　rclpy.node.Node(node_name)<font color="red">はnode_nameを渡してインスタンス化</font>
3.　<font color="red">Nodeの継承クラスにcallback関数を作成。callback関数は引数にmsgを定義する。</font>
4.　rclpy.node.Node.create_subscription(msg_type, topic, callback)<font color="red">でmsg_type、topic、callbackを渡し、Subscriberを作成する。</font>
(3, 4は書き方によって変わることがある)
5.　rclpy.spin(rclpy.node.Node)<font color="red">でループに入り処理を行う。</font>
6.　rclpy.node.Node.destroy_node<font color="red">でNodeを破壊する。</font>
7.　rclpy.shutdown()<font color="red">でRCLシャットダウンする。</font>

## 〜プログラム(Python3)〜

### +簡易プログラム+
では、上記のアルゴリズムを参考に簡易的なプログラムを作成してみましょう。

```python3:listener.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import String

# rclpy.init()でRCLの初期化？を実行
rclpy.init()

# Node(node_name)で引数node_nameにlistenerを渡してコンスタンス化
node = Node("listener")

# callback関数を作成
def callback(msg):
    print("{0}".format(msg))

# nodeからsubscriptionの作成
node.create_subscription(String, "chatter", callback)

# ループに入り、nodeの処理を行わせる
rclpy.spin(node)

# node.destroy_node()でnodeを破壊する。
node.destroy_node()

# RCLの終了
rclpy.shutdown()
```
### +汎用プログラム+

```python3:subscriber
import rclpy
from rclpy.node import Node
from std_msgs.msg import String

# Nodeクラスを継承
class Listener(Node):
    def __init__(self):
        # 引数node_nameにlistenerを渡す。
        super().__init__("listener")

        # 引数msg_type、topic_name、callbackに渡してSubscriptionを作成
        self.create_subscription(String, "chatter", self.callback)

    # 処理用callback関数
    def callback(self, msg):
        print(msg.data)


def main():
    # RCLの初期化を実行
    rclpy.init()

    # Listenerクラスのコンスタンス化
    node = Listener()

    # ループに入りnode内の処理を実行させる
    rclpy.spin(node)

    # nodeを破壊
    node.destroy_node()

    # RCLをシャットダウン
    rclpy.shutdown()

if __name__ == "__main__":
    main()
```
このプログラムにおいてはcallback関数の内部を書き換えていくことで、より複雑な処理を行うことができます。

## 〜PublisherとSubscriber〜

前回のプログラムと今回のプログラムを実行することで、Topic間通信を行うことができます。

```:talker_float_py.pyの結果
l1sum$ python3 talker_float_py.py

[INFO] [talker]: Publishing: HELLO PUBLISHER! [1]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [2]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [3]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [4]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [5]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [6]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [7]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [8]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [9]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [10]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [11]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [12]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [13]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [14]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [15]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [16]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [17]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [18]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [19]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [20]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [21]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [22]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [23]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [24]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [25]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [26]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [27]
[INFO] [talker]: Publishing: HELLO PUBLISHER! [28]
```

```:listener.pyの結果
l1sum$ python3 listener.py

HELLO PUBLISHER! [1]
HELLO PUBLISHER! [2]
HELLO PUBLISHER! [3]
HELLO PUBLISHER! [4]
HELLO PUBLISHER! [5]
HELLO PUBLISHER! [6]
HELLO PUBLISHER! [7]
HELLO PUBLISHER! [8]
HELLO PUBLISHER! [9]
HELLO PUBLISHER! [10]
HELLO PUBLISHER! [11]
HELLO PUBLISHER! [12]
HELLO PUBLISHER! [13]
HELLO PUBLISHER! [14]
HELLO PUBLISHER! [15]
HELLO PUBLISHER! [16]
HELLO PUBLISHER! [17]
HELLO PUBLISHER! [18]
HELLO PUBLISHER! [19]
HELLO PUBLISHER! [20]
HELLO PUBLISHER! [21]
HELLO PUBLISHER! [22]
HELLO PUBLISHER! [23]
HELLO PUBLISHER! [24]
HELLO PUBLISHER! [25]
HELLO PUBLISHER! [26]
HELLO PUBLISHER! [27]
HELLO PUBLISHER! [28]
```
画面はこのような感じになっていると思います。
これを利用すれば、プログラム間での送受信やROSとの連携もうまくいくのではないでしょうか。(ROSはやったことないので)
ここで重要なことがあります。
Node.create_publisher関数または、Node.create_subscription関数どちらかの引数topicに渡す文字を変更し、再度実行してみてください。
受信ができていないと思います。つまり、topicを送受信同士で合わせておかないとうまくコミュニケーションが取れないということになります。
これで通信の競合を回避していることがわかります。

# まとめ
これでTopic間通信ができるようになりました。
またtopicを変更することで競合を回避し、通信ができるということがわかりました。
これを利用することで複数のノードを何重にも構築し、複雑な通信処理が構築できるでしょう。

# 次回
[GAZEBOを動かす！](https://qiita.com/l1sum/items/78f1a904fff3d389d8be)