ROS服务(Service)通信:通信模型、Hello World与拓展

服务通讯是基于请求响应模式的,是一种应答机制。

用于偶然的、对时时性有要求、有一定逻辑处理需求的数据传输场景。

一、服务通讯模型

服务是一种双向通讯方式,它通过请求和应答的方式传递消息,该模型涉及到三个角色:

  • Master (管理者)
  • Server(服务端)
  • Client(客户端)

Master 负责保管 ServerClient 的注册信息,并匹配服务名称相同的 ServerClient ,帮助 他们建立连接,连接建立后,Client 可以发送请求信息, Server 收到请求后返回响应信息。

在这里插入图片描述

服务模型通讯流程:

  • 0)advertise:服务端注册

    服务端(Server)向管理者(Master)注册信息,包括RPC地址和Service名字。Master会将服务端的注册信息加入到注册表中。

  • 1)客户端注册

    客户端(Client)向管理者(Master)注册信息,包括Service名字。Master会将客户端(Client)的注册信息加入到注册表中。

  • 2)Master匹配信息:牵线搭桥

    管理者(Master)通过查询注册表发现有匹配的服务端(Server)和客户端(Client),则通过RPC向客户端(Client)发送服务端(Server)的 TCP/UDP 地址信息。

  • 3)客户端发送请求信息

    客户端根据服务端的 TCP/UDP 地址与服务端建立网络连接,并发送请求信息。

  • 4)服务端响应请求

    服务端收到请求数据后,通过处理产生响应数据,通过 TCP/UDP 返回给客户端。

Note:

  1. 上述实现流程中,前三步使用 RPC 协议,最后两步使用 TCP/UDP 协议,默认TCP。
  2. 客户端请求时,必须保证服务端已经启动。
  3. 服务名相同的客户端可以有多个,服务端只能有1个。
  4. 与话题通信不同,服务通信过程中,ROS Master必须处于启动状态。

二、Service Hello World

万物始于Hello World,同样,使用Hello World介绍Service的简单使用。

使用Service传输数据时,需要注意以下几点:

  • Service名称
  • 消息格式(srv)
  • 服务端实现
  • 客户端实现

接下来实现一个简单的 Service 服务通信,客户端请求启动机器人,服务端启动机器人的各个模块,然后返回执行结果。

2.1 创建并初始化功能包

首先创建 service_hello_world 包,命令如下:

catkin_creat_pkg service_hello_world std_srvs roscpp rospy

创建后,文件结构如下:

在这里插入图片描述

2.2 确定Service名称及消息格式

Service名称:/hello_world_service

消息格式:std_srvs::SetBool

消息文件路径:/opt/ros/noetic/share/std_srvs/srv/SetBool.srv

消息文件内容:

bool data # e.g. for hardware enabling / disabling
---
bool success   # indicate successful run of triggered service
string message # informational, e.g. for error messages

2.3 实现服务端与客户端(C++版)

在创建的 service_hello_world 包路径下有一个 src 目录,在这里存储C++源码,我们创建 service_hello_world_server.cpp 以实现服务端,编辑内容如下:

#include <ros/ros.h>
#include <std_srvs/SetBool.h>

bool dealRobotSwitch(std_srvs::SetBool::Request &req, std_srvs::SetBool::Response &resp)
{
    bool flag = req.data;
    ROS_INFO("服务器收到 [%s] 机器人的指令.", flag ? "启动" : "关闭");

    // 逻辑处理
    if (flag)
    {
        ROS_INFO("正在启动机器人各模块...");
        ros::Duration(2).sleep();
        // 使用时间模拟随机成功与失败
        if (ros::Time::now().toNSec() % 2 == 0)
        {
            resp.success = true;
            resp.message = "Hello World.";
            ROS_INFO("机器人各模块启动成功.\n");
        }
        else
        {
            resp.success = false;
            resp.message = "再睡一会";
            ROS_INFO("机器人各模块启动失败.\n");
        }
    }
    else
    {
        ROS_INFO("正在关闭机器人各模块...");
        ros::Duration(2).sleep();
        // 模拟成功与失败
        if (ros::Time::now().toNSec() % 2 == 0)
        {
            resp.success = true;
            resp.message = "Good Night.";
            ROS_INFO("机器人各模块关闭成功.\n");
        }
        else
        {
            resp.success = false;
            resp.message = "我还能卷";
            ROS_INFO("机器人各模块关闭失败.\n");
        }
    }

    return true;
}

int main(int argc, char **argv)
{
    setlocale(LC_ALL, "");
    ros::init(argc, argv, "service_hello_world_server");
    ros::NodeHandle nh;
    ros::ServiceServer server = nh.advertiseService("/robotSwitch", dealRobotSwitch);
    ROS_INFO("robotSwitch 服务已启动...");
    ros::spin();
    return 0;
}

创建 service_hello_world_client.cpp 以实现客户端,编辑内容如下:

#include <ros/ros.h>
#include <std_srvs/SetBool.h>

int main(int argc, char **argv)
{
    setlocale(LC_ALL, "");
    ros::init(argc, argv, "service_hello_world_client");
    ros::NodeHandle nh;
    ros::ServiceClient client = nh.serviceClient<std_srvs::SetBool>("/robotSwitch");

    std_srvs::SetBool srv;
    if (strcmp(argv[1], "on") == 0)
    {
        srv.request.data = true;
    }
    else if (strcmp(argv[1], "off") == 0)
    {
        srv.request.data = false;
    }
    else
    {
        ROS_WARN("仅支持on和off");

        return 1;
    }

    // 等待服务启动
    // ros::service::waitForService("/robotSwitch");
    // client.waitForExistence();
    if (client.call(srv))
    {
        if (srv.response.success)
        {
            ROS_INFO("操作成功, %s", srv.response.message.c_str());
        }
        else
        {
            ROS_ERROR("操作失败, %s", srv.response.message.c_str());
        }
    }
    else
    {
        ROS_ERROR("操作失败, 未知错误!");
    }

    return 0;
}

修改 CMakeLists.txt ,只需添加如下内容:

add_executable(${PROJECT_NAME}_client src/service_hello_world_client.cpp)
add_executable(${PROJECT_NAME}_server src/service_hello_world_server.cpp)

target_link_libraries(${PROJECT_NAME}_client
  ${catkin_LIBRARIES}
)

target_link_libraries(${PROJECT_NAME}_server
  ${catkin_LIBRARIES}
)

编译运行

进入工作空间执行 catkin_make 命令编译工程,编译成功后,使用如下命令依次启动服务端和客户端。

1. 启动ros master
roscore
2. 启动服务端
rosrun service_hello_world service_hello_world_server
3. 启动客户端
rosrun service_hello_world service_hello_world_client

结果如下:

在这里插入图片描述

目前为止,Service Hello World 已经成功了。

2.4 实现服务端与客户端(Python版)

在创建的 service_hello_world 包路径下 src 目录的同级,创建一个 scripts 目录,在这里存储脚本(如python脚本),我们创建 service_hello_world_server.py 以实现服务端,编辑内容如下:

import rospy
from std_srvs.srv import SetBool, SetBoolResponse


def dealRobotSwitch(req):
    flag = req.data
    rospy.loginfo("服务器收到 [%s] 机器人的指令.", "启动" if flag else "关闭")
    if flag:
        rospy.loginfo("正在启动机器人各模块...")
        if rospy.Time.now().to_nsec() % 2 == 0:
            rospy.loginfo("机器人各模块启动成功.\n")
            return SetBoolResponse(True, "Hello World.")
        else:
            rospy.logerr("机器人各模块启动失败.\n")
            return SetBoolResponse(False, "再睡一会")
    else:
        rospy.loginfo("正在关闭机器人各模块...")
        if rospy.Time.now().to_nsec() % 2 == 0:
            rospy.loginfo("机器人各模块关闭成功.\n")
            return SetBoolResponse(True, "Good Night.")
        else:
            rospy.logerr("机器人各模块关闭失败.\n")
            return SetBoolResponse(False, "我还能卷")


if __name__ == "__main__":
    rospy.init_node("service_hello_world_server")
    server = rospy.Service("/robotSwitch", SetBool, dealRobotSwitch)
    rospy.loginfo("robotSwitch 服务已启动...")
    rospy.spin()

创建 service_hello_world_client.py 以实现客户端,编辑内容如下:

import sys
import rospy
from std_srvs.srv import SetBool, SetBoolRequest


if __name__ == "__main__":
    rospy.init_node("service_hello_world_client")

    if len(sys.argv) != 2:
        rospy.logerr("参数个数有误")
        sys.exit(1)

    flag = False
    if sys.argv[1] == "on":
        flag = True
    elif sys.argv[1] == "off":
        pass
    else:
        rospy.logwarn("仅支持on和off")
        sys.exit(1)

    rospy.loginfo("客户端请求 [%s] 机器人.", "启动" if flag else "关闭")
    client = rospy.ServiceProxy("/robotSwitch", SetBool)
    client.wait_for_service()
    req = SetBoolRequest()
    req.data = flag
    res = client.call(req)

    if res.success:
        rospy.loginfo("操作成功,%s", res.message)
    else:
        rospy.logerr("操作失败,%s", res.message)

修改 CMakeLists.txt ,只需添加如下内容:

catkin_install_python(PROGRAMS
  scripts/service_hello_world_server.py
  scripts/service_hello_world_client.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

编译运行

进入工作空间执行 catkin_make 命令编译工程,编译成功后,使用如下命令依次启动服务端和客户端。

1. 启动ros master(如果已启动,无需再启动)
roscore
2. 启动服务端
rosrun service_hello_world service_hello_world_server.py
3. 启动客户端
rosrun service_hello_world service_hello_world_client.py

结果如下:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/158605.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

USART(1)

什么是USART 单片机上有的许多的外设 单片机通过这些外设实现特殊的功能 如果单片机想要和蓝牙模块实现数据的传输那么就也需要单片机有串口模块来和蓝牙模块的串口进行连接 相互传输数据 在单片机上的串口就叫USART USART就是单片机上的外设 来实现串口之间的通信功能 USART名…

基于51单片机步进电机节拍步数正反转LCD1602显示( proteus仿真+程序+原理图+设计报告+讲解视频)

基于51单片机步进电机节拍步数正反转LCD1602显示 &#x1f4d1;1. 主要功能&#xff1a;&#x1f4d1;2. 讲解视频&#xff1a;&#x1f4d1;3. 仿真&#x1f4d1;4. 程序代码&#x1f4d1;5. 设计报告&#x1f4d1;6. 设计资料内容清单&&下载链接&#x1f4d1;[资料下…

基于SSM的宠物医院管理系统

基于SSM的宠物医院管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringMyBatisSpringMVC工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 后台 摘要 随着人们对宠物健康关注的增加&#xff0c;宠物医疗服务的需求也…

许战海战略文库|企业竞争优势三大获取路径:产业、品牌和产品竞争优势

在快速发展和变革的全球化市场中&#xff0c;企业面临着持续的竞争压力。要在这种环境中脱颖而出&#xff0c;企业需要建立持久的竞争优势。通常&#xff0c;竞争优势可以从三个主要路径来获取&#xff1a;产业竞争优势、品牌竞争优势和产品竞争优势。 1. 产品竞争优势为什么很…

电子科技大学 分布式系统 期末复习笔记

第一章 为什么需要分布式系统&#xff1a;功能分离&#xff0c;固有的分布性&#xff0c;负载均衡&#xff0c;可靠性&#xff0c;经济性。 定义&#xff1a;分布式系统是这样一种系统&#xff0c;其中位于联网计算机上的组件仅通过传递消息来通信和协调它们的操作。 特点&am…

线性表的概念

目录 1.什么叫线性表2.区分线性表的题 1.什么叫线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串… 线性表在逻辑上是…

【Vue配置项】 computed计算属性 | watch侦听属性

目录 前言 computed计算属性 什么是计算属性&#xff1f; Vue的原有属性是什么&#xff1f; 得到的全新的属性是什么&#xff1f; 计算属性怎么用&#xff1f; 计算属性的作用是什么&#xff1f; 为什么说代码执行率高了&#xff1f; computed计算属性中的this指向 co…

【Java 进阶篇】JQuery 遍历 —— 无尽可能性的 `each` 之旅

在前端的征途中&#xff0c;操作元素是开发者不可避免的任务之一。而在 JQuery 中&#xff0c;each 方法则是处理这个任务的得力助手。本文将深入探讨 each 方法的奇妙之处&#xff0c;以及它与原生的 for...of 循环的关系&#xff0c;带你领略无尽可能性的遍历之旅。 起步&am…

modbusRTU通信简单实现(使用NModbus4通信库)

本文实现ModbusRTU通信&#xff0c;使用的是NModbus4通信库&#xff0c;使用 Modbus Slave是一个模拟Modbus协议从机的上位机软件&#xff0c;主要用于模拟测试跟其他主机设备通信的过程。与之成套存在的另一个软件--Modbus Poll&#xff0c;则是模拟Modbus协议主机的上位机软件…

元宇宙数字展厅无代码编辑工具的功能特点

商场如战场&#xff0c;营销是每个企业都必须重视的环节。随着科技的发展&#xff0c;3D展示营销制作平台作为企业快速搭建3D互动展厅的SaaS平台&#xff0c;逐渐崭露头角&#xff0c;为企业提供了诸多便利&#xff0c;让营销变得更加高效和引人入胜。 为企业提供身临其境的产品…

【EI会议征稿】第五届人工智能与机电自动化国际学术会议(AIEA 2024)

第五届人工智能与机电自动化国际学术会议&#xff08;AIEA 2024&#xff09; 2024 5th International Conference on Artificial Intelligence and Electromechanical Automation 第五届人工智能与机电自动化国际学术会议&#xff08;AIEA 2024&#xff09;将于2024年3月8-10…

算法竞赛备赛进阶之状态机模型训练

目录 1.大盗阿福 2.股票买卖IV 3.股票买卖V 4.设计密码 算法状态机&#xff08;ASM&#xff09;图是一种描述时序数字系统控制过程的算法流程图&#xff0c;其结构形式类似于计算机中的程序流程图。 ASM图是用一些特定符号按规定的连接方式来描述数字系统的功能。应用ASM图…

基于JavaWeb+SSM+购物系统微信小程序的设计和实现

基于JavaWebSSM购物系统微信小程序的设计和实现 源码获取入口前言主要技术系统设计功能截图Lun文目录订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 前言 第一章 绪 论 1.1选题背景 互联网是人类的基本需求&#xff0c;特别是在现代社会&#xff0c;…

信号的机制——信号处理函数的注册

在 Linux 操作系统中&#xff0c;为了响应各种各样的事件&#xff0c;也是定义了非常多的信号。我们可以通过 kill -l 命令&#xff0c;查看所有的信号。 # kill -l1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP6) SIGABRT 7) SIGBUS …

计算机毕业设计 基于SpringBoot的医院档案管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

使用 SMI 指标增强股票分析:amCharts JS Crack

使用 SMI 指标增强股票分析 2023 年 11 月 16 日 amCharts 5&#xff1a;股票图表 v5.5.3 增加了对随机动量指数指标的支持&#xff0c;帮助用户做出更明智的交易决策。 amCharts 5&#xff1a;股票图表提供了用于显示基于时间的数据的分析工具&#xff0c;无论是金融、股票还是…

使用群晖Docker搭建HomeAssistant并实现异地公网访问

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 使用群晖Docker搭建HomeAssistant并实现异地公网访问 文章目录 使用群晖Docker搭建HomeAssistant…

Mac安装Homebrew

方式一&#xff1a;官网&#xff08;很慢&#xff0c;不推荐&#xff09; curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh方式二&#xff1a; 1、执行以下命令 /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/ma…

StableDiffusion(六)——局部重绘

目录 一、局部重绘 1.局部重绘基本操作 ①打开方式 ②使用方法 ③核心参数解析 2.局部重绘&#xff08;手涂蒙版&#xff09;功能应用 3.局部重绘&#xff08;上传蒙版&#xff09;功能应用 ①选择选区 ②蒙版制作 一、局部重绘 当我们在进行AI绘画的过程中经常会出现…

JavaWeb[总结]

文章目录 一、Tomcat1. BS 与 CS 开发介绍1.1 BS 开发1.2 CS 开发 2. 浏览器访问 web 服务过程详解(面试题)2.1 回到前面的 JavaWeb 开发技术栈图2.2 浏览器访问 web 服务器文件的 UML时序图(过程) &#xff01; 二、动态 WEB 开发核心-Servlet1. 为什么会出现 Servlet2. 什么是…