ROS講座21 複数のPCでROS接続2

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

| 項目 | 値 |
|:-:|:-:|
| CPU | Core i5-8250U |
| Ubuntu | 16.04 |
| ROS | Kinetic |

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

# 概要
前回(ROS講座20)では複数のPCでROSを実行する方法を説明しました。しかし、通信する両方のPCのターミナルを操作する必要があったり、人力の作業が多かったりと面倒です。そこでremote launchを行うことでコマンド1発でリモート接続＋ROSノードの起動ができるようにしましょう。
今回はROSを立ち上げる複数のPCで以下ができる必要があります。

* 固定アドレスが割り当てられていること
* note1->robot1にパスワード認証でsshができること
* robot1側にcatkin_wsがあってビルドをしていること

# PC・ネットワーク構成
前回(ROS講座20)と同じです。固定アドレスとします。
![リモート2.png](https://qiita-image-store.s3.amazonaws.com/0/254442/511948a8-ec04-b274-b8be-ee46fb0fbd55.png)

# 接続確認
前回も行いましたがpingに加えてsshでの接続の確認も必須です。

```shell:note1のターミナルで
ssh r_user@192.168.1.20
```

# robot1側での設定
robot1側では前回と同じく`ROS_IP`の設定が必要です。一方`ROS_MASTER_URI`の設定は後述のlaunchによって自動的に行われるので不要です。しかし今回はrobot1側はターミナルを起動して設定するのではなくssh経由で起動されます。この時の環境変数などの設定はrobot1側のディレクトリの`~/catkin_ws/devel/env.sh`に書きます。remote launchをされたときに最初にこのスクリプトが実行されます。

```shell:~/catkin_ws/devel/env.shに追加
export ROS_IP=`hostname -I | cut -d' ' -f1`
```

# note1側の設定
roslaunchコマンドを実行するマシンであるnote1側の設定です。`ROS_IP`、`ROS_MASTER_URI`に加えて`ROSLAUNCH_SSH_UNKNOWN`という3つ環境変数が必要になります。

```shell:note1でのみ実行
export ROS_IP=`hostname -I | cut -d' ' -f1`
export ROS_MASTER_URI=http://`hostname -I | cut -d' ' -f1`:11311
export ROSLAUNCH_SSH_UNKNOWN=1
```

* 'ROS_MASTER_URI'はros masterのアドレス・ポートを設定する環境変数です。デフォルトでは`http://localhost:11311`となっていてnote1内でのみROSを動かす分には問題はなありません。しかし、remote launchを行う場合は、親側（roslaunchを行うPC）の'ROS_MASTER_URI'が子側にコピーされます。この時に`localhost`のままだと意味をなさないので、具体的なIPアドレスを記述する必要があります。
* `ROSLAUNCH_SSH_UNKNOWN`はssh接続する相手としてknown_hostにないマシンへのssh接続を許可するかどうかという設定です。1を代入することで許可になります。

この3行をbashrcに入れておいても良いでしょう。

# launchのソースコード
note1側で起動するlaunchファイルを示します。以下のlaunchではrobot1のROSも同時に立ち上げることができます。

```xml:net_lecture/launch/remote_simple.launch
<launch>
  <machine name="robot1" address="192.168.1.20" env-loader="/home/r_user/catkin_ws/devel/env.sh" user="r_user" password="password"/>
  
  <node name="basic_simple_listener" pkg="basic_lecture" type="basic_simple_listener" output="screen" />
  <node machine="robot1" name="basic_simple_talker" pkg="basic_lecture" type="basic_simple_talker" />
</launch>
```

* 完全に新しく出てきた要素として`<machine>`タグがあります。これはノードを実行するPCを指定するものです。
    * name属性は自由につけてよい名前です。
    * address属性はそのロボットのIPアドレスを示します。robot1側のIPアドレス（192.168.1.20とか）を書きます。
    * env_lodar属性は初期化用のスクリプトを指定します。robot1側でlaunchが立ち上がる前に実行されるスクリプトです。上記で編集したrobot1でのファイルパスを指定します。つまりrobot1用のmachineタグで`env-loader="/home/n_user/.....`のようにnote1側のファイルを選択しようとしてもエラーになります（ファイルを転送等がされているわけではないです）。
    * userとpassword属性はssh接続で使うユーザーとパスワードです。平文でパスを書くのはあまりよろしくないですが、この方法が一番お手軽です。

* `<node>`タグの中のmachine属性でそのROSノードを起動するPCを選択できます。この属性を書かないと今までの例のようにroslaunchを起動したPCでROSノードが起動します。このリモート起動を記述するうえで重要なのはmachineで指定したPCにそのROSノードがないとエラーになるということです
例えばnote1上にはtest_pkgというROSパッケージがあって、robot1上にはそのパッケージがない場合。`<node name="ros_node" pkg="test_pkg" type="test_pkg_node" />`とした場合、普通にROSノードがnote1上で実行されます。`<node machine="robot1" name="ros_node" pkg="test_pkg" type="test_pkg_node" />`とした場合、robot1上でROSノードが起動されるとうれしいのですが、エラーになります。リモートlaunchの機能ではROSノードをネットワーク越しに転送して実行することはできず、指定したPCにあるROSノードを起動することしかできません。

# 実行
以下のように普通のROSlaunchと同じように起動できます。

```shell
roslaunch basic_lecture simple_remote.launch 
```

`rosnode machine`と打つと現在のrosmasterにつながっているマシンの一覧が表示されます。

```text:結果
192.168.1.10
192.168.1.20
```

`rosnode machine 192.168.1.10`と打つとそのマシンで実行されているrosノードの一覧が出ます。

```text:結果
/basic_simple_listener
/rosout
```

# リモートで起動しているマシンがshutdownした場合
roslaunchを停止するのではなく、remote側でprocessをkillしたりshutdownした場合や、マシンの電源が切れた場合には不都合が起きます。

* shutdown(reboot)するとlaunchの画面には出てくるけどnodeは死なない
* rosnode infoで見るとつながらないと表示される
* rosnode killが効かない（roscoreを再起動しない限り表示され続けるゾンビノードになる）
    
# 目次ページへのリンク
[ROS講座の目次へのリンク](https://qiita.com/srs/items/5f44440afea0eb616b4a)