博客列表 >ROS2中使用Gtes示例

ROS2中使用Gtes示例

哈
原创
2021年11月12日 16:38:391217浏览

阿里云官方镜像站:ROS2
https://developer.aliyun.com/mirror/?utm_content=g_1000303593

一、准备工作

创建工作空间,即编写代码的位置mkdir -p dev_ws/src

进入 dev_ws/src 路径下:

先创建依赖包 tutorial_interfaces:

  1. ros2 pkg create --build-type ament_cmake tutorial_interfaces

进入 dev_ws/src/tutorial_interfaces ,然后创建msg和srv目录存放.msg文件和.srv文件:

  1. mkdir msg
  2. mkdir srv

进入dev_ws/src/tutorial_interface/msg目录,新建 Num.msg文件:

  1. int64 num

进入 dev_ws/src/tutorial_interface/srv目录,新建 AddThreeInts.srv文件:

  1. int64 a
  2. int64 b
  3. int64 c
  4. ---
  5. int64 sum

然后编辑CMakeLists.txt文件:

  1. find_package(rosidl_default_generators REQUIRED)
  2. rosidl_generate_interfaces(${PROJECT_NAME}
  3. "msg/Num.msg"
  4. "srv/AddThreeInts.srv"
  5. )

继续编辑package.xml:

  1. <build_depend>rosidl_default_generators</build_depend>
  2. <exec_depend>rosidl_default_runtime</exec_depend>
  3. <member_of_group>rosidl_interface_packages</member_of_group>

最后回到工作空间路径下编译构建 tutorial_interfaces 包:

  1. colcon build --packages-select tutorial_interfaces

以上操作步骤详细链接:

Creating custom ROS 2 msg and srv files — ROS 2 Documentation: Galactic documentation

然后在工作空间的src路径下创建自己的包 service,并指定依赖的包:

  1. ros2 pkg create --build-type ament_cmake service --dependencies rclcpp tutorial_interfaces

在 dev_ws/src/service/src目录下新建service.cpp

  1. #include "rclcpp/rclcpp.hpp"
  2. #include "tutorial_interfaces/srv/add_three_ints.hpp" // CHANGE
  3. #include <memory>
  4. void add(const std::shared_ptr<tutorial_interfaces::srv::AddThreeInts::Request> request, // CHANGE
  5. std::shared_ptr<tutorial_interfaces::srv::AddThreeInts::Response> response) // CHANGE
  6. {
  7. response->sum = request->a + request->b + request->c; // CHANGE
  8. RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld" " c: %ld", // CHANGE
  9. request->a, request->b, request->c); // CHANGE
  10. RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%ld]", (long int)response->sum);
  11. }
  12. int main(int argc, char **argv)
  13. {
  14. rclcpp::init(argc, argv);
  15. std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_three_ints_server"); // CHANGE
  16. rclcpp::Service<tutorial_interfaces::srv::AddThreeInts>::SharedPtr service = // CHANGE
  17. node->create_service<tutorial_interfaces::srv::AddThreeInts>("add_three_ints", &add); // CHANGE
  18. RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Ready to add three ints."); // CHANGE
  19. rclcpp::spin(node);
  20. rclcpp::shutdown();
  21. }

二、测试代码编写

进入工作空间的src路径下,执行下面命令生成待测模块client:

  1. ros2 pkg create --build-type ament_cmake client --dependencies rclcpp tutorial_interfaces

进入client路径下,分别新建src,test目录,include目录默认已经存在。

include目录下文件:client.h和params.h

  1. // client.h
  2. #ifndef CLIENT_H
  3. #define CLIENT_H
  4. class ClientHandler
  5. {
  6. public:
  7. ClientHandler();
  8. ~ClientHandler();
  9. bool sendParams(int argc, char **argv);
  10. };
  11. #endif
  1. // params.h
  2. #ifndef PARAMS_H
  3. #define PARAMS_H
  4. extern int my_argc;
  5. extern char** my_argv;
  6. #endif

src目录下文件:client.cpp和main.cpp

  1. // client.cpp
  2. #include "rclcpp/rclcpp.hpp"
  3. #include "tutorial_interfaces/srv/add_three_ints.hpp"
  4. #include "../include/client.h"
  5. #include <chrono>
  6. #include <cstdlib>
  7. #include <memory>
  8. #include<vector>
  9. using namespace std;
  10. using namespace std::chrono_literals;
  11. // 构造函数
  12. ClientHandler::ClientHandler(){
  13. }
  14. // 析构函数
  15. ClientHandler::~ClientHandler(){
  16. }
  17. // 普通函数——发送参数
  18. bool ClientHandler::sendParams(int argc, char **argv)
  19. {
  20. rclcpp::init(argc, argv);
  21. if (argc != 4) {
  22. RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "usage: add_three_ints_client X Y Z");
  23. return false;
  24. }
  25. std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_three_ints_client");
  26. rclcpp::Client<tutorial_interfaces::srv::AddThreeInts>::SharedPtr client =
  27. node->create_client<tutorial_interfaces::srv::AddThreeInts>("add_three_ints");
  28. auto request = std::make_shared<tutorial_interfaces::srv::AddThreeInts::Request>();
  29. request->a = atoll(argv[1]);
  30. request->b = atoll(argv[2]);
  31. request->c = atoll(argv[3]);
  32. while (!client->wait_for_service(1s)) {
  33. if (!rclcpp::ok()) {
  34. RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting.");
  35. return false;
  36. }
  37. RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again...");
  38. }
  39. auto result = client->async_send_request(request);
  40. // Wait for the result.
  41. if (rclcpp::spin_until_future_complete(node, result) ==
  42. rclcpp::FutureReturnCode::SUCCESS)
  43. {
  44. RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Sum: %ld", result.get()->sum);
  45. } else {
  46. RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Failed to call service add_three_ints");
  47. }
  48. rclcpp::shutdown();
  49. return true;
  50. }
  1. // main.cpp
  2. #include "../include/client.h"
  3. int main(int argc, char **argv){
  4. // 注意这里: C++ 编译器把不带参数的构造函数优先认为是一个函数声明
  5. ClientHandler client{};
  6. client.sendParams(argc, argv);
  7. }

test目录下文件:clientTest.cpp和main.cpp

  1. // clientTest.cpp
  2. #include "gtest/gtest.h"
  3. #include "../include/client.h"
  4. #include "../include/params.h"
  5. TEST(ClientHandler, sendParams)
  6. {
  7. // 测试的时候的交互方式也不能改变,既然client实际的效果是在命令行输入参数,
  8. // 那这里也是这样的效果
  9. ClientHandler client{};
  10. EXPECT_EQ(true, client.sendParams(my_argc, my_argv));
  11. }
  1. // main.cpp
  2. #include <gtest/gtest.h>
  3. // #include <gmock/gmock.h>
  4. int my_argc;
  5. char** my_argv;
  6. int main(int argc, char** argv) {
  7. // ::testing::InitGoogleMock(&argc, argv);
  8. // 注意这里使用的是Gtest,不是Gmock
  9. ::testing::InitGoogleTest(&argc, argv);
  10. // Runs all tests using Google Test.
  11. my_argc = argc;
  12. my_argv = argv;
  13. return RUN_ALL_TESTS();
  14. }

CMakeLists.txt文件:

  1. cmake_minimum_required(VERSION 3.8)
  2. project(client)
  3. if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  4. add_compile_options(-Wall -Wextra -Wpedantic)
  5. endif()
  6. # find dependencies
  7. find_package(ament_cmake REQUIRED)
  8. find_package(rclcpp REQUIRED)
  9. find_package(tutorial_interfaces REQUIRED)
  10. set(SRC
  11. src/client.cpp
  12. src/main.cpp
  13. )
  14. add_executable(client
  15. ${SRC}
  16. )
  17. ament_target_dependencies(client
  18. rclcpp tutorial_interfaces)
  19. # 5. 添加当前项目中的头文件 注意有顺序的要求,不能乱
  20. target_include_directories(client
  21. PRIVATE
  22. ${PROJECT_SOURCE_DIR}/include
  23. )
  24. # 如果是测试代码
  25. if(BUILD_TESTING)
  26. find_package(ament_lint_auto REQUIRED)
  27. # 加入gtest包
  28. find_package(ament_cmake_gtest REQUIRED)
  29. # the following line skips the linter which checks for copyrights
  30. # uncomment the line when a copyright and license is not present in all source files
  31. # set(ament_cmake_copyright_FOUND TRUE)
  32. # the following line skips cpplint (only works in a git repo)
  33. # uncomment the line when this package is not in a git repo
  34. # set(ament_cmake_cpplint_FOUND TRUE)
  35. set(TEST
  36. test/main.cpp
  37. test/clientTest.cpp
  38. )
  39. # 生成加入gtest的test执行文件。${PROJECT_NAME}_test为自定义的test执行文件名称;test/demo_test.cpp为test源码路径
  40. # 注意这里导包的时候,不再需要将 .h 文件导入进来,因为在 client.cpp中已经导入了我们需要使用到的.h文件
  41. # 另外,注意这里不能导入开发代码中的 main.cpp,因为已经有了一个测试的main.cpp
  42. ament_add_gtest(${PROJECT_NAME}_test ${TEST} src/client.cpp)
  43. # 务必注意这里需要添加的依赖包
  44. ament_target_dependencies(${PROJECT_NAME}_test rclcpp tutorial_interfaces)
  45. install(TARGETS
  46. ${PROJECT_NAME}_test
  47. # 将生成的test执行文件安装到DESTINATION后的路径下
  48. DESTINATION lib/${PROJECT_NAME})
  49. ament_lint_auto_find_test_dependencies()
  50. endif()
  51. install(TARGETS
  52. client
  53. DESTINATION lib/${PROJECT_NAME})
  54. # 设置编译构建类型为 调试 模式
  55. set(CMAKE_BUILD_TYPE Debug)
  56. # 生成覆盖率文件
  57. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage")
  58. set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage")
  59. ament_package()

package.xml文件:

  1. <?xml version="1.0"?>
  2. <?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
  3. <package format="3">
  4. <name>client</name>
  5. <version>0.0.0</version>
  6. <description>TODO: Package description</description>
  7. <maintainer email="zhi@todo.todo">zhi</maintainer>
  8. <license>TODO: License declaration</license>
  9. <buildtool_depend>ament_cmake</buildtool_depend>
  10. <depend>rclcpp</depend>
  11. <depend>tutorial_interfaces</depend>
  12. <test_depend>ament_lint_auto</test_depend>
  13. <test_depend>ament_lint_common</test_depend>
  14. <export>
  15. <build_type>ament_cmake</build_type>
  16. </export>
  17. </package>

然后回到工作空间执行编译构建命令:

  1. colcon build --packages-select client

进入到 dev_ws/build/client路径下,找到client_test可执行文件即为生成的测试文件

在另外一个终端启动service包:

  1. ros2 run cpp_srvcli server

执行

  1. ./client_test 23 3 4

运行测试文件即可。

生成覆盖率的脚本:

  1. #!/usr/bin/bash
  2. echo "begin gen coverage file ..."
  3. lcov --no-external --capture --initial --directory . --output-file /home/zhi/ros2-gtest-gmock/info/ros2_base.info
  4. cd /home/zhi/ros2-gtest-gmock/build/client;./client_test 45 56 56;cd /home/zhi/ros2-gtest-gmock
  5. # 注意下面的=号两侧是不可以有空格的,这是个大坑
  6. current_path=$(pwd)
  7. echo "当前目录是:" $current_path
  8. lcov --no-external --capture --directory . --output-file /home/zhi/ros2-gtest-gmock/info/ros2.info
  9. lcov --add-tracefile /home/zhi/ros2-gtest-gmock/info/ros2_base.info --add-tracefile /home/zhi/ros2-gtest-gmock/info/ros2.info --output-file /home/zhi/ros2-gtest-gmock/info/ros2_coverage.info
  10. mkdir -p coverage && genhtml /home/zhi/ros2-gtest-gmock/info/ros2_coverage.info --output-directory coverage

程序封装改写:

如果不知道返回类型,直接断点下去看下类型:

本文转自:https://www.cnblogs.com/huaibin/p/15423963.html

声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议