ROS2の勉強　第2弾：launchファイル - turtlesimを動かす

# #プログラミング ROS2< launchファイル - turtlesimを動かす >

## はじめに
ROS2(バージョンアップしたROS)を難なく扱えるようになることが目的である．その第2弾として，「launchファイル - turtlesimを動かす」を扱う．

## 環境
##### 仮想環境
|||
|:-:|:--|
|ソフト|VMware Workstation 15|
|実装RAM|2 GB|
|OS|Ubuntu 64 ビット|
|isoファイル|ubuntu-20.04.3-desktop-amd64.iso|

##### コンピュータ
|||
|:-:|:--|
|デバイス|MSI|
|プロセッサ|Intel(R) Core(TM) i5-7300HQ CPU @ 2.50GHz 2.50GHz|
|実装RAM|8.00 GB (7.89 GB 使用可能)|
|OS|Windows (Windows 10 Home, バージョン：21H1)|

##### ROS2
|||
|:-:|:--|
|Distribution|foxy|

## ワークスペースとパッケージの作成
### ワークスペース
ROS1でいうところのcatkin_wsのようなものはROS2でもある．
名前は任意だが，ここではros2_wsとしておく．

```terminal:ros2_wsの作成
mkdir -p ~/ros2_ws/src
```
とりあえず，フォルダを作っておけばよい．その他必要なものは，パッケージを作成する際にそろう．

### パッケージ
ROS1同様，パッケージはsrcフォルダ下に作っていく．

```terminal:パッケージの作成
cd ~/ros2_ws/src
ros2 pkg create turtlesim_test --build-type ament_python
```
ビルドは必ずros2_ws下で行う．（その場所以外でもビルドコマンドは通るが，そのたびにビルドによりいくつかのファイルが作成されるため注意．またパスも異なってくるため，ros2_ws下で行うべき．）以下で示すが，ビルドの場所を考えるのが面倒な場合は，aliasにでもまとめておくとよいかもしれない．

```terminal:パッケージのビルド
cd ~/ros2_ws
colcon build
```
```:pathを通す
source ~/ros2_ws/install/setup.bash
source ~/ros2_ws/install/local_setup.bash
```
```terminal:確認
ros2 pkg list | grep 
```

新たなパッケージを作成した際には，毎回pathを通すコマンドが必要となる．
このあたりを毎回打つのが面倒である場合は，bashrcファイルに書き加えてもよいかもしれない．

```bash:例
source /opt/ros/foxy/setup.bash
source ~/ros2_ws/install/setup.bash
source ~/ros2_ws/install/local_setup.bash

# ビルドコマンドをまとめておく
alias b='cd ~/ros2_ws && colcon build'
# pathもまとめておきたい場合はこっちを使う
# alias b='cd ~/ros2_ws && colcon build && source ~/ros2_ws/install/setup.bash && source ~/ros2_ws/install/local_setup.bash'
```

私の環境下では，以下のとおりで，新たなパッケージを作成した際には，新たにターミナルを立ち上げている．

```bash:例
source /opt/ros/foxy/setup.bash
source ~/ros2_ws/install/setup.bash
source ~/ros2_ws/install/local_setup.bash

alias b='cd ~/ros2_ws && colcon build'
```

これで，bを打つだけでどこにいてもビルドができる．

## turtlesim
ROS1にもあったturtlesimのデモが用意されている．

```terminal:turtlesim
ros2 run turtlesim turtlesim_node 
```

```terminal:teleop_key
ros2 run turtlesim turtle_teleop_key
```

この2つにより，turtlesimをkey入力で操作することができる．
ここでは，これらをlaunchファイルでまとめて動かすことでlaunchファイルの作成方法を学ぶ．

## launchファイルの作成
### 作成準備①: package.xmlの編集
launchファイルとして認識・実行できるようにpackage.xmlを編集する．

```xml:編集箇所（これをpackageタグ内に追加）
  <exec_depend>launch_ros</exec_depend>
```

<details><summary>**編集例**</summary><div>

```xml:package.xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>turtle_test</name>
  <version>0.0.0</version>
  <description>a package for practice through turtlesim</description>
  <maintainer email="yuya@example.com">Yuya Shimizu</maintainer>
  <license>BSD</license>

  <test_depend>ament_copyright</test_depend>
  <test_depend>ament_flake8</test_depend>
  <test_depend>ament_pep257</test_depend>
  <test_depend>python3-pytest</test_depend>
  <exec_depend>launch_ros</exec_depend>
  
  <export>
    <build_type>ament_python</build_type>
  </export>
</package>
```
</div></details>

### 作成準備②: setup.pyの編集
ビルドする際に，launchファイルがどこにあるかという部分を示す必要があるため，osライブラリとglobライブラリを用いて，汎用的にlaunchファイルのpathを取得できるようにしている．なお，ここでは，launchファイル名として~.launch.pyとなることとそのファイルがlaunchフォルダに格納されることを前提にしている．ほかにはlaunchファイル名を~_launch.pyとしている人もいる．とにかく規則性のあるもので，globを用いてワイルドカード(*)で取得できるようになっていればよいと思う．（もっと言えば，launchフォルダにはlaunch用のpythonファイルしか格納しないとすれば，~.pyをglobで取得することも可能だが，実行時には非常に分かりにくいため，おすすめはできない．）

```python:編集箇所
import os
from glob import glob

#＜省略＞

data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
        (os.path.join('share', package_name), glob('launch/*.launch.py')),
    ],

#＜省略＞
```
<details><summary>**編集例**</summary><div>

```python:setup.py
from setuptools import setup
import os
from glob import glob

package_name = 'turtle_test'

setup(
    name=package_name,
    version='0.0.0',
    packages=[package_name],
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
        (os.path.join('share', package_name), glob('launch/*.launch.py')),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='Yuya Shimizu',
    maintainer_email='yuya@example.com',
    description='a package for practice through turtlesim',
    license='BSD',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
        ],
    },
)
```
</div></details>

### launchフォルダの作成
launchファイルを格納するためのフォルダを用意する．

```terminal:
cd ~/ros2_ws/src/turtle_test
mkdir launch
```

### launchファイルの作成

```terminal:空ファイルの作成
cd ~/ros2_ws/src/turtle_test
touch launch/myTurtlesim.launch.py
chmod +x launch/myTurtlesim.launch.py
```
※最後のchmodコマンドでpythonファイルに実行権限を与えている

以下に今回作成したlaunchファイルを示す．
自分なりにコメントを書いている．
（なぜかvscodeで日本語が打てなくなっているため，英語でコメントを書いている）

```python:myTurtlesim.launch.py
import launch
from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        
        launch.actions.LogInfo(
            msg="Launch turtlesim node and turtle_teleop_key node."
        ),
        
        launch.actions.TimerAction(period=3.0, actions=[
            launch.actions.LogInfo(
                msg="It's been three minutes since the launch"
            )
        ]),

        # Node for turtlesim
        Node(
            package         = 'turtlesim',         # package name
            namespace       = 'turtlesim',         # namespace for launching node
            executable      = 'turtlesim_node',    # file name to run
            name            = 'turtlesim',         # node name
            output          = 'screen',            # option to display standard output on console
            parameters      = [ {'background_r': 255},
                                {'background_g': 255},
                                {'background_b': 0}
                                ]),
        
        # Node for teleop_key to control turtles
        Node(
            package         = 'turtlesim',         # package name
            namespace       = 'turtlesim',         # namespace for launching node
            executable      = 'turtle_teleop_key', # file name to run
            name            = 'teleop_turtle',     # node name
            prefix          = 'xterm -e',          # option: run this another terminal here; xterm requires you to install
                                ),
        
    ])
```

- 注意：launchファイルの下の方にprefix='xterm -e'とあるが，これはteleop_keyを別ターミナルで起動させるためのオプションである．これを用いるためには，あらかじめxtermをインストールしておく必要がある．

```terminal:xtermのインストール
sudo apt install xterm -y
```

### ビルド
```terminal:
cd ~/ros2_ws
colcon build
```
### 実行
実際に実行していく．

```terminal:実行コマンド
ros2 launch turtlesim myTurtlesim.launch.py
```

##### 実行の様子
![turtlesim.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/927cee4c-5504-ce41-b691-2394c1741f6f.gif)

うまく動いていることが確認できる．
なお，turtlesimの背景はlaunchファイルのparameter引数で設定している．

## 感想
launchファイルはROS1のときとはずいぶんと雰囲気が変わったが，やっていることはそう変わらないし，慣れてしまえばpythonユーザからしてみると，とっつきやすいのかもしれない．もちろん細かいところは理解できていないだろうが，これから経験を積んでいきたい．これで，とりあえずlaunchファイルまで作成して扱えるようになった．内容の中では触れていなかったが，カメの操作時にその場旋回を途中で止めているシーンがあるが，あれはアクションである．メッセージやサービス，アクションの概念はROS1と変わりなさそうなので，細かくまとめていない．ROS1環境での学習記録は以下の記事にまとめている．

https://qiita.com/Yuya-Shimizu/items/4e6a7c26110ce99886c9

https://qiita.com/Yuya-Shimizu/items/683e36f5b45677b0923e

https://qiita.com/Yuya-Shimizu/items/778774b30e5c75820a81



##参考
- ロボットシステム学講義：ロボットシステム学第11回（ROS2）　　上田隆一 氏

https://www.youtube.com/watch?v=QmRhucWxhac&list=PLbUh9y6MXvjdIB5A9uhrZVrhAaXc61Pzz&index=16&t=1737s

- ロボットプログラミングROS2入門　玉川大学　岡田浩之　著，科学情報出版株式会社