ROS講座107 rosblidgeのclientを作る

# 環境
この記事は以下の環境で動いています。

| 項目 | 値 |
|:-:|:-:|
| CPU | Core i5-8250U |
| Ubuntu | 16.04 |
| ROS | Kinetic |
| Gazebo | 7.0.0 |
| python | 2.7.12 |

インストールについては[ROS講座02 インストール](https://qiita.com/srs/items/e0e0a9dc3f94c2d3348e)を参照してください。
またこの記事のプログラムはgithubにアップロードされています。[ROS講座11 gitリポジトリ](https://qiita.com/srs/items/25a56a6abda41dcf396e)を参照してください。

# 概要
[前回](https://qiita.com/srs/items/d3ec9962f7814d7b16f5)にrosbridge_serverを立てて、ブラウザのjavascriptからアクセスする例を紹介しました。今回は`roslibpy`を使ってrosbridge_serverに接続するclientをpythonで作ります。

# インストール

## pipの問題
今回はroslibpyをインストールするのにpipを使います。pip(というよりもpython)の困ったこととしてインストールするディレクトリが様々で、混ざるとかなり面倒なことになります。例えば

* システム領域(`/usr/local/lib/python2.7/`)  
aptでpipを取得した場合・`sudo pip`で取得した場合
* ユーザー領域(`~/.local/lib/python2.7/`)  
`pip --user`で入れた場合

これにより以下のコマンドを実行すると環境が汚れてpipでエラーが起きます

```shell:これを実行後にエラーが起きる
sudo apt install python-pip
pip install --upgrade pip
```

aptとpipを併用すると問題が多いのでaptを使用せずにインストールするのが良いです。上記を行ってしまった時の対処法として以下のコマンドでpipをすべてアンインストールできます。

```shell:pipのアンインストール
sudo python -m pip uninstall pip
sudo apt autoremove python-pip
```

## 実行すべきコマンド

以下を実行するとroslibpyがインストールできます。

```shell:roslibpyのインストール
curl -kL https://bootstrap.pypa.io/get-pip.py | sudo python
sudo pip install roslibpy
sudo pip install service_identity
```

# ソースコード

```python:web_lecture/scripts/rosbridge_client.py
#!/usr/bin/env python
# -*- coding:utf8 -*-
import time
from roslibpy import Message, Ros, Topic
import time

class rosbridge_client:
    def __init__(self):
        self.ros_client = Ros('127.0.0.1', 9090)
        print("wait for server")
        self.publisher = Topic(self.ros_client, '/cmd_vel', 'geometry_msgs/Twist')
        self.listener = Topic(self.ros_client, '/odom', 'nav_msgs/Odometry')
        self.listener.subscribe(self.callback)

        self.ros_client.on_ready(self.start_thread, run_in_thread=True)
        self.ros_client.run_forever()

    def callback(self, message):
        x = message["pose"]["pose"]["position"]["x"]
        y = message["pose"]["pose"]["position"]["y"]
        print(x, y)

    def start_thread(self):
        while True:
            if self.ros_client.is_connected:
                self.publisher.publish(Message({
                    'linear': {
                        'x': 0.5,
                        'y': 0,
                        'z': 0
                    },
                    'angular': {
                        'x': 0,
                        'y': 0,
                        'z': 0.5
                    }
                }))
            else:
                print("Disconnect")
                break
            time.sleep(1.0)

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

* `self.ros_client = Ros('127.0.0.1', 9090)`でrosbridge_serverへの接続を設定します。
* `self.ros_client.is_connected`でrosbridge_serverに接続しているかを判定します。
  * 切れていても`self.ros_client.connect()`で再接続を試みます。 
* `Message()`でメッセージをjsonで記述して`self.publisher.publish()`でpublishします。
* `self.listener.subscribe(self.callback)`でsubscribeした時のcallbackを登録します。callback関数の引数で受信メッセージを渡せれて、`x = message["pose"]["pose"]["position"]["x"]`のようにアクセスします。
* `self.ros_client.on_ready(self.start_thread, run_in_thread=True)`でスレッドを生成できます。

# 実行

```shell:simの実行
roslaunch web_lecture web_if.launch 
```

```shell:roslibpyのクライアントの実行
roscd web_lecture/scripts && rospy_client.py`で実行できます。
```

![web_client.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254442/0c5ac23a-11a8-4463-a0fb-cbe69bae6064.gif)

# ユースケース
rosbridgeは本来rosがインストールされていないシステムからrosにアクセスするためのツールです。しかし、この疎結合のシステムを使うと下記の図のようにマルチマスターのシステムを組むこともできます。
![rosbridge_connection.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254442/0d9f0b20-9456-994d-ec7e-c5bc81a5dd18.png)



# 参考
[roslibpyのexample](https://roslibpy.readthedocs.io/en/latest/examples.html#putting-it-all-together)
[実装についての詳細](https://roslibpy.readthedocs.io/en/latest/_modules/roslibpy/ros.html#Ros)

# 目次ページへのリンク
[ROS講座の目次へのリンク](https://qiita.com/srs/items/5f44440afea0eb616b4a)