Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

ROS2における共有ライブラリ

ROS2関係トップページへ

【次:ROS2の共有ライブラリを使用したプログラムの作成

Eloquentまではこちら
更新履歴:
2020/07/24:ament_export_dependenciesについて必要ないかのように書いていたけど,やっぱり必要だったので修正


ROS2の共有ライブラリを作成する.

共有ライブラリはクラスで作成するので,これまでのようにROS1風のものはない.
また,共有ライブラリは色々なところで使用されるのでnamespaceを用いている.
本当はこれまででもnamespaceを使用した方がよかったのだが,自分が使用するターゲットのみの場合必須でないので省略してきた.
namespaceに関してはROS2関係トップページへの「知っといてほしい知識」などを参考に.

共通ライブラリ概要

ここで作成する共有ライブラリの概要は以下のとおり.
ちなみに作成したライブラリを使用したターゲットはここで作成せず共有ライブラリのみの作成とする.
また作成するクラスは一つでも良かったが,後々のために二つ作成する.基本的には一つのライブラリに複数の機能が収められる.

  • 共有ライブラリのパッケージ名
    • minimal_comp
  • 共有ライブラリ名
    • minimal_comp_node
  • 共有ライブラリで使用できる機能・クラスを収めたソースファイル
    • minimal_comp_node1.hppと.cpp
      • クラス名:MinimalCompNode1
    • minimal_comp_node2.hppと.cpp
      • クラス名:MinimalCompNode2
  • パッケージ共通のnamespace
    • minimal_comp

準備

terminal
$ cd ~/ros2_studies_ws/
$ ros2 pkg create minimal_comp --dependencies rclcpp

共有ライブラリ

作成物:github.comの以下のファイル

  • include/visibility.h
  • include/minimal_comp_node1.hpp
  • include/minimal_comp_node2.hpp
  • src/minimal_comp_node1.cpp
  • src/minimal_comp_node2.cpp

共有ライブラリ独特の手順

  1. visibility.hを作成する
    • 公式からDLして自分のパッケージ用にアレンジするのが楽
  2. 普通にクラスを作成する
    • 外部に公開するのでsrc/以下にヘッダファイルを置くのではなくinclude/[パッケージ名]/以下にヘッダファイルを置く.
  3. 作成したクラスを共有ライブラリ用に少し追記する
    • visibility.hで書いたPUBLICに相当する記述(ここではMINIMAL_COMP_PUBLICになる)をpublicな関数(外部に公開する機能)の前に書く

visibility.hの用意

自分で以下のファイルを用意するのでもよいが,公式のexamplesにあるminimal_compositionからvisibility.hをDLして自分用に改変することを推奨.

terminal
$ curl https://raw.githubusercontent.com/ros2/examples/master/rclcpp/composition/minimal_composition/include/minimal_composition/visibility.h > visibility.h
$ sed s/MINIMAL_COMPOSITION/MINIMAL_COMP/g visibility.h > my_visibility.h
my_visibility.hの内容を確認
$ mv my_visibility.h visibility.h

一度にやるなら以下のとおり.

terminal
$ curl https://raw.githubusercontent.com/ros2/examples/master/rclcpp/composition/minimal_composition/include/minimal_composition/visibility.h | sed s/MINIMAL_COMPOSITION/MINIMAL_COMP/g > visibility.h

今回はパッケージ名がminimal_compであったので上記のようにsedを使用した.ここは自分のパッケージ名に合わせて変更すること.

$ sed s/MINIMAL_COMPOSITION/[自分のパッケージ名]/g visibility.h > my_visibility.h

自分で作成する場合は以下の通り.
ここでもパッケージ名minimal_compで作成しているので,自分のパッケージ名に合わせて適宜変更

visibility.h
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef MINIMAL_COMP__VISIBILITY_H_
#define MINIMAL_COMP__VISIBILITY_H_

#ifdef __cplusplus
extern "C"
{
#endif

// 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 MINIMAL_COMP_EXPORT __attribute__ ((dllexport))
    #define MINIMAL_COMP_IMPORT __attribute__ ((dllimport))
  #else
    #define MINIMAL_COMP_EXPORT __declspec(dllexport)
    #define MINIMAL_COMP_IMPORT __declspec(dllimport)
  #endif

  #ifdef MINIMAL_COMP_DLL
    #define MINIMAL_COMP_PUBLIC MINIMAL_COMP_EXPORT
  #else
    #define MINIMAL_COMP_PUBLIC MINIMAL_COMP_IMPORT
  #endif

  #define MINIMAL_COMP_PUBLIC_TYPE MINIMAL_COMP_PUBLIC

  #define MINIMAL_COMP_LOCAL

#else

  #define MINIMAL_COMP_EXPORT __attribute__ ((visibility("default")))
  #define MINIMAL_COMP_IMPORT

  #if __GNUC__ >= 4
    #define MINIMAL_COMP_PUBLIC __attribute__ ((visibility("default")))
    #define MINIMAL_COMP_LOCAL  __attribute__ ((visibility("hidden")))
  #else
    #define MINIMAL_COMP_PUBLIC
    #define MINIMAL_COMP_LOCAL
  #endif

  #define MINIMAL_COMP_PUBLIC_TYPE
#endif

#ifdef __cplusplus
}
#endif

#endif  // MINIMAL_COMP__VISIBILITY_H_

共有ライブラリ化する二つのクラス

注目点はpublicとしているコンストラクタの前.visibility.hにあるMINIMAL_COMP_PUBLICが付いていることを確認.

minimal_comp_node1.hpp
#include <rclcpp/rclcpp.hpp>
#include "minimal_comp/visibility.h" // 同一ディレクトリなら#include "visibility.h"でもok

namespace minimal_comp {

class MinimalCompNode1 : public rclcpp::Node{
public:
  MINIMAL_COMP_PUBLIC
  MinimalCompNode1(
    const std::string &node_name="",
    const rclcpp::NodeOptions& options=rclcpp::NodeOptions()
  );
};

}
minimal_comp_node1.cpp
#include <rclcpp/rclcpp.hpp>
#include "minimal_comp/minimal_comp_node1.hpp"

namespace minimal_comp {

MinimalCompNode1::MinimalCompNode1(
  const std::string &node_name,
  const rclcpp::NodeOptions& options
): Node("minimal_comp1", node_name, options)
{
  RCLCPP_INFO(this->get_logger(),"minimal comp 1 test");
}

}
minimal_comp_node2.hpp
#include <rclcpp/rclcpp.hpp>
// 同一ディレクトリなら#include "visibility.h"でもok

namespace minimal_comp {

class MinimalCompNode2 : public rclcpp::Node{
public:
  MINIMAL_COMP_PUBLIC
  MinimalCompNode2(
    const std::string &node_name="",
    const rclcpp::NodeOptions& options=rclcpp::NodeOptions()
  );
};

}
minimal_comp_node2.cpp
#include <rclcpp/rclcpp.hpp>
#include "minimal_comp/minimal_comp_node2.hpp"

namespace minimal_comp {

MinimalCompNode2::MinimalCompNode2(
  const std::string &node_name,
  const rclcpp::NodeOptions& options
): Node("minimal_comp2", node_name, options)
{
  RCLCPP_INFO(this->get_logger(),"minimal comp 2 test");
}

}

概要

簡単なメッセージを出力するだけのもの

package.xmlとCMakeLists.txt

以下は追加した・重要な部分のみ抜粋.
詳しくはament_cmake User Documentationを参照.

package.xml

package.xml
<package format="3">
  <depend>rclcpp</depend>

build_dependタグからdependタグへの変更についてはROS2 package.xmlとCMakeLists.txtのチートシートを参照のこと.

CMakeLists.txt

CMakeLists.txt
find_package(rclcpp REQUIRED)

add_library(minimal_comp_node SHARED
  src/minimal_comp_node1.cpp
  src/minimal_comp_node2.cpp
)
target_compile_definitions(minimal_comp_node
  PRIVATE "MY_LIBRARY_BUILDING_LIBRARY"
)
target_compile_options(minimal_comp_node PUBLIC -Wall)
ament_target_dependencies(minimal_comp_node
  rclcpp
)

target_include_directories(minimal_comp_node
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
)

if(NOT WIN32)
  ament_environment_hooks(
    "${ament_cmake_package_templates_ENVIRONMENT_HOOK_LIBRARY_PATH}"
  )
endif()

ament_export_targets(export_${PROJECT_NAME} HAS_LIBRARY_TARGET)
ament_export_dependencies(
  rclcpp
)

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

ament_export_*に関するFoxy Fitzroyからの変更について

Eloquent Elusorまでは共有ライブラリのエクスポートに関するコマンドは以下の4つ3つであった.
  • ament_export_interfaces
  • ament_export_libraries
  • ament_export_inclujde_directories
  • ament_export_dependencies
    • これは必要だった!

これがament_export_targetsの一つになった.使い方としては,ament_export_interfacesの代わりにament_export_targetsを使うような感じらしい.

詳しくは公式のDistributionのニュースのうち,Foxy Fitzroyでの変更:Classic CMake vs. modern CMakeを参照のこと.

概要

通常のfind_package,ament_target_dependencies以外に以下の要素がある.

  • add_library
  • target_compile_definitions
  • target_compile_options
  • target_include_directories
  • ament_environment_hooks
  • ament_export_interfaces
  • ament_export_dependencies
  • ament_export_libraries
  • ament_export_include_directories
  • install 2種類

説明:add_library

add_executableと同じように他で使用するライブラリ名を付けつつコンパイルに必要なソースファイルを記述.
ライブラリ名を使用するのは以下の要素.

  • target_compile_definitions
  • target_compile_options
  • ament_target_dependencies
  • target_include_directories
  • ament_export_libraries
  • install

説明:target_compile_*

コンパイル時の設定.他でもそのまま使用.

説明:target_include_directories

include/[パッケージ名]/以下のヘッダファイルは外部に公開するものである.他のパッケージはtarget_include_directoriesによって外部公開用のヘッダファイルにアクセスできる.共有ライブラリ自身にとってもinclude/[パッケージ名]/以下にヘッダファイルがあるので(src/以下にはないので),target_include_directoriesを指定する必要がある.

説明:ament_environment_hooks

OS依存の処理っぽい.他でもそのまま使用.

説明:install

最初のinstallは共有ライブラリを使用するパッケージ用にヘッダファイルをインストールするためのコマンド.

install(
  DIRECTORY include/
  DESTINATION include
)

二つ目のinstallは作成したライブラリをインストールするためのコマンド.
特にEXPORTはament_export_interfacesで使うためのもの.
名前はなんでもよいがライブラリ名にexport_を付けたものが用いられる.
理由はament_cmake User DocumentationのBuilding a Libraryを参照のこと.

install(TARGETS
  minimal_comp_node
  EXPORT export_minimal_comp_node
  ARCHIVE DESTINATION lib
  LIBRARY DESTINATION lib
  RUNTIME DESTINATION bin
  INCLUDES DESTINATION include
)

説明:ament_export_targets

ament_export_targetsは,installのEXPORTで指定したものにHAS_LIBRARY_TARGETを.

説明:ament_export_dependencies

共有ライブラリを使う側に依存関係を知らせ,使う側で改めて依存関係処理しなくて済むようにするもの.

ビルド

$ cd ~/ros2_studies_ws/
$ colcon build --symlink-install --packages-up-to minimal_comp
$ . install/setup.bash

複数のライブラリを作成する場合

一つのライブラリの中に複数の機能(クラス)を詰め込むのではなく,複数のライブラリ自体を一つのパッケージで作成する場合.

一つのライブラリのやり方の中で,以下に注目・変更

  • ament_export_targets
  • install

概要

作成したライブラリをインストールするためのinstallにて.

  • TARGETS
    • add_libraryのライブラリ名を複数記述
  • EXPORT
    • この項は全体で一つ.
    • よって,例えばexport_[package名]などに.
  • ament_export_targets
    • EXPORTで名づけしたものを記述
    • よって上記の例ではexport_[package名]

抜粋

CMakeLists.txt
ament_export_targets(export_${PROJECT_NAME} HAS_LIBRARY_TARGET)

install(TARGETS
  model1
  model2
  EXPORT export_${PROJECT_NAME}
...
)

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away