• ROS2从入门到精通1-2:详解ROS2服务通信机制与自定义服务


    0 专栏介绍

    本专栏旨在通过对ROS2的系统学习,掌握ROS2底层基本分布式原理,并具有机器人建模和应用ROS2进行实际项目的开发和调试的工程能力。

    🚀详情:《ROS2从入门到精通》


    1 服务通信模型

    服务是 ROS 图中节点之间的另一种通信方法。服务基于服务器-客户端模型,不同于话题的发布者-订阅者模型。话题允许节点订阅数据流并获取持续更新,而服务只在客户端特别调用时才提供数据。二者更详细的对比请参考第5节

    在这里插入图片描述

    在这里插入图片描述

    2 服务模型实现(C++)

    实验目标:客户端提交请求给turtlesim功能包的/spawn服务,在界面上生成新的乌龟。

    • 服务器

      本实验中无需编程,为turtlesim::Spawn定义的/spwan服务

    • 客户端

      void OnResultCallBack(rclcpp::Client<turtlesim::srv::Spawn>::SharedFuture result) {
          auto response = result.get();
          RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Request service successfully! [turtle id: %s]", response->name.c_str());
      }
      
      void request() {
          auto spawn = std::make_shared<turtlesim::srv::Spawn::Request>();          
          spawn->name = "winter_turtle";
          spawn->x = 1.0;
          spawn->y = 1.0;
          spawn->theta = 1.57;
      
          while (!client_->wait_for_service(std::chrono::seconds(1))) {                                                   
              if (!rclcpp::ok()) {
                  RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting.");
                  return;
              }
              RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again...");
          }
      
          auto result = client_->async_send_request(spawn, std::bind(&ClientNode::OnResultCallBack, this, std::placeholders::_1));
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22

    服务通信的效果如下所示:

    在这里插入图片描述

    3 服务模型实现(Python)

    实验目标:客户端提交请求给turtlesim功能包的/spawn服务,在界面上生成新的乌龟。

    • 服务器

      本实验中无需编程,为turtlesim::Spawn定义的/spwan服务

    • 客户端

      class ClientNode(Node):
          def __init__(self, name):
              super().__init__(name)
              self.client = self.create_client(Spawn, '/spawn') 
      
              while not self.client.wait_for_service(timeout_sec=1.0):
                  self.get_logger().info('service not available, waiting again...') 
              self.request = Spawn.Request()
                          
          def sendRequest(self):
              self.request.name = "winter_turtle"
              self.request.x = 1.0
              self.request.y = 1.0
              self.request.theta = 1.57
              self.future = self.client.call_async(self.request)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15

    服务通信的效果如下所示:

    在这里插入图片描述

    4 自定义服务

    自定义服务的通用流程如下:

    • 功能包下新建srv文件夹,在其中添加自定义服务xxx.srv,注意请求和响应数据结构使用---分割
    • 功能包package.xml中添加编译依赖与执行依赖
      <buildtool_depend>rosidl_default_generatorsbuildtool_depend>
      <exec_depend>rosidl_default_runtimeexec_depend>
      <member_of_group>rosidl_interface_packagesmember_of_group>
      
      • 1
      • 2
      • 3
    • 功能包CMakeLists.txt中添加编译消息相关依赖
      find_package(rosidl_default_generators REQUIRED)
      rosidl_generate_interfaces(${PROJECT_NAME}
      	"xxx.srv"
      	DEPENDENCIES xxx_srvs
      )
      
      ament_export_dependencies(rosidl_default_runtime)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    • 编译自定义消息,在install//include中生成由xxx.srv编译的C++可识别的xxx.hpp头文件
    • 引入xxx.hpp即可调用自定义服务

    下面给出一个实例

    添加如下自定义服务实现一个加法服务,并按上面步骤配置依赖

    # client
    int32 a
    int32 b
    ---
    # server
    int32 sum
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    定义一个服务器、一个客户端,限于篇幅只贴出部分代码,完整代码见文末。

    • 服务器
      class ServerNode : public rclcpp::Node
      {
          public:
              ServerNode() : Node("lab_srv_server_own") {
                  server_ = create_service<own_srv_lab::srv::Add>(
                      "/add_service",
                      std::bind(&ServerNode::OnAddSrvCallBack, this, std::placeholders::_1, std::placeholders::_2)
                  ); 
              }
      
          private:
              rclcpp::Service<own_srv_lab::srv::Add>::SharedPtr server_;
      
              void OnAddSrvCallBack(
                  const std::shared_ptr<own_srv_lab::srv::Add::Request> request, 
                  std::shared_ptr<own_srv_lab::srv::Add::Response> response
              ) {
                  response->sum = request->a + request->b;
                  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %d" " b: %d", request->a, request->b);
                  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%d]", response->sum);
              }
      };
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
    • 客户端
      ClientNode() : Node("lab_srv_client_own") {
          client_ = create_client<own_srv_lab::srv::Add>("/add_service"); 
      }
      
      void request(int a, int b) {
          auto add_srv = std::make_shared<own_srv_lab::srv::Add::Request>();
          add_srv->a = a;          
          add_srv->b = b;
      
          while (!client_->wait_for_service(std::chrono::seconds(1))) {                                                   
              if (!rclcpp::ok()) {
                  RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting.");
                  return;
              }
              RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again...");
          }
      
          auto result = client_->async_send_request(add_srv, std::bind(&ClientNode::OnResultCallBack, this, std::placeholders::_1));
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19

    服务通信效果如下所示:

    在这里插入图片描述

    5 话题、服务通信的异同

    对比话题服务
    通信模式发布-订阅请求-响应
    同步性异步同步
    缓冲区
    实时性
    节点关系多对多一对多(1个server对应一个服务)
    通信格式.msg.srv
    使用场景连续高频的数据传输,例如激光雷达、里程计传输数据偶尔调用的功能,例如图像识别

    完整代码通过下方博主名片联系获取


    🔥 更多精彩专栏


    👇源码获取 · 技术交流 · 抱团学习 · 咨询分享 请联系👇
  • 相关阅读:
    回炉重造,温故知新__css常规布局方法梳理__开发实战后的经验之谈
    这份华为以太网接口配置命令太真香了
    天猫超市电商营销系统:无代码开发实现API连接集成
    【第八篇】商城系统-库存管理
    码农跃迁三角色:程序员、技术主管与架构师
    Vue中的计算属性、方法和侦听属性的区别入门版
    测试开发基础 | Python 算法与数据结构面试题系列一(附答案)
    特征缩放和转换以及自定义Transformers(Machine Learning 研习之九)
    Vue笔记(三)
    【无标题】
  • 原文地址:https://blog.csdn.net/FRIGIDWINTER/article/details/136518450