環境
この記事は以下の環境で動いています。
項目 | 値 |
---|---|
CPU | Core i5-8250U |
Ubuntu | 16.04 |
ROS | Kinetic |
インストールについてはROS講座02 インストールを参照してください。
またこの記事のプログラムはgithubにアップロードされています。ROS講座11 gitリポジトリを参照してください。
概要
ROSのパッケージを製作するためにはCMakeList.txtとpackage.xmlを記述する必要があります。しかしこれらの記述についてまとまって書かれている情報が少ないのでここでまとめてみます。
2つのファイルの役割
2つのファイルはそれぞれ役割が違います。以下のように2つのファイルは使われるところが違いますが、2つのファイルに書かれる依存の整合性を保つ仕組みはありません(これはROSの欠点の1つといえます)。これらのファイルを正確に記述する必要があります。
rospack listを実行した場合
$ROS_PACKAGE_PATHにあるディレクトリの中でpackage.xmlが探され、これがあることでROSパッケージだと認識されます。roscdなどで特定のROSパッケージにcdする場合のパスもこれを使って探されます。
caktin_makeを実行した場合
まずcatkin_ws/src以下のROSパッケージのpackage.xmlに書かれている依存を読んでcatkinマクロを解決して、ビルドする対象と順番を決めます。その後各ROSパッケージのCMakeList.txtがCmakeにかけられます。
package.xmlの役割
package.xmlは以下の役割があります。
- システム依存の記述系
あくまでROSシステムとしての依存を扱います。ビルドの依存は以下のCMakeList.txtで扱います。roscdやrospack等のコマンドで参照されるのはこちらです。 - プラグインやパスのエクスポートの記述
例えばnodeletなどでは「nodelet」パッケージのノードから、「ユーザーが作った」パッケージのプラグインを参照して実行します。この時nodeletがプラグインの存在を通知するのにpackage.xmlのexportの機能を使います。 - その他の記述
メンテナンスなどの人が参照する部分です。
最小構成
<package format="2">
<name>foo_core</name>
<version>1.2.4</version>
<description>
This package provides foo capability.
</description>
<maintainer email="ivana@osrf.org">Ivana Bildbotz</maintainer>
<license>BSD</license>
</package>
ROSwikiより
- packageタグのフォーマット属性
package.xmlにはversion1とversion2があり、両者の構文を混ぜるとエラーになります。現在は2が主流なのでそちらで解説します。 - nameタグ
パッケージ名を決定します。実はパッケージのディレクトリ名はパッケージ名に影響せずに、ここで書いた名前がパッケージの名前となります。 - versionタグ・descripionタグ・maintainerタグ・licenseタグ
自由に記述できます。
依存の記述
依存タグは以下の7つがあります。
-
<buildtool_depend>
ビルドツールの指定をします。通常はcatkin
のみを指定します。 -
<build_depend>
ビルド時の依存の指定をします。基本的にCMakeList.txtで指定するものは全てここで指定しておく必要があります。 -
<build_export_depend>
ビルド依存のエクスポートの指定をします。 -
<exec_depend>
実行時の依存の指定をします。pythonのノードを使うときには要注意です。version1では<run_depend>
となっていたものです。バージョンを混ぜるとエラーになるので注意です。 -
<depend>
これを書くと<build_depend>
、<build_export_depend>
、<exec_depend>
の3つを指定したのと同じ効果になります。 -
<test_depend>
テスト実行時の依存を指定します。 -
<doc_depend>
doxygen等のドキュメント作成用の依存を指定します。
<package format="2">
<!-- 省略 -->
<buildtool_depend>catkin</buildtool_depend>
<depend>roscpp</depend>
<depend>std_msgs</depend>
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
<exec_depend>rospy</exec_depend>
</package>
依存を正確に記述するためには<build_depend>
や<exec_depend>
を使い分ける必要がありますが、実用的には全部を<depend>
で指定すれば問題ありません。
依存しているパッケージへのエクスポート
依存しているパッケージへのプラグインなどのエクスポートをすることが出来ます。
例えば以下の様に記述すると各ROSパッケージで<export>
タグの中で<gazebo_ros gazebo_media_path="*******" />
と書いてある内容の一覧を取得できます。
plugins --attrib=gazebo_model_path gazebo_ros
CMakeList.txtの役割
これはcatkin_makeをするときに使用されるものです。名前の通りcmakeで使われるファイルです。逆に言うとそれ以外では使いません。
cmakeの標準の書式にcatkin特有のマクロが含まれているのに注意です。
このファイルに記述する記述は以下のように分けれらます。
- 依存の記述
- ビルド前に行われる作業
- 依存のエクスポートの記述
- ビルドする対象
- ビルド対象の依存
最小構成
cmake_minimum_required(VERSION 2.8.3)
project(package_A)
1行目は最小バージョンンの指定です。rosのversionによって違いますが、kineticでは標準では2.8.3
と書くようです。
2行目はパッケージ名を書きます。
実行ファイルを生成する最小構成
cmake_minimum_required(VERSION 2.8.3)
project(package_A)
find_package(catkin REQUIRED COMPONENTS
roscpp
)
include_directories(
${catkin_INCLUDE_DIRS}
)
add_executable(main src/main.cpp)
target_link_libraries(main
${catkin_LIBRARIES}
)
#include <stdio.h>
#include <ros/ros.h>
int main(int argc, char **argv){
ros::init(argc, argv, "talker");
ros::NodeHandle n;
return 0;
}
実行ファイルをつくり、それをrosのライブラリとリンクする最小の例です。
-
find_package(catkin REQUIRED COMPONENTS <依存package1> <依存package2>)
では依存するROSパッケージ名を記述します。 -
include_directories()
ではインクルードパスを追加します。これが無いとros/ros.h
とインクルードすることができません。 -
add_executable(<ターゲット名> <cppファイルのパス>)
ビルドするターゲットを選択します -
target_link_libraries(<ターゲット名> ${catkin_LIBRARIES})
ではリンクするライブラリのパスを追加します。これが無いとros/ros.h
をインクルードするのでAPIには到達しますが、内部の実装を読めずにエラーになります。
find_packageでのcatkinマクロの効果
以下のようにcatkinのマクロを使用せずにcmake標準の文法だけで同等の記述できます。
cmake_minimum_required(VERSION 2.8.3)
project(package_A)
find_package(roscpp)
include_directories(
${roscpp_INCLUDE_DIRS}
)
add_executable(main src/main.cpp)
target_link_libraries(main
${roscpp_LIBRARIES}
)
今回は依存がroscppだけなので上記のように短いですが、依存するパッケージが多くなると記述が長くなります。find_package(catkin REQUIRED COMPONENTS <依存package>)
とするとパッケージの依存を追加してかつ${依存package_***}
のパスを${catkin_***}
に追加してくれます。このために${catkin_***}
だけを書いていれば足ります。
してはfind_package(roscpp)
だけでroscppへの依存を追加できますが、上記のようにcatkinマクロを使用することで、わざわざインクルードやライブラリの依存で${roscpp_INCLUDE_DIRS}
や${roscpp_LIBRARIES}
ライブラリを作成する例(使用する側への設定が抜けている)
cmake_minimum_required(VERSION 2.8.3)
project(package_A)
find_package(catkin REQUIRED COMPONENTS
roscpp
std_msgs
)
include_directories(
include
${catkin_INCLUDE_DIRS}
)
add_library(TestLib
src/TestLib.cpp
)
target_link_libraries(TestLib
${catkin_LIBRARIES}
)
#include <stdio.h>
#include <ros/ros.h>
#include <std_msgs/Int32.h>
std_msgs::Int32 ReturnInt(void);
#include <package_A/TestLib.h>
#include <stdio.h>
#include <ros/ros.h>
#include <std_msgs/Int32.h>
std_msgs::Int32 ReturnInt(void){
std_msgs::Int32 int_msg;
int_msg.data = 0;
return int_msg;
}
ライブラリの場合はadd_executable
の代わりにadd_library(<ターゲット名> <cppファイルのパス1> <cppファイルのパス2>)
と書きます。このように記述するとpakckage_A/lib<ターゲット名>.soといオブジェクトファイルが生成されます。複数の関数やクラスを1つのオブジェクトファイルで扱うことができます。
しかしこの記述では使用する側がいちいちincludeファイルのパスを手動で解決したり、このライブラリの依存をわざわざ調べてライブラリを使用する側で依存を書かないといけません。これをcatkinマクロで解決できます。
ライブラリを作成する例(使用する側への設定込み)
cmake_minimum_required(VERSION 2.8.3)
project(package_A)
find_package(catkin REQUIRED COMPONENTS
roscpp
std_msgs
)
catkin_package(
INCLUDE_DIRS include
LIBRARIES package_A
CATKIN_DEPENDS roscpp std_msgs
)
include_directories(
include
${catkin_INCLUDE_DIRS}
)
add_library(TestLib
src/TestLib.cpp
)
target_link_libraries(TestLib
${catkin_LIBRARIES}
)
catkin_package()
が加わりました。
-
INCLUDE_DIRS include
はにincludeを加えます。これでほかのパッケージからこのパッケージのincludeファイルが見えるようになります。 -
LIBRARIES package_A
は上記と同じようにライブラリのパスが自動で解決できるようになります。 -
CATKIN_DEPENDS roscpp std_msgs
では他のパッケージがこのパッケージに依存をした時に自動的にここで記述したパッケージも依存に加わります。
msg・srv・actファイルを作成する例(使用する側への依存の解決が抜けている)
この3つはそれぞれROS形式のファイルをhファイルに変換して、各ノードで読み込めるヘッダファイルをを生成します。この3つは同様のコマンドなのでmsgの生成を例に説明します。
cmake_minimum_required(VERSION 2.8.3)
project(package_A)
find_package(catkin REQUIRED COMPONENTS
roscpp
std_msgs
message_generation
)
add_message_files(
FILES
Msg1.msg
)
generate_messages(
DEPENDENCIES
std_msgs
)
catkin_package(
CATKIN_DEPENDS message_runtime std_msgs
)
-
find_package
ではmessage_generation
を追加する必要があります。 -
add_message_files
にmsgファイルのファイル名を指定します。 -
generate_messages
ではmsgファイル内で使用しているmsgの依存を書く必要があります。 -
catkin_package
はこのパッケージを依存に追加するだけでmessage_runtime
とstd_msgs
を自動で依存が追加されるようにします。
msgを使う側
cmake_minimum_required(VERSION 2.8.3)
project(package_A)
find_package(catkin REQUIRED COMPONENTS
roscpp
)
include_directories(
${catkin_INCLUDE_DIRS}
)
add_dependencies(main ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_executable(main src/main.cpp)
target_link_libraries(main
${catkin_LIBRARIES}
)
ここではpackage_Aの中のmsgをmainが使うとします。add_dependencies(main ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
が必要です。msgファイルはビルド時に生成されるものですので、ただpackage_A
に依存しても、ファイルを取得できません。package_A
のビルド後の生成物に依存をする必要があります。