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,使得参数管理变得更加方便。

相关推荐
冬奇Lab17 小时前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
tingshuo291718 小时前
D006 【模板】并查集
笔记
BoomHe1 天前
Now in Android 架构模式全面分析
android·android jetpack
二流小码农1 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos
鹏程十八少1 天前
4.Android 30分钟手写一个简单版shadow, 从零理解shadow插件化零反射插件化原理
android·前端·面试
Kapaseker1 天前
一杯美式搞定 Kotlin 空安全
android·kotlin
三少爷的鞋1 天前
Android 协程时代,Handler 应该退休了吗?
android
tingshuo29172 天前
S001 【模板】从前缀函数到KMP应用 字符串匹配 字符串周期
笔记
火柴就是我2 天前
让我们实现一个更好看的内部阴影按钮
android·flutter
砖厂小工2 天前
用 GLM + OpenClaw 打造你的 AI PR Review Agent — 让龙虾帮你审代码
android·github