ROS2の勉強　第4弾：serviceを使う

# #プログラミング ROS2< serviceを使う >

##はじめに
ROS2(バージョンアップしたROS)を難なく扱えるようになることが目的である．その第4弾として，「serviceを使う」を扱う．

##環境
#####仮想環境
|||
|:-:|:--|
|ソフト|VMware Workstation 15|
|実装RAM|3 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|

## service
serviceにはRequestとResponseがあり，リクエストして待ち続けるという特徴をもち，topicのようにデータを投げ続けるようなものではない．ここでは，turtlesim_nodeにあるspawnサービスにリクエストするようなプログラムを通して，serviceを使うプログラムの作成について学んでいく．

## プログラム: turtleを生み出す（spawn）
### turtlesimにおけるspawnサービス
まず，serviceを使うにあたって，どのようなserviceを使うのかということを知っておく必要がある．そこで，turtlesim_nodeを立ち上げた際に用意されるserviceを``ros2 serice list``で確認した．その時の様子を以下に示す．今回の対象を赤線で示している．

![service_list.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/aabff82d-7f7d-b72c-e689-624a2f79fbb3.png)

この``/tutlesim/spawn``という名前のサービスを使う．Typeも知っておく必要があるため，``ros2 service type /turtlesim/spawn``で調べた．その結果を以下に示す．
![type.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/12750dce-ca74-18bb-b5af-34f219c3bb73.png)
どうやら，turtlesimパッケージにあるSpawnサービスというもののようだ．以降ではこれらの情報を使って，組み立てていく．

<details><summary>**カメを生み出すプログラム**</summary><div>

```python:spwanTurtle.py
import rclpy
from turtlesim.srv import Spawn

def main(args=None):
    rclpy.init(args=args)
    node = rclpy.create_node('spawn_client')        # create a node for service
    client = node.create_client(Spawn, 'turtlesim/spawn')    # create a client for service

    req = Spawn.Request()       # set a request to call the service

    req.x     = 2.0                 # set position x
    req.y     = 2.0                 # set position y
    req.theta = 0.2                 # set pose theta

    req.name = 'new_turtle'         # new name for turle

    while not client.wait_for_service(timeout_sec=1.0):
        node.get_logger().info('service not available, waiting again...')
    
    # call the service Asynchronously
    future = client.call_async(req)
    rclpy.spin_until_future_complete(node, future)
    try:
        result = future.result()
    except Exception as e:
        node.get_logger().info(f"Service call failed {e}")
    else:
        node.get_logger().info(f"Result of x, y, theta, name : {req.x:.3f}, {req.y:.3f}, {req.theta:.3f}, {req.name}")
    
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()    
```
</details></div>

ソースコード内でコメントを書き忘れたが，

```python:
while not client.wait_for_service(timeout_sec=1.0):
```
はclientがサービスを受け取れるまでループするように記述しているが，``timeout_sec=1.0``はそのループ間の間隔で，1ループ後1秒待ってまたループというようにすることができる．これにより，「サービスを待っています」というようなログを大量ではなく1秒間隔で表示することができる．

### ビルドの準備
#### setup.pyの編集
基本的にビルドに関する情報はsetup.pyの中に記述されている．

<details><summary>**setup.pyの編集**</summary><div>

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

package_name = 'service_lesson'

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']),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='Yuya Shimizu',
    maintainer_email='yuya@example.com',
    description="a package for practice 'service'",
    license='BSD',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
            'spawn = service_lesson.spawnTurtle:main'
        ],
    },
)
```
</details></div>

#### ビルド
```bash:ビルド
cd ~/ros2_ws
colcon build
```

#### 実行コマンド
```bash:terminal_1
ros2 launch turtlesim_test myTurtlesim.launch.py
```

```bash:terminal_2
ros2 run service_lesson spawn
```


#### 実行の様子
![service_spawn.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/9d1f026b-23d8-6db9-56ac-13a1be6d4855.gif)

今回のプログラムはある座標にカメを一体出現させれば終了というものであるため，サービスのやり取りが一度行われた時点でプログラムは終了している．また，カメの出現座標をソースコード内で1つに決めてしまっているため，連続でプログラムを実行しようとしてももうすでにカメがその座標に存在しているため，turtlesim_node側でエラーだという表示が出てくる．

今回は試していないが，座標の部分を乱数にしたり，ユーザの入力ができるようにすると様々なところにカメを出現させることができる．これを実現することでよりserviceというものの理解を深める練習となるかもしれない．

##感想
ROS2でも少しずつ感覚がつかめてきた気がする．もちろん，まだまだではあるが，serviceを少し扱えるようになったことは非常に大きな進歩であるとROS1のとき同様に感じられる．次回はパラメータを使うプログラムについて学ぶ．大まかにはserviceではあるが，パラメータを使うにあたって，serviceをどのように活用していくのかということで，パラメータだけでなくserviceについての理解も深められそうで非常に楽しみである．

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