単純な ROS2 の例を学習すると、この問題が発生しない可能性があります。しかし、ROS2 が rclcpp などのパッケージを公式に提供している理由については、どうしても考えてしまいます。これらのパッケージを使用する場合、依存関係を直接追加し、find_package(rclcpp REQUIRED) を使用してそれらを見つけることができます。自分たちでコンパイルしたパッケージが他のパッケージでも使用できるかどうか。
もう少し大きなプロジェクトを多人数で開発する場合、自分が開発したパッケージが共通のパッケージになる可能性があるので、他の人にも使えるように提供したいのですが、どうすればよいでしょうか?
この記事では、例を使用して、この依存関係の問題を解決する方法を説明します。ROS2プロジェクトに共通パッケージとサンプルパッケージの2つのパッケージを作成する例で、サンプルパッケージは共通ライブラリに依存しています。
この効果を達成するには、いくつかの問題を考慮する必要があります。
- 共通パッケージをコンパイルする方法
- ROS2 並列コンパイル、共通パッケージがサンプル パッケージの前にコンパイルされることを確認する方法
- サンプルパッケージを使用する場合に、共通パッケージを含める必要があるヘッダーファイルのパスとダイナミックライブラリのパスを確認する方法
上記の最初の質問は最も基本的な質問であり、他の人に提供したい場合は、自分自身でコンパイルする必要があります。
2 番目の質問は、多くのパッケージを含む少し大きなプロジェクトがある場合、コンパイルを高速化するために、colcon build を使用すると、複数のパッケージが同時にコンパイルされることがわかります。ただし、自分のパッケージが他の人が使用する前にコンパイルされることを保証する方法はありません。もちろん、デフォルトの方法を使用して並列コンパイルされない場合でも、すべてのパッケージが順次コンパイルできることを保証する方法はありません。
この問題を解決するには、パッケージの package.xml に依存関係を追加する必要があります。たとえば、サンプル パッケージは共通パッケージに依存しているため、それをサンプル パッケージの package.xml に追加します。
<depend>common</depend>
パッケージ B をコンパイルするときにパッケージ B がパッケージ A に依存しており、パッケージ A を最初にコンパイルする必要があることをコンパイラーに伝えます。実際、package.xml から rclcpp への依存関係も確認できます。
<depend>rclcpp</depend>
rclcpp がずっと前にコンパイルされ、システムにインストールされているだけです。
3 番目の質問については、サンプル パッケージの実装では共通パッケージのヘッダー ファイルをインクルードし、ダイナミック ライブラリをリンクする必要があるため、共通パッケージのヘッダー ファイルのパスとダイナミック ライブラリのパスをサンプル パッケージに知らせるにはどうすればよいですか? CMake について知っている人なら、次のように変数を直接設定することで解決できることを間違いなく知っていると思います。
set(COMMON_INCLUDE_DIR xxx/xxx/include)
set(COMMON_LIBRARY_PATH xxx/xxx/lib)
Cmake とプロジェクトの相対ディレクトリについてよく理解している場合は、それを作成できます。ただし、この方法ではより粗く感じられます。また、絶対パスで記述されることが多く、ディレクトリが変わると見つからなくなります。
Cmake は、対応するパッケージを検索し、関連するヘッダー ファイルとライブラリ環境変数をそのパッケージに追加する find_package() 関数を提供します。
find_package(xxxx) はどのようにしてそれを実現するのでしょうか? 原理は、CMake の検索ディレクトリで xxx パッケージの cmake フォルダーを見つけることです。通常は、xxxConfig.cmake または Findxxx.cmake ファイルが存在します。ファイルの 1 つが見つかる限り、xxx のすべての環境変数を見つけることができます。を見つけて cmake にロードします。詳細についてはここでは説明しませんが、一般的な原則だけ知っておいてください。このような Findxxx.cmake ファイルを自分で作成することはほとんどないため、インストールするライブラリのほとんどは、インストールを実行するときにこのファイルを生成します。
find_package() を使用すると、環境変数を直接設定するよりも洗練されており、ROS2 ライブラリに依存する場合にもこれを使用します。たとえば、ROS2 プログラムを作成するとき、次のような使用法がよく見られます。
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
ここで考えなければならないのは、他の人が find_package() を使用して直接見つけられるように、独自のパッケージを rclcpp のようなパッケージにパッケージ化する方法です。
実はその理由は非常に単純で、ヘッダーファイルのパスとダイナミックライブラリのパスをエクスポートする方法です。このようにして、他のパッケージを使用できるようになります。
ここではROS2のamentの2つの機能を利用します。
ament_export_include_directories(include)
ament_export_libraries(your_library)
これら 2 つの機能の 1 つはヘッダー ファイルのエクスポートであり、もう 1 つはダイナミック ライブラリのエクスポートです。このようにして、colcon がこのパッケージをコンパイルすると、xxxConfig.cmake ファイルが生成されます。他のパッケージは find_package() で見つけることができます。
実装原理を明確に説明し、以下に例を示します。
コード構造は次のとおりです。
crabe@crabe-virtual-machine:~/Desktop/sample$ tree sample2/
sample2/
└── src
├── common
│ ├── CMakeLists.txt
│ ├── include
│ │ └── common
│ │ └── common.h
│ ├── package.xml
│ └── src
│ └── common.cpp
└── sample
├── CMakeLists.txt
├── include
│ └── sample
│ └── sample.h
├── package.xml
└── src
└── sample.cpp
共通のパッケージとサンプルの 2 つのパッケージをビルドしました。
共通パッケージの cpp には次のような単純な実装のみがあり、ヘッダー ファイルはこのクラスの定義です。
#include "common/common.h"
#include <iostream>
using namespace std;
CommonLib::CommonLib()
{
cout << "construct.."<<endl;
}
CommonLib::~CommonLib()
{
}
void CommonLib::Publish()
{
cout << "this is common package func.."<< endl;
}
共通パッケージのCmakeList.txtは以下のとおりです。
cmake_minimum_required(VERSION 3.5)
project(common)
# 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)
find_package(rclcpp REQUIRED)
#add_executable(common src/common.cpp)
add_library(common SHARED ${CMAKE_CURRENT_SOURCE_DIR}/src/common.cpp)
target_include_directories(common PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
ament_target_dependencies(
common
"rclcpp"
)
ament_export_include_directories(include)
install(DIRECTORY include/
DESTINATION include
)
install(
TARGETS common
LIBRARY DESTINATION lib
)
ament_export_libraries(${PROJECT_NAME})
#ament_export_targets(common)
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()
この CMakeList.txt は主に 2 つのインストールを行い、ヘッダー ファイルとダイナミック ライブラリをインストール ディレクトリにインストールします。
install(DIRECTORY include/
DESTINATION include
)
install(
TARGETS common
LIBRARY DESTINATION lib
)
次に、サンプル パッケージを使用できるように、ament_export を通じてディレクトリを commonConfig.cmake にエクスポートします。
ament_export_include_directories(include)
ament_export_libraries(${PROJECT_NAME})
サンプルパッケージの書き方
#include "sample/sample.h"
#include <iostream>
using namespace std;
Sample::Sample()
{
lib_ = make_shared<CommonLib>();
}
Sample::~Sample()
{
}
void Sample::Publish()
{
lib_->Publish();
}
int main(int argc, char ** argv)
{
Sample sample;
sample.Publish();
return 0;
}
サンプルパッケージには主にヘッダーファイルが含まれています
#include "sample/sample.h"
共通に定義されたクラス CommonLib の使用もあり、これらは非常に一般的な記述方法です。
次に、サンプルの CMakeList.txt の書き方を見てみましょう。
cmake_minimum_required(VERSION 3.5)
project(sample)
# 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 -std=c++11)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(common REQUIRED)
if(common_FOUND)
message("find the lib common")
else()
message("not find lib common")
endif()
add_executable(sample src/sample.cpp)
target_include_directories(sample PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
${common_INCLUDE_DIRS})
ament_target_dependencies(
sample
"rclcpp"
common
)
install(TARGETS sample
DESTINATION lib/${PROJECT_NAME})
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()
ここで直接使用されます
find_package(common REQUIRED)
共通パッケージを見つけるには、変数 common_FOUND を通じてパッケージが見つかったかどうかを知ることができ、以下で使用するときにヘッダー ファイル パスに ${common_INCLUDE_DIRS} を追加し、リンク依存関係 ament_target_dependency に common を追加します。普通にリンクします。
最後に、コンパイルの順序が保証されるように、サンプルの 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>sample</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="[email protected]">yangcb</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>common</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>