【Python】ROSのプログラムをPythonのclassを使ったらとても便利だった

# はじめに
最近少しずつROSを触り始めていて、Pythonでノードを実装するときに**1つのノード内でpublisherとsubscriberを実装する**にあたって困ったポイントがあったので紹介します。Pythonのクラスを使うことできれいにすることができました。
自分でパッケージが作れる程度のROSの知識と少しのオブジェクト指向の知識がある前提です。

実装例は[こちら](https://qiita.com/koichi_baseball/items/d15d52856188120647f4#publish-%E3%81%A8-subscribe%E3%82%92%E5%90%8C%E6%99%82%E3%81%AB1%E3%81%A4%E3%81%AE%E3%83%8E%E3%83%BC%E3%83%89%E3%81%A7%E8%A1%8C%E3%81%86)

# やりたかったこと
このトピックのデータをsubscribeして、処理をして、あのトピックにpublishしたいな

ということがあると思います。調べ方が悪かったのか**1つのノードで同時にsubscriberとpublisherを使う**ときはどうすればいいんだ、、、
と、かなりはまりました。

# Pythonで実装するROSのPublisherとSubscriber
[ROS.org](http://wiki.ros.org/ja/ROS/Tutorials/WritingPublisherSubscriber%28python%29)やよく紹介されているPublisherとSubscriberのチュートリアルでは以下のようになっていることが多いと思います。

### Publisher(talkerとも)
`rospy.Publisher` で トピック名とメッセージのタイプを指定して、`pub.publish()`でpublish。

```Python:talker.py
# rospyとメッセージのimport
import rospy
import std_msgs.msg import String

def talker():
    pub = rospy.Publisher('chatter', String, queue_size=10)
    rospy.init_node('talker', anonymous=True)

    # 処理が続く
    
    pub.publish(data)

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

### Subscriber(listenerとも)
`rospy.Subscriber`でトピック名、データの型の指定、そしてcallback関数を指定してsubscribe後の処理をcallback関数で行う、という形になっています。


```Python:listener.py
# rospyとメッセージのimport
import rospy
from std_msgs.msg import String

def callback(data):
    # subscribeのcallback関数
    
def listener():
    rospy.init_node('listener', anonymous=True)

    rospy.Subscriber("chatter", String, callback)

    rospy.spin()
        
if __name__ == '__main__':
    listener()
```

# Publish と Subscribeを同時に1つのノードで行う
チュートリアルが終わって、PublishとSubscribeの仕組みなどは一応理解しました。
じゃあ新しいノードを作ってPublishとSubscribe同時にやろうかなー。と思い、実装してみると `rospy.Subscriber`のcallback関数内でPublishすればいいのか、、？とコードをきれいにできずに悩みました。

その時に[この記事](https://qiita.com/okanosyogo/items/d62e6471ca3bb2877b0c)を見ていて、[コードも拝見した](https://github.com/OkanoShogo0903/ros_openpose_joint_converter/blob/master/scripts/main.py#L10)ところとてもきれいだったので参考にさせていただきました。
上記のチュートリアルではPythonのクラスを使っていませんでしたが、クラスを使うときれいに実装することができます。

`__init__()`でPublisher, Subscriberを作成し、callback関数、Publishする関数という流れで処理をしていきます。
実行する際にはmainの部分でクラスを作成すれば完了です！

```Python:test.py
#! /usr/bin/env python
# -*- coding: utf-8 -*-

import rospy
from std_msgs.msg import String

import time

class testNode():
    def __init__(self):
        # Subscriberの作成
        self.sub = rospy.Subscriber('topic name', String, self.callback)
        # Publisherの作成
        self.pub = rospy.Publisher('topic name', String, queue_size=1)

    def callback(self, data):
        # callback関数の処理をかく

        self.publish(data)

    def publish(self, data):
        self.pub.publish(data)

    def function(self, data):
        # そのほかの処理もあったら書く

        return data

if __name__ == '__main__':
    rospy.init_node('test_node')

    time.sleep(3.0)
    node = testNode()

    while not rospy.is_shutdown():
        rospy.sleep(0.1)
```

# おわりに
個人的にオブジェクト指向はとっかかりづらいと思っていましたが便利にきれいに書くことができました。
[こんな感じ](https://qiita.com/MENDY/items/e842e3c3ad7fe98c12a0)でテンプレートを作ってくれている方もいますね！

"ros python publish subscribe 同じノード" とかの検索に欲しい情報なかったです。。。ROS初心者すぎるから自分が見当違いなことしているのですかね。なにかおかしかったら教えてください。

# 参考
[ROSの機械学習用に、OpenPoseから関節角度をTopicで流すパッケージ作った](https://qiita.com/okanosyogo/items/d62e6471ca3bb2877b0c)
[ROS講座31 python基礎](https://qiita.com/srs/items/a0ce380687d9cc896404)
[ROS のプログラムテンプレート](https://qiita.com/MENDY/items/e842e3c3ad7fe98c12a0)
[シンプルな配信者(Publisher)と購読者(Subscriber)を書く(Python)](http://wiki.ros.org/ja/ROS/Tutorials/WritingPublisherSubscriber%28python%29)