7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

実習ROS 2 ROS 2 Launch 1:概要

Last updated at Posted at 2024-03-27

環境

本記事は以下の環境を想定して記述している。

項目
OS Ubuntu 22.04
ROS ROS 2 Humble

概要

このページでは、ROS 2で使われるlaunchシステムの目的とその機能について、概念的な説明をする。
その後、2つのPublisherをまとめて起動するlaunchファイルと、2つのSubscriberをまとめて起動するlaunchファイルを記述し、実行結果を確認する。
launchファイルは同じ内容のものをPythonとXMLの2種類で記述する。

本記事で解説していること

  • launchファイルの目的
  • Pub&Sub通信のページで実装したPublisherとSubscriberを同時に起動するlaunchファイル(Python形式及びXML形式)
  • launchファイルを記述する言語(Python, XML, YAML)による違いについて

本記事ではlaunchファイルの概念と基本的な作成方法を説明することに焦点を当てる。
そのため、本記事で実装するlaunchファイルは単純なものとし、別の記事でlaunchシステムを活用した様々な機能を紹介する。

前提

本記事の内容は、Pub&Sub通信で作成したディレクトリ群があることを前提にしている。
また、ワークスペースの名称はros2_lecture_wsであると仮定して説明する。
前提としているパッケージと今回作成するパッケージの構成を以下に示す。

$ cd ~/ros2_lecture_ws/src/
$ tree           # ディレクトリ構成を表示するコマンド
.
├── pub_sub_comm
│   ├── CMakeLists.txt
│   ├── include
│   │   └── pub_sub_comm
│   ├── package.xml
│   └── src
│       ├── simple_listener.cpp
│       └── simple_talker.cpp
├── ros2_launch_1       # この記事で作成するパッケージ
│   ├── CMakeLists.txt
│   ├── launch
│   │   ├── publisher_nodes.launch.py
│   │   ├── publisher_nodes.launch.xml
│   │   ├── subscriber_nodes.launch.py
│   │   └── subscriber_nodes.launch.xml
│   ├── package.xml
│   └── README.md

launchファイル

説明

launchファイルは、複数のROSノードをまとめて起動し、それらの実行を制御するためのファイルである。

ROS 2の場合、プログラムの基本単位となるノードはros2 run <パッケージ名> <実行ファイル名>というコマンドで実行できる。
この場合、ノード数だけターミナルを起動し、人が都度都度コマンドを実行する必要がある。

launchファイルを用いると、1つのターミナルで複数のノードをまとめて起動できるようになる。
また、launchシステムにはノードやそれらの間の通信を制御する様々な機能があり、柔軟で再利用可能なプログラムの構成を記述できる。
launchファイルで実現できる機能の一例を以下に示す。

  • 条件に応じて起動するノードを選択する
  • 他のlaunchファイルをインクルードする
  • launchファイルに対する引数を定義する
  • ノード名やトピック名をremap(*注)する
  • ノード名やトピック名にnamespace(名前空間)を追加する

(*注):remapとは、ある名称を別の名称に置き換えることである。例えば、/chatter1という名前でソースコード中に実装されたトピックを/chatter1_remappedという名前としてノードを起動することがremapにあたる。

パッケージの作成

launchファイルはパッケージの中に実装するため、まずはパッケージを作成する。

以下のように、ros2 pkg createコマンドでパッケージを作成する。
本記事ではpub_sub_commに依存したパッケージを作成するため、オプションで依存パッケージを指定している。(--dependencies pub_sub_commの部分)。

# パッケージを作成
$ cd ~/ros2_lecture_ws/src
$ ros2 pkg create ros2_launch_1 --build-type ament_cmake --dependencies pub_sub_comm

Pythonでの書き方

ROS 2ではPythonでlaunchファイルが記述されることが多いため、まずPythonを用いた実装を示す。
ros2_launch_1のパッケージにlaunchというディレクトリを作成し、その中にpublisher_nodes.launch.pyというlaunchファイルを作成する。

# ディレクトリを作成し、ファイルを新規作成
cd ~/ros2_lecture_ws/src/ros2_launch_1
mkdir launch
cd launch
touch publisher_nodes.launch.py

このlaunchファイルでは、同一の処理を行うPublisherノードを2つ起動する。
実装したファイルの中身を以下に示す。launchファイルへのリンクはこちら

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    # launchの構成を示すLaunchDescription型の変数の定義
    ld = LaunchDescription()

    # publisher nodeを、"talker_renamed1"という名前で定義
    pub_node1 = Node(
        package='pub_sub_comm',
        executable='talker',
        name='talker_renamed1',
        namespace='namespace_app1',                 # namespace_app1というnamespaceを追加
        remappings=[('chatter', 'chatter_app1')]    # chatterトピックをchatter_app1トピックにremap
    )

    # publisher nodeを、"talker_renamed2"という名前で定義
    pub_node2 = Node(
        package='pub_sub_comm',
        executable='talker',
        name='talker_renamed2',
        namespace='namespace_app2',                 # namespace_app2というnamespaceを追加
        remappings=[('chatter', 'chatter_app2')]    # chatterトピックをchatter_app2トピックにremap
    )

    # LaunchDescriptionに、起動したいノードを追加する
    ld.add_action(pub_node1)
    ld.add_action(pub_node2)

    # launch構成を返すようにする
    return ld

Pythonで記述するlaunchファイルでは、launch.LaunchDescription型のオブジェクトを返す、generate_launch_description()という名前の関数を定義する。

関数内ではlaunch.Action型を継承したクラスであるlaunch_ros.Node型の変数をLaunchDescription型の変数ldに追加している。

ノードはtalker_renamed1talker_renamed2という名前にリネームしてから起動している。
同じ名前のノードを複数起動することはできないが、ノード名をリネームすることで、同一の処理を行うノードを複数起動できるようになる。

また、1つ目のノードにはnamespace_app1、2つ目のノードにはnamespace_app2というnamespaceを追加している。
これにより、トピック名やノード名を階層的に扱うことが可能になり、大規模なROS 2システムが扱いやすくなる。

さらにトピック名についても、2つのノードで異なる名前にリマップしている。
これにより、ノードのソースコードで実装したトピック名を変更することが容易になり、ノードの再利用性が高まる。

同様にして、subscriber_nodes.launch.pyを作成する。
このlaunchファイルでは、pub_sub_commパッケージのノードlistenerを、listener_renamed1listener_renamed2という別個のノードとして起動する。
本記事中では実装は省略するが、launchファイルへのリンクをこちらに示す。

XMLでの書き方

上記と同じ内容のlaunchファイルをXMLで実装する。
publisher_nodes.launch.pyを作成した場所に、publisher_nodes.launch.xmlという名前のlaunchファイルを作成する。

# ファイルを新規作成
cd ~/ros2_lecture_ws/src/ros2_launch_1/launch
touch publisher_nodes.launch.xml

実装したファイルの中身を以下に示す。launchファイルへのリンクはこちら

<launch>
    <!-- publisher nodeを、"talker_renamed1"という名前で定義 -->
    <node pkg="pub_sub_comm" exec="talker" name="talker_renamed1" namespace="namespace_app1">
        <remap from="chatter" to="chatter_app1"/>
    </node>

    <!-- publisher nodeを、"talker_renamed2"という名前で定義 -->
    <node pkg="pub_sub_comm" exec="talker" name="talker_renamed2" namespace="namespace_app2">
        <remap from="chatter" to="chatter_app2"/>
    </node>
</launch>

XMLで記述する場合、要素と属性の組み合わせで情報を記述する。

実行

設定ファイルの更新

パッケージ内の設定ファイルに、追加したlaunchファイルに関する記述を追加する。

package.xmlには、下記の部分を追記する。
このようにすることで、パッケージのビルド後にros2 launchコマンドが通ることと、すべてのlaunchファイルのフォーマットが認識されることが保証される。
追記する行とその周辺を以下に示す。

   <depend>pub_sub_comm</depend>
+
+  <exec_depend>ros2launch</exec_depend>

   <test_depend>ament_lint_auto</test_depend>
   <test_depend>ament_lint_common</test_depend>

CMakeLists.txtには、以下の部分を追記する。
このようにすることで、launchファイルがワークスペースにインストールされる。
追記する行とその周辺を以下に示す。

  # find dependencies
  find_package(ament_cmake REQUIRED)
  find_package(pub_sub_comm REQUIRED)

+  
+ # install launch files
+ install(
+   DIRECTORY launch
+   DESTINATION share/${PROJECT_NAME}/
+ )
  

ビルド

ワークスペースのトップディレクトリに移動し、colcon buildコマンドを実行する(*注)。

cd ~/ros2_lecture_ws
colcon build --packages-select pub_sub_comm ros2_launch_1
. install/setup.bash

(*注):launchファイルをワークスペースにインストールし、launchファイルをros2 launchコマンドとして実行するためには、colcon buildコマンドの実行が必要である。パッケージの中にコンパイルやリンクを必要とするファイルがない場合でも、コマンドの実行が必要であることに注意する。

Pythonでの起動方法

ros2 launch <package_name> <launch_file>という構文で、launchファイルを実行できる。

# 構文:ros2 launch <package_name> <launch_file>
# 1つ目のターミナル:Publisherの同時起動
ros2 launch ros2_launch_1 publisher_nodes.launch.py

# 2つ目のターミナル:Subscriberの同時起動
ros2 launch ros2_launch_1 subscriber_nodes.launch.py

XMLでの起動方法

Pythonのときと同様に、指定するファイル名をxmlにすることで、同様に実行できる。

# 構文:ros2 launch <package_name> <launch_file>

# 1つ目のターミナル:Publisherの同時起動
ros2 launch ros2_launch_1 publisher_nodes.launch.xml

# 2つ目のターミナル:Subscriberの同時起動
ros2 launch ros2_launch_1 subscriber_nodes.launch.xml

どちらの方法でも、1つのターミナルで同時にPublisherとSubscriberを起動することができる。

ROS 2システム上で使用するlaunchファイルのフォーマットは統一されていなくてもよい。
例えば、以下の状況においてA.launchB.launchを同時に起動できる。

  • あるlaunchファイルA.launchはPythonで書かれており、ノードA1A2を起動する
  • 別のlaunchファイルB.launchはXMLで書かれており、ノードB1B2を起動する

また、ROS 2インタフェースが合っていれば、ノードA1B1の通信ができる。
つまり、ノードはどのlaunchファイルから起動されたかに関係なく通信できる。

結果

ターミナルを開き、ワークスペースのセットアップスクリプトを実行してから、Publisherを起動する。
すると、ログ出力から、異なる名前の2つのPublisherが同時に起動しており、同じ内容のトピックをPublishしていることがわかる。

$ ros2 launch ros2_launch_1 publisher_nodes.launch.py
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [talker-1]: process started with pid [3601]
[INFO] [talker-2]: process started with pid [3603]
[talker-2] [INFO] [1710231655.950076094] [namespace_app2.talker_renamed2]: publish: hello, world!
[talker-1] [INFO] [1710231655.950079658] [namespace_app1.talker_renamed1]: publish: hello, world!
[talker-2] [INFO] [1710231656.050054310] [namespace_app2.talker_renamed2]: publish: hello, world!
[talker-1] [INFO] [1710231656.050082519] [namespace_app1.talker_renamed1]: publish: hello, world!
[talker-2] [INFO] [1710231656.149976967] [namespace_app2.talker_renamed2]: publish: hello, world!
[talker-1] [INFO] [1710231656.150041808] [namespace_app1.talker_renamed1]: publish: hello, world!
[talker-2] [INFO] [1710231656.250055714] [namespace_app2.talker_renamed2]: publish: hello, world!
[talker-1] [INFO] [1710231656.250088787] [namespace_app1.talker_renamed1]: publish: hello, world!
[talker-2] [INFO] [1710231656.350064832] [namespace_app2.talker_renamed2]: publish: hello, world!
[talker-1] [INFO] [1710231656.350067788] [namespace_app1.talker_renamed1]: publish: hello, world!

同様に、別のターミナルを起動し、以下のコマンドでSubscriberを起動する。
Publisherと同様に、listener_renamed1listener_renamed2のノードが起動しており、ともにメッセージを受け取っていることがわかる。

$ ros2 launch ros2_launch_1 subscriber_nodes.launch.py
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [listener-1]: process started with pid [3626]
[INFO] [listener-2]: process started with pid [3628]
[listener-1] [INFO] [1710231660.649992085] [namespace_app1.listener_renamed1]: subscribe: hello, world!
[listener-2] [INFO] [1710231660.650022050] [namespace_app2.listener_renamed2]: subscribe: hello, world!
[listener-1] [INFO] [1710231660.750524197] [namespace_app1.listener_renamed1]: subscribe: hello, world!
[listener-2] [INFO] [1710231660.750588888] [namespace_app2.listener_renamed2]: subscribe: hello, world!
[listener-1] [INFO] [1710231660.850591883] [namespace_app1.listener_renamed1]: subscribe: hello, world!
[listener-2] [INFO] [1710231660.850660257] [namespace_app2.listener_renamed2]: subscribe: hello, world!
[listener-1] [INFO] [1710231660.950597642] [namespace_app1.listener_renamed1]: subscribe: hello, world!
[listener-2] [INFO] [1710231660.950683223] [namespace_app2.listener_renamed2]: subscribe: hello, world!
[listener-1] [INFO] [1710231661.050594018] [namespace_app1.listener_renamed1]: subscribe: hello, world!

ノードとトピックの関係を図示するのに、rqt_graphコマンドを使用できる。
3つ目のターミナルを開き、以下のコマンドを実行する。

$ rqt_graph

すると、以下のようなグラフが表示される。

以下の図は、丸がノード、四角がトピックを示している。
図より、2組のPublisherとSubscriberが、別々のnamespaceをつけられた状態で通信していることが分かる。
例えば、ノード/namespace_app1/taker_renamed1は、トピック/namespace_app1/chatter_app1にメッセージをPublishしている。

rqt_graph

launchファイルを記述する言語

現在のROS 2ではlaunchファイルの記述にPython, XML, YAMLの3種類の形式が使用できる。ROS 1ではXMLを用いることが標準であるが、ROS 2ではPythonを用いることが多い。

その理由としては、以下の点が挙げられる。

  • Pythonはスクリプト言語であるため、制御構造の扱いやすさ、言語のライブラリ、デバッグのしやすさなどの点で優れている。
  • ROS 2のlaunchシステム自体がPythonで実装されているため、より低レベルの機能を利用できる。

上記の長所の裏返しとして、本記事の2つのlaunchファイルを比較して分かるように、Pythonで記述したlaunchファイルは複雑で冗長になりやすい面がある。

なお、ROS 1のlaunchファイルをROS 2のXML形式のlaunchファイルに移行する際には公式サイトのガイドが参考になる。

参考

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?