打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
ROS2中使用Gtes示例

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

一、准备工作

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

进入 dev_ws/src 路径下:

先创建依赖包 tutorial_interfaces:

ros2 pkg create --build-type ament_cmake tutorial_interfaces

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

mkdir msg

mkdir srv

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

int64 num

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

int64 a
int64 b
int64 c---int64 sum

然后编辑CMakeLists.txt文件:

find_package(rosidl_default_generators REQUIRED)rosidl_generate_interfaces(${PROJECT_NAME}
  "msg/Num.msg"
  "srv/AddThreeInts.srv"
 )

继续编辑package.xml:

<build_depend>rosidl_default_generators</build_depend><exec_depend>rosidl_default_runtime</exec_depend><member_of_group>rosidl_interface_packages</member_of_group>

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

colcon build --packages-select tutorial_interfaces

以上操作步骤详细链接:

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

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

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

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

#include "rclcpp/rclcpp.hpp"#include "tutorial_interfaces/srv/add_three_ints.hpp"                                        // CHANGE#include <memory>void add(const std::shared_ptr<tutorial_interfaces::srv::AddThreeInts::Request> request,     // CHANGE
          std::shared_ptr<tutorial_interfaces::srv::AddThreeInts::Response>       response)  // CHANGE{
  response->sum = request->a + request->b + request->c;                                      // CHANGE
  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld" " c: %ld",  // CHANGE
                request->a, request->b, request->c);                                         // CHANGE
  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%ld]", (long int)response->sum);}int main(int argc, char **argv){
  rclcpp::init(argc, argv);

  std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_three_ints_server");   // CHANGE

  rclcpp::Service<tutorial_interfaces::srv::AddThreeInts>::SharedPtr service =               // CHANGE
    node->create_service<tutorial_interfaces::srv::AddThreeInts>("add_three_ints",  &add);   // CHANGE

  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Ready to add three ints.");                     // CHANGE

  rclcpp::spin(node);
  rclcpp::shutdown();}

二、测试代码编写

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

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

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

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

// client.h#ifndef CLIENT_H#define CLIENT_Hclass ClientHandler{
    public:
        ClientHandler();
        ~ClientHandler();
        bool sendParams(int argc, char **argv);};#endif
// params.h#ifndef PARAMS_H#define PARAMS_Hextern int my_argc;extern char** my_argv;#endif

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

// client.cpp#include "rclcpp/rclcpp.hpp"#include "tutorial_interfaces/srv/add_three_ints.hpp"                                    #include "../include/client.h"#include <chrono>#include <cstdlib>#include <memory>#include<vector>using namespace std;using namespace std::chrono_literals;// 构造函数ClientHandler::ClientHandler(){}// 析构函数ClientHandler::~ClientHandler(){}// 普通函数——发送参数bool ClientHandler::sendParams(int argc, char **argv){
  rclcpp::init(argc, argv);

  if (argc != 4) {
      RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "usage: add_three_ints_client X Y Z");      
      return false;
  }

  std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_three_ints_client");  
  rclcpp::Client<tutorial_interfaces::srv::AddThreeInts>::SharedPtr client =               
    node->create_client<tutorial_interfaces::srv::AddThreeInts>("add_three_ints");         

  auto request = std::make_shared<tutorial_interfaces::srv::AddThreeInts::Request>();      
  request->a = atoll(argv[1]);
  request->b = atoll(argv[2]);
  request->c = atoll(argv[3]);                                                             

  while (!client->wait_for_service(1s)) {
    if (!rclcpp::ok()) {
      RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting.");
      return false;
    }
    RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again...");
  }

  auto result = client->async_send_request(request);
  // Wait for the result.
  if (rclcpp::spin_until_future_complete(node, result) ==
    rclcpp::FutureReturnCode::SUCCESS)
  {
    RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Sum: %ld", result.get()->sum);
  } else {
    RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Failed to call service add_three_ints");
  }

  rclcpp::shutdown();
  return true;}
// main.cpp#include "../include/client.h"int main(int argc, char **argv){
    // 注意这里: C++ 编译器把不带参数的构造函数优先认为是一个函数声明
    ClientHandler client{};
    client.sendParams(argc, argv);}

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

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

CMakeLists.txt文件:

cmake_minimum_required(VERSION 3.8)project(client)if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)endif()# find dependenciesfind_package(ament_cmake REQUIRED)find_package(rclcpp REQUIRED)find_package(tutorial_interfaces REQUIRED)set(SRC
  src/client.cpp
  src/main.cpp)add_executable(client 
              ${SRC}
              )ament_target_dependencies(client
  rclcpp tutorial_interfaces)# 5. 添加当前项目中的头文件 注意有顺序的要求,不能乱target_include_directories(client    PRIVATE 
    ${PROJECT_SOURCE_DIR}/include)# 如果是测试代码if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # 加入gtest包  find_package(ament_cmake_gtest 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)
  set(TEST
      test/main.cpp
      test/clientTest.cpp    )
  # 生成加入gtest的test执行文件。${PROJECT_NAME}_test为自定义的test执行文件名称;test/demo_test.cpp为test源码路径
  # 注意这里导包的时候,不再需要将 .h 文件导入进来,因为在 client.cpp中已经导入了我们需要使用到的.h文件
  # 另外,注意这里不能导入开发代码中的 main.cpp,因为已经有了一个测试的main.cpp  ament_add_gtest(${PROJECT_NAME}_test ${TEST} src/client.cpp)
  # 务必注意这里需要添加的依赖包  ament_target_dependencies(${PROJECT_NAME}_test rclcpp tutorial_interfaces)

  install(TARGETS
          ${PROJECT_NAME}_test
          # 将生成的test执行文件安装到DESTINATION后的路径下          DESTINATION lib/${PROJECT_NAME})                                       
  ament_lint_auto_find_test_dependencies()endif()install(TARGETS
  client  DESTINATION lib/${PROJECT_NAME})# 设置编译构建类型为 调试 模式set(CMAKE_BUILD_TYPE Debug)   # 生成覆盖率文件set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage")set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage")ament_package()

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>client</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="zhi@todo.todo">zhi</maintainer>
  <license>TODO: License declaration</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <depend>rclcpp</depend>
  <depend>tutorial_interfaces</depend>

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export></package>

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

colcon build --packages-select client

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

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

ros2 run cpp_srvcli server

执行

./client_test 23 3 4

运行测试文件即可。

生成覆盖率的脚本:

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

程序封装改写:

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

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

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
ROS2学习笔记(三)——colcon编译并编写自己的包_ros2 编译自己的小包
ros svr 机制
​X3派+大疆无人机-SLAM单目建图.ROS包制作
科学网
ros 消息发布和订阅
ROS学习(四)发布者与订阅者
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服