環境
本記事は以下の環境を想定して記述している。
項目 | 値 |
---|---|
OS | Ubuntu 22.04 |
ROS | ROS 2 Humble |
概要
ROS 2では、パッケージ情報を記述するファイルとしてpackage.xml
を、C++プログラムのビルド情報を記述するためのファイルとしてCMakeLists.txt
を用いる。
この記事では、これら2つのファイルの役割と記述方法を説明する。具体的には、以下の事柄を扱う。
- 背景
- package.xmlの書き方
- package.xmlの最小構成
- 依存関係の記述
- その他の記述
- action_tutorials_cppパッケージの例
- CMakeLists.txtの書き方
- CMakeについて
- CMakeの拡張:ament_cmake
- CMakeLists.txtの最小構成
- 実行ファイルを生成する最小構成
- minimal_clientパッケージの例
- 参考
背景
ROS 2では、ソースコードや様々な設定ファイル等を「パッケージ」と呼ばれる単位で管理する。
パッケージは再利用可能な形で作成されるのが望ましく、アプリケーションは複数のパッケージを組み合わせて実現される。これを実現するためには、パッケージ間の依存関係(パッケージが正しくビルドされ、動作するために必要となる他のパッケージの情報)を正しく記述する事が重要になる。
また、ROS 2はUbuntu、Windows、macOSでサポートされており、プログラミング言語は主にC++とPythonが使われる。したがって、パッケージの情報は異なるプログラミング言語間で、また、異なる開発環境間で統一して使用できなくてはならない。
このような背景からROS 2パッケージで必要とされるファイルがCMakeLists.txt
とpackage.xml
である。
package.xmlの書き方
package.xml
には、以下の情報を記述する役割がある。このファイルはパッケージのトップディレクトリに必ず存在しなくてはならない。
- ROSシステムとしてのパッケージ情報
依存関係、ビルドに必要な情報等 - パッケージのメタデータ
パッケージのバージョン情報、パッケージの説明、パッケージのメンテナー、ソフトウェアライセンス等
package.xml
の記述方法には複数のバージョンがあるが、本記事では2022年9月時点で最新のバージョン3の記述方法について説明する。
なお、ros2 pkg create
コマンドで自動生成されるpackage.xml
のバージョンも3である。
記述方法の仕様はREP149で決められている(REP: ROS Enhancement Proposal)ため、詳細な仕様についてはこのページを参照すると良い。
バージョン2とバージョン3の違いは以下の3点であり、大きな変更はない。そのため、バージョン2で記述されたOSSのROS 2パッケージも存在する。
基本的に新しいプロジェクトを開始する場合は、最新の機能とサポートを利用できるバージョン3を推奨する。
既存のプロジェクトでバージョン2を利用している場合は互換性を考慮しバージョン2を選択することもある。
- 依存関係のグループ化の機能の追加
- ABI(Application Binary Interface)のバージョン指定機能の追加
- 同一パッケージでROS 1とROS 2の両方に対応するための機能の追加
package.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>minimum_package</name>
<version>0.0.1</version>
<description>The minimum package</description>
<maintainer email="maintainer@example.com">Maintainer Name</maintainer>
<license>2-Clause BSD License</license>
</package>
はじめの2行では、このファイルがROS 2のpackage.xml
バージョン3のファイルであることを宣言している。
それ以降のタグの意味は次の通り。
-
<package>
トップレベルのタグ。以降のタグはこのタグの中に記述する。
format
属性でバージョンを指定する。 -
<name>
このパッケージの名称。後述するCMakeLists.txt
のプロジェクト名と同一の名称にする。
パッケージ名はディレクトリの名前ではなく、このタグによって定義される。 -
<version>
このパッケージのバージョン。MAJOR.MINOR.PATCH
の順で数値で記載する。 -
<description>
このパッケージの簡単な説明。 -
<maintainer>
このパッケージをメンテンナンスしている人のメールアドレスと名前。 -
<license>
ソフトウェアライセンス。少なくとも1つ以上指定する必要がある。
必須ではないが、file
属性を用いてパッケージ中のライセンス文へのパスを指定することができる。
依存関係の記述
ROSパッケージやシステムパッケージの依存を記述するタグは様々なものがあるが、必要なもののみを記述すればよい。
よく用いられるタグは以下である。
-
<buildtool_depend>
ビルドの過程で使用するツールとして必要なものを記述する。
C++プログラムではament_cmake
が、Pythonプログラムではament_python
がよく用いられる。 -
<build_depend>
ビルドに必要なパッケージを記述する。 -
<exec_depend>
実行時に必要なパッケージを記述する。 -
<test_depend>
テスト時に必要なパッケージを記述する。 -
<depend>
このタグで依存を書くと、<build_depend>
、<build_export_depend>
、<exec_depend>
の3つ全てを書いたことになる。
また、以下のようなタグも記述することができる。
-
<build_export_depend>
このパッケージをエクスポート(※)し、それを利用して別のパッケージをビルドするときに必要になるパッケージを記述する。 -
<buildtool_export_depend>
ビルドツールとして他のパッケージにエクスポート(※)する必要があるパッケージを記述する。 -
<doc_depend>
ドキュメントをビルドするときに必要なパッケージを記述する。 -
<conflict>
このパッケージと競合する(同時にインストールできない)ものを記述する。 -
<replace>
このパッケージを置き換えることができるものを記述する。 -
<group_depend>
フォーマット バージョン3で指定可能。
いくつかのパッケージをまとめてグループとして扱ったとき、このパッケージが依存するグループを指定する。 -
<member_of_group>
フォーマット バージョン3で指定可能。
このパッケージが指定したグループに属することを示す。
(※)エクスポートとは、 パッケージやデータ等を外部のパッケージで使えるように公開することを指す。
その他の記述
その他の様々な情報を記述するためのタグとして、<export>
がある。
このタグの中に入れ子にして追加の情報を記述する。
よく使われるものとして、C++のプログラムを実装するパッケージに対して指定する<build_type>
がある。
action_tutorials_cppパッケージの例
具体的な例として、ros2/demosリポジトリにあるaction_tutorials_cppパッケージの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>action_tutorials_cpp</name>
<version>0.9.4</version>
<description>C++ action tutorial cpp code</description>
<maintainer email="mabel@openrobotics.org">Mabel Zhang</maintainer>
<maintainer email="michael.jeronimo@openrobotics.org">Michael Jeronimo</maintainer>
<license>Apache License 2.0</license>
<author email="jacob@openrobotics.org">Jacob Perron</author>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>action_tutorials_interfaces</depend>
<depend>rclcpp</depend>
<depend>rclcpp_action</depend>
<depend>rclcpp_components</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
CMakeLists.txtの書き方
CMakeについて
CMakeLists.txt
(この名前は固定されている)は、CMakeで用いる設定ファイルの名前である。
CMakeはROSとは独立して存在する、クロスプラットフォーム対応のオープンソースソフトウェアで、ソフトウェアのビルド、テスト、パッケージングためのツールである。
なお、CMakeは幅広い言語に対応しており多くのプロジェクトで使用される。また、CMakeではクロスコンパイルが可能であり、ビルド環境と異なる実行環境でシステムを動かすことが可能となる。
CMakeの必要性は、具体的な例を挙げるとわかりやすい。
作成したC++のプログラムをビルドする際、環境によってコンパイルに必要なオプションや設定ファイルが全く異なる。例として、Linux環境でgccを用いる場合と、Windows環境でVisual C++を用いる場合では必要な設定が異なってくる。CMakeを用いると、プラットフォームに依存しないCMake独自の構文でこれらの設定を定義し、同じCMakeの記述から各々の環境に対応したビルド設定ファイルを出力することができる。
CMakeの構文については、オンラインドキュメントに説明されている。
CMakeの拡張:ament_cmake
ROS 2ではCMakeをベースとして拡張したビルドシステムament_cmake
を使用する。ament_cmake
は基本的にはCMakeの構文と同じ記述ができるが、ROS 2のために拡張されたマクロが定義されている。
CMakeLists.txt
はパッケージのトップディレクトリに配置する。
ament_cmake
におけるCMakeLists.txt
の役割はCMakeと同様である。ROS 1ではPythonプログラムのパッケージにもCMakeLists.txt
が必要であったが、ROS 2では不要になった。
以降では、ament_cmake
を用いる際のCMakeLists.txt
について説明する。
CMakeLists.txtの最小構成
最小構成のCMakeLists.txt
を以下に示す。
cmake_minimum_required(VERSION 3.5)
project(minimum_package)
ament_package()
最初の2行はCMakeの構文、最後のament_package()
はament_cmake
の構文である。
-
cmake_minimum_required()
このファイルで要求するCMakeの最低バージョンを指定する。 -
project()
プロジェクト名を指定する。
ROSパッケージでは必ずパッケージ名と同一にしなくてはならない。 -
ament_package()
この行までに書いてきた情報をamentシステムに登録する。
CMakeプロジェクトに対して最後に一度だけ呼び出すようにする。
実行ファイルを生成する最小構成
実行ファイルを生成する場合の最小構成を以下に示す。
以下の例では、src/main.cpp
からmain
という実行ファイルを作成している。
注意:以下の例は最小構成のため、ros2 run
コマンドで実行することは出来ない(ワークスペースにインストールしていないため)。
cmake_minimum_required(VERSION 3.5)
project(minimum_package)
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
add_executable(main
src/main.cpp
)
ament_target_dependencies(main
rclcpp
)
ament_package()
新たに登場した構文について以下に示す。
-
find_package()
このプロジェクトが利用するほかパッケージ(ここでのパッケージは、ROSパッケージ以外のライブラリ等も含む)を探す。 -
add_executable()
実行可能ファイルmain
を宣言し、それに必要なソースファイルを与える。
このようなビルド対象のことをCMakeでは「ターゲット」と呼ぶ。 -
ament_target_dependencies()
ament_cmake
独自の構文。
ターゲットが依存する(リンクさせる)ROS 2パッケージを追加する。
ROSではないライブラリをリンクさせたいときは、純粋なCMakeの構文であるtarget_link_libraries()
を用いる。
minimal_clientパッケージの例
具体的な例として、ros2/demosリポジトリにあるminimal_clientパッケージのCMakeLists.txtを以下に示す。
cmake_minimum_required(VERSION 3.5)
project(examples_rclcpp_minimal_client)
# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
find_package(ament_cmake REQUIRED)
find_package(example_interfaces REQUIRED)
find_package(rclcpp REQUIRED)
add_executable(client_main main.cpp)
ament_target_dependencies(client_main rclcpp example_interfaces)
install(TARGETS client_main
DESTINATION lib/${PROJECT_NAME})
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
最小構成の説明に含まれていない構文について、以下、抜き出して説明する。
# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
C++14を使うための設定である。
CMakeにおける変数CMAKE_CXX_STANDARD
が設定されていないとき、この変数に14
を設定している。
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
コンパイラがGNU C++コンパイラか、Clang系のコンパイラのときに、コンパイルオプションを設定する。
add_compile_options()
では、Warningに対する3つのオプションを指定している。
install(TARGETS client_main
DESTINATION lib/${PROJECT_NAME})
ビルドしたターゲットclient_main
のインストール先を指定している。
ビルドを実行したワークスペース下のinstall/[パッケージ名]/lib/[パッケージ名]
下にターゲットが配置される。
これにより、ros2 run
等のコマンドでノードを実行できるようになる。
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
endif()
テスト時の設定である。
package.xml
に記載されたリンタ(静的解析ツール)を実行する。