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

相关推荐
能来帮帮蒟蒻吗7 分钟前
GO语言学习(16)Gin后端框架
开发语言·笔记·学习·golang·gin
无知的前端11 分钟前
Flutter 一文精通Isolate,使用场景以及示例
android·flutter·性能优化
_一条咸鱼_15 分钟前
Android Compose 入门之字符串与本地化深入剖析(五十三)
android
chushiyunen1 小时前
dom操作笔记、xml和document等
xml·java·笔记
chushiyunen1 小时前
tomcat使用笔记、启动失败但是未打印日志
java·笔记·tomcat
汇能感知1 小时前
光谱相机的光谱数据采集原理
经验分享·笔记·科技
ModestCoder_1 小时前
将一个新的机器人模型导入最新版isaacLab进行训练(以unitree H1_2为例)
android·java·机器人
人人题1 小时前
汽车加气站操作工考试答题模板
笔记·职场和发展·微信小程序·汽车·创业创新·学习方法·业界资讯
小脑斧爱吃鱼鱼2 小时前
鸿蒙项目笔记(1)
笔记·学习·harmonyos
robin_suli2 小时前
Spring事务的传播机制
android·java·spring