本文的目的是了解如何在 ROS2 中创建库,以供其他 ROS2 包使用。除此之外,本文还介绍了如何使用 catch2 框架编写单元测试。本文的第 1 部分将详细介绍如何创建库包。第 2 部分将介绍 ROS2 软件包如何利用创建的库
库的流程
// include/try_out_utils/point.hpp
#ifndef TRY_OUT_UTILS__POINT_HPP_
#define TRY_OUT_UTILS__POINT_HPP_
namespace try_out_utils
{
class Point
{
private:
double x_;
double y_;
public:
Point(double x, double y);
double get_x();
double get_y();
};
} // namespace try_out_utils
#endif // TRY_OUT_UTILS__POINT_HPP_
具有两个私有变量 x 和 y 的标头类,用于表示 2d 中的点及其 getter 和 setter 方法
// src/point.cpp
#include
Point::Point(double x, double y)
{
this->x_ = x;
this->y_ = y;
}
double Point::get_x()
{
return this->x_;
}
double Point::get_y()
{
return this->y_;
}
之前在标头类中声明的 2d 点的实现类
// test/main.cpp
#define CATCH_CONFIG_MAIN
#include
测试用例的 Catch2 入口点
// test/test_point.cpp
#include
#include
TEST_CASE("Test for point", "[]")
{
SECTION("Test for point with object creation")
{
Point p(11, 10);
REQUIRE(p.get_x() == 11);
REQUIRE(p.get_y() == 10);
}
}
对点类进行单元测试以创建新的点对象
@PACKAGE_INIT@
get_filename_component(try_out_utils_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
include(CMakeFindDependencyMacro)
if(NOT TARGET try_out_utils::try_out_utils)
include("${try_out_utils_CMAKE_DIR}/try_out_utils-targets.cmake")
endif()
check_required_components(try_out_utils)
Cmake 配置别名,以便外部包可以引用该库作为 try_out_utils::try_out_utils
# setting up cmake minimum version and project name
cmake_minimum_required(VERSION 3.8)
project(try_out_utils VERSION 0.1.0)
# setting c++ version standard to 17
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
endif()
# adding compiler arguments
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# added to use install directory variables
include(GNUInstallDirs)
# adding external dependencies required
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(ament_cmake_catch2 REQUIRED)
find_package(Catch2 REQUIRED)
# creating library package with reference to required files
add_library(${PROJECT_NAME} SHARED
src/point.cpp
)
# including external directories reference for the created library
target_include_directories(try_out_utils PUBLIC
$
$
)
# helpers functions for creating config files that can be included by other projects to find and use a package
include(CMakePackageConfigHelpers)
set(INSTALL_CONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}/cmake")
set(PACKAGE_CONFIG_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake")
set(PACKAGE_CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake")
# creating version configuration for external package to perform compatibility check
configure_package_config_file(
"${CMAKE_CURRENT_LIST_DIR}/cmake/${PROJECT_NAME}-config.cmake.in"
${PACKAGE_CONFIG_FILE}
INSTALL_DESTINATION ${INSTALL_CONFIG_DIR}
)
# creating version configuration for external package to perform compatibility check
write_basic_package_version_file(
${PACKAGE_CONFIG_VERSION_FILE}
COMPATIBILITY ExactVersion
)
# installing library files
install(
TARGETS try_out_utils
EXPORT try_out_utils-targets
DESTINATION lib
)
# installing all reference header files
install(
DIRECTORY include/try_out_utils
DESTINATION include/
)
# installing cmake config files for try_out_utils
install(
FILES
${PACKAGE_CONFIG_VERSION_FILE}
${PACKAGE_CONFIG_FILE}
DESTINATION ${INSTALL_CONFIG_DIR}
)
# installing cmake config files for try_out_utils-targets
install(
EXPORT try_out_utils-targets
FILE try_out_utils-targets.cmake
NAMESPACE try_out_utils::
DESTINATION ${INSTALL_CONFIG_DIR}
)
# exporting the try_out_utils-target cmake config to build folder
export(
EXPORT try_out_utils-targets
FILE ${CMAKE_CURRENT_BINARY_DIR}/try_out_utils-targets.cmake
NAMESPACE try_out_utils::
)
# checking whether build includes test
if(BUILD_TESTING)
# listing files for testing
file(GLOB_RECURSE unit_test_srcs "test/*.cpp")
# adding listed files for testing
ament_add_catch2(test_try_out_utils ${unit_test_srcs} TIMEOUT 300)
# linking libraries required to the current package for testing
target_link_libraries(
test_try_out_utils
try_out_utils
Catch2::Catch2
)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
让我们详细介绍一下上面创建的cmakelist
configure_package_config_file()
configure_file()
Config.cmake
-config.cmake
Config.cmake
DIRECTORY
FILES
EXPORT
...
(NAMESPACE)
EXPORT
try_out_utils
0.1.0
Utility package for commonly used functions
santosh balaji
Apache License 2.0
ament_cmake
ament_lint_auto
ament_lint_common
ament_cmake_catch2
ament_cmake_uncrustify
ament_cmake
用于指定依赖项的包文件
# To build created package
colcon build --packages-select try_out_utils
# To run tests on package
colcon test --event-handlers console_direct+ --packages-select try_out_utils
运行上述命令以构建和测试库包
// include/try_out/point_checker.hpp
#ifndef TRY_OUT__POINT_CHECKER_HPP_
#define TRY_OUT__POINT_CHECKER_HPP_
#include
#include
#include
#include
class PointChecker
{
private:
std::vector points_;
public:
void add_point(double x, double y);
std::vector> find_distance_matrix();
};
#endif // TRY_OUT__POINT_CHECKER_HPP_
带有向量的标头类,用于存储点和逻辑函数
// src/point_checker.cpp
#include
#include
void PointChecker::add_point(double x, double y)
{
try_out_utils::Point * point = new try_out_utils::Point(x, y);
this->points_.push_back(point);
}
std::vector> PointChecker::find_distance_matrix()
{
std::vector> overall_vect;
for (unsigned int i = 0; i < this->points_.size(); i++) {
std::vector inner_vect;
for (unsigned int j = 0; j < this->points_.size(); j++) {
double x_compute =
(this->points_[j]->get_x() - this->points_[i]->get_x()) *
(this->points_[j]->get_x() - this->points_[i]->get_x());
double y_compute =
(this->points_[j]->get_y() - this->points_[i]->get_y()) *
(this->points_[j]->get_y() - this->points_[i]->get_y());
double distance = std::sqrt(x_compute + y_compute);
inner_vect.push_back(distance);
}
overall_vect.push_back(inner_vect);
}
return overall_vect;
}
int main()
{
PointChecker point_checker;
point_checker.add_point(5, 5);
return 0;
}
前面在标头中声明的逻辑类的实现类。此处添加了距离计算逻辑
// test/main.cpp
#define CATCH_CONFIG_MAIN
#include
测试用例的 Catch2 入口点
// test/test_point_checker.cpp
#include
#include
#include
#include
TEST_CASE("Test for point checker", "[]")
{
SECTION("Test for distance matrix computation"
{
PointChecker point_checker;
point_checker.add_point(1, 1);
point_checker.add_point(2, 2);
point_checker.add_point(3, 3);
std::vector> result =
point_checker.find_distance_matrix();
REQUIRE(result.size() == 3);
REQUIRE(result[0].size() == 3);
REQUIRE(result[1].size() == 3);
REQUIRE(result[2].size() == 3);
REQUIRE(result[0][0] == 0);
REQUIRE(std::round(result[0][1] - 1.4142135624) == 0);
REQUIRE(std::round(result[0][2] - 2.8284271247) == 0);
REQUIRE(std::round(result[1][2] - 1.4142135624) == 0);
REQUIRE(result[1][1] == 0);
REQUIRE(std::round(result[1][2] - 1.4142135624) == 0);
REQUIRE(std::round(result[2][0] - 2.8284271247) == 0);
REQUIRE(std::round(result[2][1] - 1.4142135624) == 0);
REQUIRE(result[2][2] == 0);
}
}
点检查器类的单元测试,用于计算提供的点之间的距离
# setting up cmake minimum version and project name
cmake_minimum_required(VERSION 3.8)
project(try_out VERSION 0.1.0)
# setting c++ version standard to 17
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
endif()
# adding compiler arguments
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# adding external dependencies required
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(try_out_utils REQUIRED)
find_package(ament_cmake_catch2 REQUIRED)
find_package(Catch2 REQUIRED)
# creating library package with reference to required files
add_library(try_out SHARED
src/point_checker.cpp
)
# including external directory reference for the created library
target_include_directories(try_out PUBLIC
$
$
${rclcpp_INCLUDE_DIRS})
# including external library reference for the created library
target_link_libraries(try_out
PUBLIC
try_out_utils::try_out_utils
)
# creating executables with reference to required files
add_executable(
try src/point_checker.cpp)
# including external directory reference for the created executable
target_include_directories(try PUBLIC
$
$
${rclcpp_INCLUDE_DIRS})
# including external library reference for the created executable
target_link_libraries(try
${rclcpp_LIBRARIES}
try_out_utils::try_out_utils
)
# installing executable file
install(TARGETS
try
DESTINATION lib})
# installing library file
install(
TARGETS try_out
DESTINATION lib
)
# installing all reference header files
install(
DIRECTORY include/try_out
DESTINATION include/
)
# checking whether build includes test
if(BUILD_TESTING)
# listing files for testing
file(GLOB_RECURSE unit_test_srcs "test/*.cpp")
# adding listed files for testing
ament_add_catch2(test_try_out ${unit_test_srcs} TIMEOUT 300)
# linking libraries required to the current package for testing
target_link_libraries(test_try_out
try_out
try_out_utils::try_out_utils
Catch2::Catch2)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
让我们详细介绍一下上面创建的cmakelist
DIRECTORY
TARGETS
try_out
0.1.0
Work package which utilizes the created library package
santosh balaji
Apache License 2.0
ament_cmake
try_out_utils
ament_lint_auto
ament_lint_common
ament_cmake_catch2
ament_cmake_uncrustify
ament_cmake
用于指定依赖项的包文件
# To build created package
colcon build --packages-select try_out
# To run tests on package
colcon test --event-handlers console_direct+ --packages-select try_out
运行上述命令以构建和测试库包
参考资料
GitHub - open-rmf/rmf_utils: Internal utilities for RMF libraries (Robotics middleware framework utilities)
GitHub - open-rmf/rmf_traffic: Traffic management libraries for RMF (Traffic management framework which uses the utility library)
CMake Reference Documentation — CMake 3.27.6 Documentation (Cmake documentation)
ament_cmake user documentation — ROS 2 Documentation: Foxy documentation (Enhanced version of Cmake for ROS2 packages)
Source code
GitHub - santoshbalaji/ros2-library-package-medium: Repository which has package used for ROS2 based work package tutorial