[ROS自動起動] robot_upstart vs 自分で書く

# はじめに
ロボット制御ROSを自動起動する必要があり、ROSパッケージの[robot_upstart](http://wiki.ros.org/robot_upstart)と
[ROSを自動起動する](https://qiita.com/strv/items/535a370842ae60af9b64)を参考に自分で書く、2パターンでトライしてみましたのでそのメモです。
結論としては、使うデバイスやLaunchファイルの数が少ないシンプルな構成の時は`robot_upstart`、デバイスやPermissionが複雑な構成の時は自分で書いた方が幸せになれるように思います。

## 環境
- Ubuntu 18.04 (JetPack 4.1.1 Developer Preview)
- ROS Melodic Morenia
- Jetson AGX Xavier

#比較
どちらも、実施していることはサービスの作成です。なので、robot_upstart特有のトレードオフというよりは「ツールを使うか自作するか」という種の問題に一般的な長所短所があります。

|  | 長所 | 短所 |
| :--------- | :--------- | :--------- |
|robot_upstart|- 導入と使用方法が簡便<br>- Linux特有の知識はほぼ不要|- 自動化されすぎててデバッグが面倒|
|自分で書く|- 柔軟性が高い<br>- デバッグが容易|- Linux (Ubuntu)の知識がある程度必要|

# robot_upstartを使ってみた

`robot_upstart`の導入は簡単です。わずか一行。

```
sudo apt-get install ros-<distro>-robot-upstart
```

次に、自動で起動させたいlaunch fileを登録します。これも簡単。

```
rosrun robot_upstart install <your_package_name>/launch/<your_launch_file>.launch
```

これだけで、次回起動時には登録したlaunchファイルが起動するようになります。

## robot_upstartのつまづきポイント
### ログの出力先がドキュメントと異なる。 
たぶんドキュメントが更新されてないだけだと思いますが、私の環境ではドキュメントに書いてある`/var/log/upstart`ではなく`/var/log/syslog`に出力されていました。

### 環境変数の設定
`setuidgid`で直接`roslaunch`を叩いているためか`.bash_profile`や`.bashrc`が読み込まれないので、必要な環境変数が無かったり意図しない値になってたりすることがあります。
[こちら](https://answers.ros.org/question/205905/how-to-reference-environmental-variables-inside-an-upstart-installed-launch-file/)にあるように、必要な設定を書いた`.robotrc`などの適当なファイルを作って`/opt/ros/<distro>/setup.bash`や`<your_catkin_ws>/devel/setup.bash`から`source path/to/your/file`してあげるのが良いようです。

【参考】
[ログインシェルとインタラクティブシェルと~/.bashrc達の関係](https://qiita.com/incep/items/7e5760de0c2c748296aa)
[How to reference environmental variables inside an upstart installed launch file](https://answers.ros.org/question/205905/how-to-reference-environmental-variables-inside-an-upstart-installed-launch-file/)

### CUDAがエラーになる（未解決）
CPUオンリーのノード群は問題なく上記の設定で動いたのですが、私の環境ではCUDAを使っているノードがエラーでコケてしまい立ち上がりませんでした。
おそらく何かのPermissionが適切に設定されてないためだと思うのですが、未解決です。

# 自分で書いてみた
一見とっつきづらいですが、[ROSを自動起動する](https://qiita.com/strv/items/535a370842ae60af9b64)を参考に書いていくと意外と簡単に書けました。
非常にわかりやすい記事を書いてくださった@strv様に感謝です。

robot_upstartで直面したCUDAエラーもこちらでは発生しなかったので、私は現在こちらを使用しています。

## 自分で書いた場合のつまづきポイント
### デバイスをsystemdで利用可能にする

> systemdで利用可能なデバイス名については、
`systemctl --t device -a --full`
で確認することが出来ます。

とありますが、デバイスによってはデフォルトのままだとこのリストに出てこないことがあります。
そういう時は、該当するデバイスのudevルールに`TAG+="systemd"`を追加してあげましょう。
既存のルールが無い場合は自分で作成しても大丈夫です。私の場合は下記のようなudevルールを用意しました。

```
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="xxxx", ATTRS{idProduct}=="yyyy", TAG+="systemd", SYMLINK+="sensor_device%n", MODE="0666"
```
【参考】
[Raspberry Pi 2 + systemd + udevで、USBデバイス挿入時にサービスを起動する](http://thinkami.hatenablog.com/entry/2015/06/25/064658)、[Linux でデバイスを接続している時だけ動くサービスを作る](https://qiita.com/propella/items/dcc1c14f71e5f06f3a88)）

### roscoreの検出
[元記事](https://qiita.com/strv/items/535a370842ae60af9b64)だと`ExecStartPre=/bin/bash -l -c 'rostopic list'`でroscoreの検出を行っていますが、このやり方だと`rostopic list`が失敗した時点でサービス自体が落ちるので、後ろに書いてある`Restart=always`との合わせ技で成立しています。

```
ExecStartPre=/bin/bash -l -c 'rostopic list'
<中略>
Restart=always
```

元々の方針として`Restart=always`であるlaunchファイルならこのやり方が良いと思いますが、私の場合は多数のデバイスが絡むlaunchファイルになっていて頻繁に再起動するような仕様にしたくなかったので`Restart=no`にしています。
この場合`rostopic list`でのroscore検出が出来ないので、元記事のコメントにあるように`roslaunch --wait`オプションで待ってあげる必要があります。

```
ExecStart=/usr/bin/screen -S mavros -ADm bash -l -c 'roslaunch --wait mavros px4.launch'
```