LoginSignup
10
10

賢くROS2でパッケージづくりをしよう

Last updated at Posted at 2023-04-21

パッケージをどう作ってますか?

$ ros2 pkg create hoge

だけで済ませてませんか?

引数を設定しよう

ここでどんな引数が使えるか見てみましょう

$ ros2 pkg create --help

と打つと以下のように出てきますね

出力
usage: ros2 pkg create [-h] [--package-format {2,3}]
                       [--description DESCRIPTION] [--license LICENSE]
                       [--destination-directory DESTINATION_DIRECTORY]
                       [--build-type {cmake,ament_cmake,ament_python}]
                       [--dependencies DEPENDENCIES [DEPENDENCIES ...]]
                       [--maintainer-email MAINTAINER_EMAIL]
                       [--maintainer-name MAINTAINER_NAME]
                       [--node-name NODE_NAME] [--library-name LIBRARY_NAME]
                       package_name

Create a new ROS2 package

positional arguments:
  package_name          The package name

optional arguments:
  -h, --help            show this help message and exit
  --package-format {2,3}, --package_format {2,3}
                        The package.xml format.
  --description DESCRIPTION
                        The description given in the package.xml
  --license LICENSE     The license attached to this package
  --destination-directory DESTINATION_DIRECTORY
                        Directory where to create the package directory
  --build-type {cmake,ament_cmake,ament_python}
                        The build type to process the package with
  --dependencies DEPENDENCIES [DEPENDENCIES ...]
                        list of dependencies
  --maintainer-email MAINTAINER_EMAIL
                        email address of the maintainer of this package
  --maintainer-name MAINTAINER_NAME
                        name of the maintainer of this package
  --node-name NODE_NAME
                        name of the empty executable
  --library-name LIBRARY_NAME
                        name of the empty library

よく使うものからピックアップしていきましょう

一応参考程度に引数なしで出力したものを書いておきます

おそらくデフォルトではament_cmakeが設定されているようです

引数なしver
file-tree
├── CMakeLists.txt
├── include
│   └── hoge
├── package.xml
└── src
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(hoge)

# Default to C99
if(NOT CMAKE_C_STANDARD)
  set(CMAKE_C_STANDARD 99)
endif()

# 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 dependencies
find_package(ament_cmake REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)

if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # uncomment the line when a copyright and license is not present in all source files
  #set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # uncomment the line when this package is not in a git repo
  #set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

ament_package()

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>hoge</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="your-mail">your-name</maintainer>
  <license>TODO: License declaration</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

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

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

--build-type

これは使っている人は多いのではないでしょうか
これは文字どおりビルドの方式を変えてくれるものです
使用例としては以下のような感じです

パッケージ名はhogeとします

# cmake
$ ros2 pkg create hoge --build-type ament_cmake

# python 
$ ros2 pkg create hoge --build-type ament-python

以下に出力されるファイルを書きます
長いので畳んでいます

ament_cmake

引数なしのところで書いたものと同じですが書きます

file-tree
├── CMakeLists.txt
├── include
│   └── hoge
├── package.xml
└── src

CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(hoge)

# Default to C99
if(NOT CMAKE_C_STANDARD)
  set(CMAKE_C_STANDARD 99)
endif()

# 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 dependencies
find_package(ament_cmake REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)

if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # uncomment the line when a copyright and license is not present in all source files
  #set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # uncomment the line when this package is not in a git repo
  #set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

ament_package()
package.xml
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>hoge</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="your-email">your-name</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
ament_python
file-tree
├── hoge
│   └── __init__.py
├── package.xml
├── resource
│   └── hoge
├── setup.cfg
├── setup.py
└── test
    ├── test_copyright.py
    ├── test_flake8.py
    └── test_pep257.py
setup.cfg
script-dir=$base/lib/hoge
[install]
install-scripts=$base/lib/hoge
setup.py
from setuptools import setup

package_name = 'hoge'

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='your-name',
    maintainer_email='your-email',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
        ],
    },
)
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>hoge</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="your-mail">your-name</maintainer>
  <license>TODO: License declaration</license>

  <test_depend>ament_copyright</test_depend>
  <test_depend>ament_flake8</test_depend>
  <test_depend>ament_pep257</test_depend>
  <test_depend>python3-pytest</test_depend>

  <export>
    <build_type>ament_python</build_type>
  </export>
</package>

--dependencies

ここからはament_cmakeベースでお話します

これは実に便利なやつなんです

例えば--build-typeament_cmakeにしたとして依存関係を記述したくなった時、
いつも依存関係をどのように記述していますか?
例えばrclcppを使いたくなり、cmakeに直接

find_package(rclcpp REQUIRED)

とか書いてませんか?
わざわざcmakeをこれを書くために編集するなんてめんどくさいと思いませんか?

ここで--dependenciesを使います

$ ros2 pkg create hoge --build-type ament_cmake --dependencies rclcpp

と書くだけでなんとCMakeLists.txtpackage.xmlに記述されるのです

実際に確認してみると

CMakeLists.txt
# find dependencies
find_package(rclcpp REQUIRED)
package.xml
<depend>rclcpp</depend>

と記述されていますね

他にもよく使うものとしてstd_msgs, sensor_msgs, pythonとc++を両立したいときにはament_cmake_python, rclpy, 独自にメッセージ型を記述したいときはrosidl_default_generatorsなどでしょうか

全部依存関係に書いてしまいましょう

$ ros2 pkg create hoge --build-type ament_cmake --dependencies rclcpp rclpy ament_cmake_python std_msgs sensor_msgs rosidl_default_generators

クッソなげぇですわ〜

でもこうすると

CMakeLists.txt
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclpy REQUIRED)
find_package(ament_cmake_python REQUIRED)
find_package(std_msgs REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)
package.xml
<depend>rclcpp</depend>
<depend>rclpy</depend>
<depend>ament_cmake_python</depend>
<depend>std_msgs</depend>
<depend>sensor_msgs</depend>
<depend>rosidl_default_generators</depend>

と勝手に記述してくれるので楽ですね

--node-name

これを書くだけでcmakeの記述がしなくて良くなります

どこまで書いてくれるのか確かめて見ましょう

次のコマンドを打ってプロジェクトを作成します

$ ros2 pkg create hoge --build-type ament_cmake --node-name my_node

ここでCMakeLists.txtはどうでしょうか

CMakeLists.txt
add_executable(my_node src/my_node.cpp)
target_include_directories(my_node PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>)

install(TARGETS my_node
  DESTINATION lib/${PROJECT_NAME})

と記述されていますね

また、生成されたファイルを見てみると

file-tree
.
├── CMakeLists.txt
├── include
│   └── hoge
├── package.xml
└── src
    └── my_node.cpp

となっていて、src/my_node.cppを見てみると、

#include <cstdio>

int main(int argc, char ** argv)
{
  (void) argc;
  (void) argv;

  printf("hello world hoge package\n");
  return 0;
}

となっています
いやもうちょっとがんばってよ

あとはament_target_dependenciesを記述するくらいでしょうか

+ --dependencies

ではここでさっきやった--dependenciesを記述しましょう
そうするとどうなるのでしょうか

$ ros2 pkg create huga --build-type ament_cmake --node-name my_node --dependencies rclcpp

ここでCMakeLists.txtを確認してみるとどうなっているかというと

CMakeLists.txt
ament_target_dependencies(
  my_node
  "rclcpp"
)

という行が追加されています

つまりament_target_dependenciesも書かなくてもいいということです

これで書く文量がかなり減ったのではないでしょうか

2つ書いてみた

ros2 pkg create hoge --build-type ament_cmake --node-name my_node1 --node-name my_node2

としたらどうなるか気になりませんか?

実行してCMakeLists.txtを確認してみると

CMakeLists.txt
add_executable(my_node2 src/my_node2.cpp)
target_include_directories(my_node2 PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>)

としか実行ファイルの生成が指示されていません

つまりあとに書いたノードしか生成されないということみたいです

ちなみに

file-tree
.
├── CMakeLists.txt
├── include
│   └── hoge
├── package.xml
└── src
    └── my_node2.cpp

からも本当に後ろに書いたノードしか生成してくれないようです

--library-name

ライブラリ作って便利にコーディングしたいという方のためにros2はライブラリの生成も対応してくれています

次のコマンドを打ってプロジェクトを作成しましょう

$ ros2 pkg create hoge --build-type ament_cmake --library-name my_lib

生成されるファイルは以下のようになっています

file-tree
.
├── CMakeLists.txt
├── include
│   └── hoge
│       ├── my_lib.hpp
│       └── visibility_control.h
├── package.xml
└── src
    └── my_lib.cpp

ここでCMakeLists.txtを確認すると

CMakeLists.txt
find_package(ament_cmake_ros REQUIRED)

# 省略

add_library(my_lib src/my_lib.cpp)
target_include_directories(my_lib PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>)
# 省略
target_compile_definitions(my_lib PRIVATE "HOGE_BUILDING_LIBRARY")

install(
  DIRECTORY include/
  DESTINATION include
)
install(
  TARGETS my_lib
  EXPORT export_${PROJECT_NAME}
  ARCHIVE DESTINATION lib
  LIBRARY DESTINATION lib
  RUNTIME DESTINATION bin
)

# 省略
  include
)
ament_export_libraries(
  my_lib
)
ament_export_targets(
  export_${PROJECT_NAME}
)

ではpackage.xmlはどうかというと

package.xml
  <buildtool_depend>ament_cmake_ros</buildtool_depend>

が追加されていますね

では生成されているinclude/hoge/my_lib.hppsrc/my_lib.cppを確認していきましょう

include/hoge/my_lib.hpp
#ifndef HOGE__MY_LIB_HPP_
#define HOGE__MY_LIB_HPP_

#include "hoge/visibility_control.h"

namespace hoge
{

class MyLib
{
public:
  MyLib();

  virtual ~MyLib();
};

}  // namespace hoge
src/my_lib.cpp
#include "hoge/my_lib.hpp"

namespace hoge
{

MyLib::MyLib()
{
}

MyLib::~MyLib()
{
}

}  // namespace hoge

クラスの基本のキみたいな感じのものが生成されていますね
基本的にはこれに追加していって書いていく感じです

では一番(?)気になっているinclude/hoge/visibility_control.hを見てみましょう

長いのでたたんでます

include/hoge/visibility_control.h
#ifndef HOGE__VISIBILITY_CONTROL_H_
#define HOGE__VISIBILITY_CONTROL_H_

// This logic was borrowed (then namespaced) from the examples on the gcc wiki:
//     https://gcc.gnu.org/wiki/Visibility

#if defined _WIN32 || defined __CYGWIN__
  #ifdef __GNUC__
    #define HOGE_EXPORT __attribute__ ((dllexport))
    #define HOGE_IMPORT __attribute__ ((dllimport))
  #else
    #define HOGE_EXPORT __declspec(dllexport)
    #define HOGE_IMPORT __declspec(dllimport)
  #endif
  #ifdef HOGE_BUILDING_LIBRARY
    #define HOGE_PUBLIC HOGE_EXPORT
  #else
    #define HOGE_PUBLIC HOGE_IMPORT
  #endif
  #define HOGE_PUBLIC_TYPE HOGE_PUBLIC
  #define HOGE_LOCAL
#else
  #define HOGE_EXPORT __attribute__ ((visibility("default")))
  #define HOGE_IMPORT
  #if __GNUC__ >= 4
    #define HOGE_PUBLIC __attribute__ ((visibility("default")))
    #define HOGE_LOCAL  __attribute__ ((visibility("hidden")))
  #else
    #define HOGE_PUBLIC
    #define HOGE_LOCAL
  #endif
  #define HOGE_PUBLIC_TYPE
#endif

#endif  // HOGE__VISIBILITY_CONTROL_H_

ビルド環境で変わるやつですね
つまり、様々な環境に対応したROS2に対応するためののヘッダファイルだと思われます

もし、これでライブラリを作るときは触るファイルはinclude/<project_name>/<lib_name>.hppsrc/<lib_name>.cppだけでいいみたいですね

このライブラリに対しても--dependenciesが使えるようです
なので、ここでも簡単に依存関係を記述できるみたいです

終わりに

これで簡単に書けるようになったのではないでしょうか
例えば今、rclcppstd_msgs, sensor_msgsを使ったノードを生成してくれと言われたらコマンド一行ですぐ作れますよね?

当然

$ ros2 pkg create hoge --node-name my_node --dependencies rclcpp std_msgs sensor_msgs

ですよね

これにてすべて終了です

では良きROS2ライフをお送りください!

不明点、間違っている点があればコメントなどで教えてください!

10
10
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
10
10