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

目录

  • [0 专栏介绍](#0 专栏介绍)
  • [1 服务通信模型](#1 服务通信模型)
  • [2 服务模型实现(C++)](#2 服务模型实现(C++))
  • [3 服务模型实现(Python)](#3 服务模型实现(Python))
  • [4 自定义服务](#4 自定义服务)
  • [5 话题、服务通信的异同](#5 话题、服务通信的异同)

0 专栏介绍

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

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


1 服务通信模型

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

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

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

  • 服务器

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

  • 客户端

    cpp 复制代码
    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));
    }

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

3 服务模型实现(Python)

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

  • 服务器

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

  • 客户端

    python 复制代码
    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)

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

4 自定义服务

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

  • 功能包下新建srv文件夹,在其中添加自定义服务xxx.srv,注意请求和响应数据结构使用---分割

  • 功能包package.xml中添加编译依赖与执行依赖

    xml 复制代码
    <buildtool_depend>rosidl_default_generators</buildtool_depend>
    <exec_depend>rosidl_default_runtime</exec_depend>
    <member_of_group>rosidl_interface_packages</member_of_group>
  • 功能包CMakeLists.txt中添加编译消息相关依赖

    txt 复制代码
    find_package(rosidl_default_generators REQUIRED)
    rosidl_generate_interfaces(${PROJECT_NAME}
    	"xxx.srv"
    	DEPENDENCIES xxx_srvs
    )
    
    ament_export_dependencies(rosidl_default_runtime)
  • 编译自定义消息,在install/<pkg_name>/include中生成由xxx.srv编译的C++可识别的xxx.hpp头文件

  • 引入xxx.hpp即可调用自定义服务

下面给出一个实例

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

shell 复制代码
# client
int32 a
int32 b
---
# server
int32 sum

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

  • 服务器

    cpp 复制代码
    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);
            }
    };
  • 客户端

    cpp 复制代码
    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));
    }

服务通信效果如下所示:

5 话题、服务通信的异同

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

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


🔥 更多精彩专栏

👇源码获取 · 技术交流 · 抱团学习 · 咨询分享 请联系👇

相关推荐
美狐美颜sdk1 小时前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk
DeepSeek-大模型系统教程1 小时前
推荐 7 个本周 yyds 的 GitHub 项目。
人工智能·ai·语言模型·大模型·github·ai大模型·大模型学习
郭庆汝1 小时前
pytorch、torchvision与python版本对应关系
人工智能·pytorch·python
小雷FansUnion3 小时前
深入理解MCP架构:智能服务编排、上下文管理与动态路由实战
人工智能·架构·大模型·mcp
资讯分享周3 小时前
扣子空间PPT生产力升级:AI智能生成与多模态创作新时代
人工智能·powerpoint
叶子爱分享5 小时前
计算机视觉与图像处理的关系
图像处理·人工智能·计算机视觉
鱼摆摆拜拜5 小时前
第 3 章:神经网络如何学习
人工智能·神经网络·学习
一只鹿鹿鹿5 小时前
信息化项目验收,软件工程评审和检查表单
大数据·人工智能·后端·智慧城市·软件工程
张较瘦_5 小时前
[论文阅读] 人工智能 | 深度学习系统崩溃恢复新方案:DaiFu框架的原位修复技术
论文阅读·人工智能·深度学习
cver1235 小时前
野生动物检测数据集介绍-5,138张图片 野生动物保护监测 智能狩猎相机系统 生态研究与调查
人工智能·pytorch·深度学习·目标检测·计算机视觉·目标跟踪