Posted at

ROS講座97 CMakeList.txtとpackage.xmlの書き方


環境

この記事は以下の環境で動いています。

項目

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つといえます)。これらのファイルを正確に記述する必要があります。


  • caktin_make

  • roscd


依存のエクスポートの概念

依存のエクスポートという概念があるので説明しておきます。例えばpackageAとpackageBがあって依存の設定が以下のようになっているとします。

A 依存:A1、依存のエクスポートA2

B 依存:B1・A、依存のエクスポートB2

この時に依存の解決をすると


package.xmlの役割

package.xmlは以下の役割があります。


  • システム依存の記述系

    あくまでROSシステムとしての依存を扱います。ビルドの依存は以下のCMakeList.txtで扱います。roscdやrospack等のコマンドで参照されるのはこちらです。

  • プラグインやパスのエクスポートの記述

    例えばnodeletなどでは「nodelet」パッケージのノードから、「ユーザーが作った」パッケージのプラグインを参照して実行します。この時nodeletがプラグインの存在を通知するのにpackage.xmlのexportの機能を使います。

  • その他の記述

    メンテナンスなどの人が参照する部分です。


最小構成


package.xmlの最小構成

<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>で指定すれば問題ありません。


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}
)


ソースの例(package_A/src/main.cpp)

#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標準の文法だけで同等の記述できます。


最小構成(catkinマクロを使わない例)

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/package_A/TestLib.h

#include <stdio.h>

#include <ros/ros.h>
#include <std_msgs/Int32.h>

std_msgs::Int32 ReturnInt(void);


src/TestLib.cpp

#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の生成を例に説明します。


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_runtimestd_msgsを自動で依存が追加されるようにします。


msgを使う側


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 ${catkin_EXPORTED_TARGETS})
add_executable(main src/main.cpp)
target_link_libraries(main
${catkin_LIBRARIES}
)

ここではpackage_Aの中のmsgをmainが使うとします。add_dependencies(main ${catkin_EXPORTED_TARGETS})が必要です。msgファイルはビルド時に生成されるものですので、ただpackage_Aに依存しても、ファイルを取得できません。package_Aのビルド後の生成物に依存をする必要があります。


参考

ROSwiki

依存の解説


目次ページへのリンク

ROS講座の目次へのリンク