主页 > 其他  > 

ROS2库包设置和使用Catch2进行单元测试

ROS2库包设置和使用Catch2进行单元测试

说明

        本文的目的是了解如何在 ROS2 中创建库,以供其他 ROS2 包使用。除此之外,本文还介绍了如何使用 catch2 框架编写单元测试。本文的第 1 部分将详细介绍如何创建库包。第 2 部分将介绍 ROS2 软件包如何利用创建的库

上篇  ROS2 库包设置和使用 Catch2 进行单元测试 一、项目结构大纲

库的流程

在包含/点下为 2d 点创建标题.hpp在 src/point 下为 2d 点添加实现类.cpp为测试下的测试类/主类创建入口点.cpp在测试/test_point.cpp下添加单元测试在 cmake/try_out_utils-config.cmake.in 下为 cmake 添加命名空间配置在根项目目录下创建 CMakeLists.txt (此处指定了 lib 的配置)在根项目目录下创建包.xml  二、程序代码实现 2.1 为 2d 点创建标题.hpp // 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_  2.2 为 2d 点添加实现类.cpp

        具有两个私有变量 x 和 y 的标头类,用于表示 2d 中的点及其 getter 和 setter 方法

// src/point.cpp #include <try_out_utils/point.hpp> 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/catch.hpp>

测试用例的 Catch2 入口点

// test/test_point.cpp #include <catch2/catch.hpp> #include <try_out_utils/point.hpp> 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

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 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> ) # 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

include(GNUInstallDirs) — 允许使用 cmake 安装变量add_library — 使用指定的参照文件创建库target_include_directories — 要包含在目标中的内部和外部包中的目录include(CMakePackageConfigHelpers) — 用于创建配置文件的帮助程序函数,其他项目可以包含这些文件来查找和使用包configure_package_config_file — 在创建用于安装项目或库的 or 文件时,应使用代替普通命令。它通过避免已安装文件中的硬编码路径来帮助使生成的包可重定位configure_package_config_file()configure_file()<PackageName>Config.cmake<PackageName>-config.cmakeConfig.cmakewrite_basic_package_version_file — 应该用于创建版本配置文件,以便导入此库的外部包可以执行与此处提供的版本兼容性检查在目标位置安装目录 — 表单将一个或多个目录的内容安装到给定目标DIRECTORY在目标位置安装文件 — 表单指定为项目安装文件的规则。在指定目标中安装文件FILES在目标位置安装导出 — 该窗体生成并安装一个 Make 文件,其中包含用于将目标从安装树导入到另一个项目中的代码EXPORT出口 — 创建一个可能由外部项目包含的文件,以从当前项目的生成树中导入命名的目标。这在交叉编译期间非常有用,可以生成实用程序可执行文件,这些实用程序可执行文件可以在一个项目中的主机平台上运行,然后将它们导入到为目标平台编译的另一个项目中。将字符串附加到写入文件的所有目标名称前面<filename><target>...(NAMESPACE)<namespace>使用导出在目标位置安装目标 — 在指定目标位置安装目标的代码段。选项将已安装的目标文件与名为EXPORT<export-name>ament_add_catch2 — 使用为测试提供的测试文件创建测试目标target_link_libraries — 将目标与指定的 库链接起来(Ament 是一个用 Cmake 编写的包装器,用于简化 colcon 构建的一些功能) <?xml version="1.0"?> <?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http:// .w3.org/2001/XMLSchema"?> <package format="3"> <name>try_out_utils</name> <version>0.1.0</version> <description>Utility package for commonly used functions</description> <maintainer email="sample@email ">santosh balaji</maintainer> <license>Apache License 2.0</license> <buildtool_depend>ament_cmake</buildtool_depend> <test_depend>ament_lint_auto</test_depend> <test_depend>ament_lint_common</test_depend> <test_depend>ament_cmake_catch2</test_depend> <test_depend>ament_cmake_uncrustify</test_depend> <export> <build_type>ament_cmake</build_type> </export> </package>   四、包依赖关系

      用于指定依赖项的包文件

# 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       

        运行上述命令以构建和测试库包

执行后的测试结果

下篇 ROS2 库包设置和使用 Catch2 进行单元测试 五、项目结构大纲

六、库的流程 在包含/try_out/point_checker.hpp 下为逻辑函数创建标头在 src/point_checker.cpp 下为逻辑函数添加实现类为测试下的测试类/主类创建入口点.cpp在测试/test_point_checker.cpp下添加单元测试在根项目目录下创建 CMakeLists.txt在根项目目录下创建包.xml 七、程序实现 // include/try_out/point_checker.hpp #ifndef TRY_OUT__POINT_CHECKER_HPP_ #define TRY_OUT__POINT_CHECKER_HPP_ #include <try_out_utils/point.hpp> #include <memory> #include <cmath> #include <vector> class PointChecker { private: std::vector<try_out_utils::Point *> points_; public: void add_point(double x, double y); std::vector<std::vector<double>> find_distance_matrix(); }; #endif // TRY_OUT__POINT_CHECKER_HPP_

带有向量的标头类,用于存储点和逻辑函数

// src/point_checker.cpp #include <try_out/point_checker.hpp> #include <vector> 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<std::vector<double>> PointChecker::find_distance_matrix() { std::vector<std::vector<double>> overall_vect; for (unsigned int i = 0; i < this->points_.size(); i++) { std::vector<double> 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/catch.hpp>

测试用例的 Catch2 入口点

// test/test_point_checker.cpp #include <catch2/catch.hpp> #include <try_out/point_checker.hpp> #include <vector> #include <cmath> 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<std::vector<double>> 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 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> ${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 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> ${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

add_library — 使用指定的参照文件创建库add_executable — 使用指定文件创建可执行文件target_include_directories — 要包含在目标中的内部和外部包中的目录。可以使用命令target_link_libraries — 将目标与指定的库链接在目标位置安装目录 — 表单将一个或多个目录的内容安装到给定目标DIRECTORY在目标位置安装目标 — 表单指定从项目安装目标的规则TARGETSament_add_catch2 — 使用提供的 测试文件创建测试目标(Ament 是一个用 Cmake 编写的包装器,用于简化 colcon 构建的一些功能) <?xml version="1.0"?> <?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http:// .w3.org/2001/XMLSchema"?> <package format="3"> <name>try_out</name> <version>0.1.0</version> <description>Work package which utilizes the created library package</description> <maintainer email="santechselva@gmail ">santosh balaji</maintainer> <license>Apache License 2.0</license> <buildtool_depend>ament_cmake</buildtool_depend> <depend>try_out_utils</depend> <test_depend>ament_lint_auto</test_depend> <test_depend>ament_lint_common</test_depend> <test_depend>ament_cmake_catch2</test_depend> <test_depend>ament_cmake_uncrustify</test_depend> <export> <build_type>ament_cmake</build_type> </export> </package> 十、单元测试实现

        用于指定依赖项的包文件

# 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 codeGitHub - santoshbalaji/ros2-library-package-medium: Repository which has package used for ROS2 based work package tutorial

罗斯2 克马克 第2条军规 科尔康 阿门特

 

标签:

ROS2库包设置和使用Catch2进行单元测试由讯客互联其他栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“ROS2库包设置和使用Catch2进行单元测试