ROS2笔记之服务通信和基于参数的服务通信区别

ROS2笔记之服务通信和基于参数的服务通信区别

code review!

参考书籍

------ 《ROS2机器人开发从入门到实践》Page 92

文章目录

1.服务通信和基于参数的服务通信概述

  • 服务通信

    用于同步的请求-响应交互,客户端发送请求,服务端处理后返回响应,适合需要即时反馈的操作。例如,触发一个动作、获取传感器结果等。

  • 参数通信

    用于配置和动态更新节点的内部状态,不存在请求-响应的交互。参数的更新通常通过外部工具(如 ros2 param set)或者节点内部更新来完成,适合调节节点行为、控制阈值等场景。

2.主要区别

特性 服务通信 基于参数的通信
交互模式 请求-响应模式 获取/设置模式
使用场景 执行计算、触发行为、查询状态 配置管理、状态存储
通信方向 双向通信(客户端→服务端→客户端) 读取/写入参数服务器
实时性 同步调用,等待响应 随时可访问,可设置变更回调
数据复杂性 支持复杂的自定义消息类型 支持基本类型和数组
生命周期 服务随节点创建和销毁 参数可持久化,超出节点生命周期

3.服务通信模式

服务通信是一种请求-响应的通信模式,适用于需要双向交互的场景。

3.1 服务端实现

cpp 复制代码
#include "rclcpp/rclcpp.hpp"
#include "std_srvs/srv/trigger.hpp"

using Trigger = std_srvs::srv::Trigger;

class ServiceServer : public rclcpp::Node
{
public:
  ServiceServer() : Node("service_server")
  {
    service_ = this->create_service<Trigger>(
      "trigger_service",
      std::bind(&ServiceServer::handle_service, this, std::placeholders::_1, std::placeholders::_2)
    );
    RCLCPP_INFO(this->get_logger(), "Service server started.");
  }

private:
  void handle_service(
    const std::shared_ptr<Trigger::Request> request,
    std::shared_ptr<Trigger::Response> response)
  {
    (void)request;  // 未使用请求内容
    response->success = true;
    response->message = "Service executed successfully";
    RCLCPP_INFO(this->get_logger(), "Service request handled.");
  }

  rclcpp::Service<Trigger>::SharedPtr service_;
};

int main(int argc, char ** argv)
{
  rclcpp::init(argc, argv);
  auto node = std::make_shared<ServiceServer>();
  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}

3.2 客户端实现

cpp 复制代码
#include "rclcpp/rclcpp.hpp"
#include "std_srvs/srv/trigger.hpp"

using Trigger = std_srvs::srv::Trigger;

int main(int argc, char ** argv)
{
  rclcpp::init(argc, argv);
  auto node = rclcpp::Node::make_shared("service_client");
  auto client = node->create_client<Trigger>("trigger_service");

  while (!client->wait_for_service(std::chrono::seconds(1))) {
    RCLCPP_INFO(node->get_logger(), "Waiting for service to be ready...");
  }

  auto request = std::make_shared<Trigger::Request>();
  auto future_result = client->async_send_request(request);

  if (rclcpp::spin_until_future_complete(node, future_result) ==
      rclcpp::FutureReturnCode::SUCCESS)
  {
    auto response = future_result.get();
    RCLCPP_INFO(node->get_logger(), "Response: %s", response->message.c_str());
  }
  else {
    RCLCPP_ERROR(node->get_logger(), "Failed to call service");
  }

  rclcpp::shutdown();
  return 0;
}

3.3 实用示例:加法服务

3.3.1 服务端
cpp 复制代码
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"

class AddServer : public rclcpp::Node {
public:
  AddServer() : Node("add_server") {
    server_ = create_service<example_interfaces::srv::AddTwoInts>(
      "add_two_ints", std::bind(&AddServer::add, this, std::placeholders::_1, std::placeholders::_2));
  }

private:
  void add(const example_interfaces::srv::AddTwoInts::Request::SharedPtr request,
          example_interfaces::srv::AddTwoInts::Response::SharedPtr response) {
    response->sum = request->a + request->b;
    RCLCPP_INFO(get_logger(), "收到请求: %d + %d = %d", request->a, request->b, response->sum);
  }
  
  rclcpp::Service<example_interfaces::srv::AddTwoInts>::SharedPtr server_;
};
3.3.2 客户端
cpp 复制代码
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"

class AddClient : public rclcpp::Node {
public:
  AddClient() : Node("add_client") {
    client_ = create_client<example_interfaces::srv::AddTwoInts>("add_two_ints");
    callService(3, 5);
  }

private:
  void callService(int a, int b) {
    auto request = std::make_shared<example_interfaces::srv::AddTwoInts::Request>();
    request->a = a;
    request->b = b;
    
    auto future = client_->async_send_request(request);
    auto result = future.get(); // 等待响应
    RCLCPP_INFO(get_logger(), "结果: %d", result->sum);
  }
  
  rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedPtr client_;
};

4.参数通信模式

参数通信用于节点配置和状态共享,适合需要动态调整节点行为的场景。

4.1 参数监听与更新

cpp 复制代码
#include "rclcpp/rclcpp.hpp"

class ParamNode : public rclcpp::Node
{
public:
  ParamNode() : Node("parameter_node")
  {
    // 声明参数,初始值为 10
    this->declare_parameter<int>("my_param", 10);
    // 每秒钟读取一次参数值
    timer_ = this->create_wall_timer(
      std::chrono::seconds(1),
      std::bind(&ParamNode::timer_callback, this)
    );
    RCLCPP_INFO(this->get_logger(), "Parameter node started.");
  }

private:
  void timer_callback()
  {
    int param_value = this->get_parameter("my_param").as_int();
    RCLCPP_INFO(this->get_logger(), "Current parameter value: %d", param_value);
  }

  rclcpp::TimerBase::SharedPtr timer_;
};

int main(int argc, char ** argv)
{
  rclcpp::init(argc, argv);
  auto node = std::make_shared<ParamNode>();
  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}

4.2 高级参数处理

cpp 复制代码
#include "rclcpp/rclcpp.hpp"

class ParamNode : public rclcpp::Node {
public:
  ParamNode() : Node("param_node") {
    // 声明参数
    declare_parameter("max_speed", 50.0);
    declare_parameter("robot_name", "robot1");
    
    // 获取参数
    double max_speed = get_parameter("max_speed").as_double();
    std::string name = get_parameter("robot_name").as_string();
    RCLCPP_INFO(get_logger(), "参数值: max_speed=%f, name=%s", max_speed, name.c_str());
    
    // 设置参数
    auto result = set_parameter(rclcpp::Parameter("max_speed", 75.0));
    if (result.successful) {
      RCLCPP_INFO(get_logger(), "参数已更新");
    }
    
    // 参数变化回调
    param_callback_ = add_on_set_parameters_callback(
      std::bind(&ParamNode::paramCallback, this, std::placeholders::_1));
  }

private:
  rcl_interfaces::msg::SetParametersResult paramCallback(
    const std::vector<rclcpp::Parameter> &parameters) {
    rcl_interfaces::msg::SetParametersResult result;
    result.successful = true;
    
    for (const auto &param : parameters) {
      RCLCPP_INFO(get_logger(), "参数'%s'变更为: %s", 
                  param.get_name().c_str(), param.value_to_string().c_str());
    }
    return result;
  }
  
  OnSetParametersCallbackHandle::SharedPtr param_callback_;
};

5.命令行使用对比

5.1 服务通信

bash 复制代码
# 列出可用的服务
ros2 service list

# 查看服务接口类型
ros2 service type /add_integers

# 调用服务
ros2 service call /add_integers example_interfaces/srv/AddTwoInts "{a: 5, b: 10}"

5.2 参数通信

bash 复制代码
# 列出所有参数
ros2 param list

# 获取参数值
ros2 param get /parameter_node max_velocity

# 设置参数值
ros2 param set /parameter_node max_velocity 5.0

# 保存参数到文件
ros2 param dump /parameter_node

# 从文件加载参数
ros2 param load /parameter_node params.yaml

6.两种通信模式的比较

6.1 服务通信特点

  • 同步通信:提供请求-响应模式
  • 适用场景:需要确认操作完成后才能继续的任务,如计算服务
  • 使用方式:创建服务器和客户端,客户端发送请求并等待响应

6.2 参数通信特点

  • 配置管理:主要用于节点配置和状态共享
  • 动态调整:允许运行时更改节点行为
  • 命令行支持 :可以通过ROS2命令行工具直接修改(如ros2 param set
  • 参数回调:可以监听参数变化并触发相应行为

6.3 选择指南

  • 对于需要节点间直接交互的场景,使用服务通信
  • 对于配置和状态管理,使用参数通信
  • 参数通信更适合用户或操作者动态调整系统行为

参数系统实际上在底层也使用了服务通信机制,但对用户提供了更简单的API,使得参数管理变得更加方便。

相关推荐
su_ym81104 分钟前
Android 与 Linux 对比
android·linux·framework
玄米乌龙茶1235 分钟前
Web Search API 实操:构建智能搜索 Agent
人工智能·笔记·学习
默|笙6 分钟前
【Linux】线程同步与互斥_日志与线程池
android·linux·运维
fengci.7 分钟前
蜀道山2024上半部分
android
XS0301067 分钟前
Java基础笔记(一)
java·笔记·python
jrlong36 分钟前
HelloAgents 进阶篇 task02
笔记·学习
XS03010636 分钟前
Java 基础笔记(二)
java·笔记·python
撩得Android一次心动37 分钟前
Android DataBinding 全面解析【源码篇1】
android·android jetpack·databinding
aq553560041 分钟前
Laravel 1.x:现代PHP框架的雏形
android
智者知已应修善业43 分钟前
【51单片机数码管+蜂鸣器的使用】2023-6-14
c++·经验分享·笔记·算法·51单片机