WHY_Blog

why的小窝

0%

ROS2-service

ROS2 服务


是什么?

ROS2平台上的一种 通信机制 ,
组成: 生产端(producer)消费端(consumer)
作用: 消费端(consumer) 提出 请求(request) , 生产端(producer) 返回 回复(response)
特点在于消费端


代码快速开发

代码逻辑

创建生产端对象 -> 创建服务 -> 编写服务回调函数(收到 请求(request) 做什么,返回怎么样的 回复(response))

创建消费端对象 -> 关联服务 -> 编写 请求(request) (根据消息格式定义 请求(request) 内容) -> 编写服务端返回结果回调函数(收到 回复(response),做什么)

代码框架

  1. 生产端(producer)

    1. 创建生产端

      1
      2
      3
      4
      5
      /* srv1.创建生产端服务 */
      srv_producer_temp_ = this->create_service<SrvInterface>(
      "srv_temp",
      std::bind(&SrvProducerTemp::srv_producer_temp_callback, this, std::placeholders::_1, std::placeholders::_2));
      // 指定服务名称,(未知参数),回调函数
    2. 编写服务回调函数

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      /* srv2.服务回调函数 */
      void srv_producer_temp_callback(const std::shared_ptr<SrvInterface::Request> request, const std::shared_ptr<SrvInterface::Response> response)
      {
      /* srv2.1 处理服务端响应 */
      int8_t length = request->test_request_data;
      RCLCPP_INFO(this->get_logger(), "SrvProducerTemp received request with length %d", length);
      /* srv2.2 返回响应结果 */
      response->test_response_data = SrvInterface::Response::SUCCESS; // 调用常量,返回响应结果

      // 数组操作示例/参数调用示例
      if (param_name2_ > 0 && length > param_name2_)
      {
      response->test_response_data = SrvInterface::Response::SUCCESS;

      response->test_array_data.resize(length); // 确保数组有足够的空间
      for (int i = 0; i < param_name2_; i++)
      {
      response->test_array_data[i] = i;
      }
      }
      else
      {
      response->test_response_data = SrvInterface::Response::FAILURE;
      }
      }
    3. *声明和获取参数

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      /* srv3.声明和获取参数*/
      this->declare_parameter("param_name1", 1.0);
      this->declare_parameter("param_name2", 1);

      this->get_parameter("param_name1", param_name1_);
      this->get_parameter("param_name2", param_name2_);

      // 添加参数设置回调
      parameters_callback_handle_ = this->add_on_set_parameters_callback(
      [&](const std::vector<rclcpp::Parameter> &params)
      -> rcl_interfaces::msg::SetParametersResult
      {
      // 遍历参数
      for (auto param : params)
      {
      if (param.get_name() == "param_name1")
      {
      RCLCPP_INFO(this->get_logger(), "更新参数 %s 值为:%f", param.get_name().c_str(), param.as_double());
      param_name1_ = param.as_double();
      }
      else if (param.get_name() == "param_name2")
      {
      RCLCPP_INFO(this->get_logger(), "更新参数 %s 值为:%ld", param.get_name().c_str(), param.as_int());
      param_name2_ = param.as_int();
      }
      }
      auto result = rcl_interfaces::msg::SetParametersResult();
      result.successful = true;
      return result;
      });
  2. 消费端(consumer)代码框架

    1. 创建消费端(consumer)

      1
      2
      srv_consumer_temp_ = this->create_client<SrvInterface>("srv_temp"); // 请求服务的名称

    2. 编写发送请求逻辑

      1. 等待服务端上线

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        /* cli2.1等待服务端上线 */
        while (!srv_consumer_temp_->wait_for_service(1s))
        {
        // 等待时检测rclcpp的状态
        if (!rclcpp::ok())
        {
        RCLCPP_ERROR(this->get_logger(), "等待服务的过程中被打断...");
        return;
        }
        RCLCPP_INFO(this->get_logger(), "等待服务端上线中");
        }
      2. 构造请求

        1
        2
        3
        /* cli2.2构造请求*/
        auto request = std::make_shared<SrvInterface::Request>();
        request->test_request_data = 10;
      3. 发送请求

        1
        2
        3
        /* cli2.3发送异步请求,然后等待返回,返回时调用回调函数*/
        // 回调函数见 SrvConsumerTemp::srv_consumer_temp_callback()
        srv_consumer_temp_->async_send_request(request, std::bind(&SrvConsumerTemp::srv_consumer_temp_callback, this, std::placeholders::_1));
      4. 编写服务端返回结果回调函数

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        /* 服务端返回结果回调函数 */
        void srv_consumer_temp_callback(std::shared_future<typename SrvInterface::Response::SharedPtr> result_future)
        {
        /* cli2.4获取和处理服务端返回的结果 */
        auto response = result_future.get();

        /* cli2.4处理服务端返回的结果 */
        if (response->test_response_data == SrvInterface::Response::SUCCESS)
        {
        RCLCPP_INFO(this->get_logger(), "处理成功");
        }
        else if (response->test_response_data == SrvInterface::Response::FAILURE)
        {
        RCLCPP_INFO(this->get_logger(), "处理失败");
        }
        }
    3. *编写参数修改请求函数(服务请求另一种实现方式)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      /* 参数修改请求函数*/
      void param_modify_request(int new_value)
      {
      /* par1.创建参数修改请求 */
      auto param = rcl_interfaces::msg::Parameter();
      param.name = "param_name2"; // 参数名称

      auto param_value = rcl_interfaces::msg::ParameterValue(); // 参数值对象
      param_value.type = rclcpp::ParameterType::PARAMETER_INTEGER; // 参数类型
      param_value.integer_value = new_value; // 参数值
      param.value = param_value;
      /* par2.发送请求函数 */
      // 见 SrvConsumerTemp::param_modify_request_send()
      auto response = param_modify_request_send(param);
      /* par3.处理返回结果 */
      if (response == nullptr)
      {
      RCLCPP_WARN(this->get_logger(), "参数修改失败");
      return;
      }
      else
      {
      for (auto result : response->results)
      {
      if (result.successful)
      {
      RCLCPP_INFO(this->get_logger(), "参数 已修改为:%d", new_value);
      }
      else
      {
      RCLCPP_WARN(this->get_logger(), "参数 修改失败, 失败原因:%s", result.reason.c_str());
      }
      }
      }
      };

      /* 参数修改请求发送函数 */
      std::shared_ptr<rcl_interfaces::srv::SetParameters::Response> param_modify_request_send(rcl_interfaces::msg::Parameter param)
      {
      /* par2.1创建客户端 */
      auto param_client = this->create_client<rcl_interfaces::srv::SetParameters>(
      "/srv_producer_temp_node/set_parameters");
      /* par2.2等待服务端上线 */
      while (!param_client->wait_for_service(1s))
      {
      // 等待时检测rclcpp的状态
      if (!rclcpp::ok())
      {
      RCLCPP_ERROR(this->get_logger(), "等待服务的过程中被打断...");
      return nullptr;
      }
      RCLCPP_INFO(this->get_logger(), "等待参数修改服务端上线中");
      }
      /* par2.3构造请求 */
      auto request = std::make_shared<rcl_interfaces::srv::SetParameters::Request>();
      request->parameters.push_back(param);
      /* par2.4发送请求 */
      auto response_future = param_client->async_send_request(request);
      /* par2.5等待返回结果 */
      rclcpp::spin_until_future_complete(this->get_node_base_interface(), response_future);
      /* par2.6获取返回结果 */
      auto response = response_future.get();
      return response;
      }