ROSで簡単な通信を行う(python)

##はじめに
ROSの初歩的なコードをまとめる。
自分のメモ代わり。
(公式チュートリアルの方がわかりやすいと思います。)
関連記事は以下のとおり。

| 記事タイトル  |  内容 |
|:----------------:|:-----------------:|
| [ROSでHello World](https://qiita.com/keinko/items/880ff30e5a4ae57c6315)    | ROSに慣れるために簡単な処理を行う|
| [ROSで簡単な通信を行う](https://qiita.com/keinko/items/7d0f26c19414291660d9)             | ROSの基本である通信の処理をテストする |
| [ROSを使って画像情報でロボットを動かす](https://qiita.com/keinko/items/c8f9390419cb1cbb6467)          | 少し実践的に複数のシステムを動かす |

##準備
###本当にROSを初めて触る人用の準備
`catkin_ws`という作業ディレクトリを作る。
この名前は慣例的に(？)決まっているものなのでROSを始める際につくりましょう。

```terminal
$ mkdir -p ~/catkin_ws/src
$ cd ~/catkin_ws/src
$ catkin_init_workspace
$ cd ~/catkin_ws
$ catkin_make
```
`catkin_init_workspace`は最初に一回やるだけで大丈夫。
ってか必要なのか、これ。

`catkin_make`で`devel`等のディレクトリが立ち上がる。
その後、`gedit ~/.bashrc`で`devel/setup.bash`を見ているか確認する。
`source ~/catkin_ws/devel/setup.bash`があれば先へ。
なければ一番下とかに記述しておく。

###パッケージの作成
ROSには「パッケージ」という概念がある。
とりあえず、なにか新しいプログラムをROSで動かしたいならパッケージを作成していくものだと思えば良い。
google検索する際も「ROS パッケージ 追加」とかで調べれば良い。
(ここらへんも正直、最新の公式チュートリアルを見た方がいい。)

```terminal
$ cd ~/catkin_ws/src
$ catkin_create_pkg easy_comu rospy roscpp std_msgs
```

`catkin_create_pkg`でパッケージを新規に作成できる。
次の引数、ここでは`easy_comu`がパッケージ名。好きな名前をつけましょう。
そのあとの引数が使用する依存パッケージ。とりあえず黙って書く。
その後、以下のようにcatkin_makeで変更を反映。

```terminal
$ cd ~/catkin_ws
$ catkin_make
$ source ~/catkin_ws/devel/setup.bash
```

パッケージがちゃんと生成されたか確認する方法として`roscd`がある。
これは、どのディレクトリにいてもrosのパッケージに飛べるコマンド。

```
$ cd
$ roscd easy_comu
```
これで正しく`easy_comu`ディレクトリに入れれば成功。

##簡単な通信システムの作成
###前提知識
ROSの主な機能は通信。
この通信は他の言語同士のファイルでも行うことができるから便利。
そしていろんな人がインターネット上にROSで動かせるモジュールを公開しているため便利。
ここでは基本的な通信をpythonによって行う。

以下の3つはROSをするなら覚えておく言葉。

- Publisher: 通信で何かを送る側。
- Subscriber: 通信で何かを受け取る側。
- Topic: 通信によって送られる何かそのもの。

関数の名前になっていたりするので、プログラムを検索するときなどに使いましょう。

###コーディング

まず、以下のようにディレクトリを準備する。

```terminal
$ roscd easy_comu
$ mkdir scripts
$ cd scripts
```

その後、好きなエディタで`talker.py`と`listener.py`を以下のように記述する。

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

import rospy
from std_msgs.msg import String # ROS通信で文字列を取得できるようにstd_msgsというパッケージからStringという型を取得
from datetime import datetime

rospy.init_node('talker') # ノードの生成
pub = rospy.Publisher('chatter', String, queue_size=10) # chatterという名前のTopicを生成し型やらを定義
rate = rospy.Rate(10) # 10Hzで動かすrateというクラスを生成
print("Conection started...")
while not rospy.is_shutdown():
    hello_str = String() # Stringというクラスで送信するメッセージ、"hello_str"を生成
    timestamp = rospy.get_time()
    time = datetime.fromtimestamp(timestamp) # ここらへんはROSと関係ないです
    hello_str.data = "hello world(%s)" % time # 内容の書き込み
    pub.publish(hello_str) # hello_strを送信！
    rate.sleep() # 先程定義したrateをここで動かす
```

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

import rospy
from std_msgs.msg import String

def callback(message):
    rospy.loginfo("get message! [%s]", message.data) # ターミナルへの表示

rospy.init_node('listener')
sub = rospy.Subscriber('chatter', String, callback) # chatterというTopicを受信！受信したら上で定義したcallback関数を呼ぶ
rospy.spin()
```

###実行
以下`chmod`のコマンドでpythonファイルを実行ファイルとして扱う。

```terminal
$ cd ~/catkin_ws/src/easy_comu/scripts
$ chmod 755 talker.py listener.py
```

ターミナルを3つ開き、以下の順番で実行する。

```terminal:ターミナル1
$ roscore
```

```terminal:ターミナル2
$ rosrun easy_comu talker.py
```

```terminal:ターミナル3
$ rosrun easy_comu listener.py
```

`rosrun`のルールとして先にパッケージ名、その後実行ファイルを引数として書く。
`rosrun`を使えば、どのディレクトリからでも実行することができる。

実行結果は以下のとおり。メッセージの送信が10Hzで行われているため、0.1秒に一回メッセージを取得できるはず…。

```terminal:実行結果
$ rosrun ros_start listener.py 
[INFO] [1561534794.212049]: get message! [hello world(2019-06-26 16:39:54.211102)]
[INFO] [1561534794.312196]: get message! [hello world(2019-06-26 16:39:54.311231)]
[INFO] [1561534794.412188]: get message! [hello world(2019-06-26 16:39:54.411242)]
[INFO] [1561534794.512196]: get message! [hello world(2019-06-26 16:39:54.511235)]
[INFO] [1561534794.612122]: get message! [hello world(2019-06-26 16:39:54.611119)]
[INFO] [1561534794.712045]: get message! [hello world(2019-06-26 16:39:54.711120)]
[INFO] [1561534794.812181]: get message! [hello world(2019-06-26 16:39:54.811233)]
[INFO] [1561534794.912186]: get message! [hello world(2019-06-26 16:39:54.911233)]
[INFO] [1561534795.012254]: get message! [hello world(2019-06-26 16:39:55.011237)]
[INFO] [1561534795.112203]: get message! [hello world(2019-06-26 16:39:55.111249)]
[INFO] [1561534795.212027]: get message! [hello world(2019-06-26 16:39:55.211132)]
[INFO] [1561534795.312287]: get message! [hello world(2019-06-26 16:39:55.311287)]
[INFO] [1561534795.412052]: get message! [hello world(2019-06-26 16:39:55.411121)]
```
###補足
`talker.py`の中の、

```python
hello_str.data = "hello world(%s)" % time # 内容の書き込み
```
この記述における`data`ってなんやねんと思った人へ。
`data`はその数行前で定義しているROSのStringクラスのメンバ変数です。
プログラミングに疎い方は、ROSが用意してくれている文字列の形式"String"にあらかじめ`data`っていう名前の変数が定義されていたと思ってください。

メンバ変数の調べ方は以下のコマンドで行えます。

```terminal
$ rosmsg show std_msgs/String
string data
```

string型でdataって名前で定義されていることがわかります。
まあ、公式サイトで調べるのが一番！