ROS通信机制(二) —— 服务(service)与srv文件

2023-05-16

文章目录

  • 简述
  • 特点
  • 相关常用命令
  • 通信模型
  • 核心元素
  • 通信过程
  • 代码示例(服务端和客户端)
    • 服务端(server.cpp)
    • 客户端(client.cpp)
    • 配置 CMakeLists.txt
    • 编译和运行
    • 问题扩展
  • 自定义消息
    • 自定义srv文件
    • 编辑配置文件
    • 执行编译
    • 修改服务端和客户端代码
    • 编译和运行

简述

服务消息通信是指请求服务的服务客户端与负责服务响应的服务服务器之间的同步双向服务消息通信,一个服务被分成服务服务器和服务客户端,其中服务服务器只在有请(request)的时候才响应(response),而服务客户端会在发送请求后接收响应。

特点

与话题不同,服务是一次性消息通信。因此,当服务的请求和响应完成时,两个连接的节点将被断开。该服务通常被用作请求机器人执行特定操作时使用的命令,或者用于根据特定条件需要产生事件的节点。由于它是一次性的通信方式,又因为它在网络上的负载很小,所以它也被用作代替话题的手段,因此是一种非常有用的通信手段。

相关常用命令

命令详细说明
rosservice list显示活动的服务信息
rosservice info [服务名称]显示指定服务的信息
rosservice type [服务名称]显示服务类型
rosservice find [服务类型]查找指定服务类型的服务
rosservice uri [服务名称]显示ROSRPC URI服务
rosservice args [服务名称]显示服务参数
rosservice call [服务名称] [参数]用输入的参数请求服务

通信模型

在这里插入图片描述

核心元素

  1. ROS Master(管理者)
    必须首先被运行,并使用XMLRPC服务器,负责管理节点之间的消息通信中的连接信息。
  2. Server (服务端)
    服务端以请求作为输入,以响应作为输出,请求和响应都是消息,服务端收到服务请求后,执行指定的服务,并将结果下发给客户端,服务端是用于执行指定命令的节点。
  3. Client (客户端)
    客户端以请求作为输出,以响应作为输入,请求和响应都是消息,并发送服务请求到服务端后接收其结果,客户端是用于传达给定命令并接收结果值的节点。

通信过程

  1. 运行主节点
  2. 运行服务端
    服务端向主节点注册自身信息,包括节点名称、服务名称、消息类型、URI地址和端口。
  3. 运行客户端
    客户端端向主节点注册自身信息,包括节点名称、服务名称、消息类型、URI地址和端口。
  4. 主节点向客户端发送服务端信息
    主节点向客户端发送此客户端希望访问的服务端节点的名称、服务名称、消息类型、URI地址和端口等信息。
  5. 客户端发送请求
    客户端根据主节点响应的信息,使用 TCPROS 与 服务端建立网络连接,并发送请求数据。
  6. 服务端发送响应
    服务端接收、处理请求的数据,并将响应结果返回给客户端。

代码示例(服务端和客户端)

需求描述:编写服务端和客户端文件,模拟打开、关闭开关的操作,服务端收到请求true(1)时打印 “开关打开” 的信息,收到请求false(0)时打印 “开关关闭” 的信息。

首先,这里创建了一个service_test的包,然后分别创建server.cpp(服务端)和client.cpp(客户端)两个文件。
在这里插入图片描述

服务端(server.cpp)

#include "ros/ros.h"
#include "std_srvs/SetBool.h"

bool processRequest(std_srvs::SetBool::Request  &req,
                std_srvs::SetBool::Response &res)
{
    res.success = req.data;
    if(req.data)
    {
        res.message = "开关已打开";
    }
    else
    {
        res.message = "开关已关闭";
    }
    ROS_INFO("状态切换为:%d --- %s", res.success, res.message.c_str());

    return true;
}

int main(int argc, char **argv)
{
    // 设置编码
    setlocale(LC_ALL, "");

    // 1.初始化ROS节点
    ros::init(argc, argv, "server");

    // 2.实例化ROS句柄
    ros::NodeHandle nh;

    // 3.实例化服务端对象
    // 参数1为服务名称,参数2为处理请求的回调函数
    ros::ServiceServer server = nh.advertiseService("service_chatter", processRequest);

    ros::spin();

    return 0;
}

客户端(client.cpp)

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

int main(int argc, char **argv)
{
    // 设置编码
    setlocale(LC_ALL, "");

    // 1.初始化ROS节点
    // 参数3为节点名称,全局唯一
    ros::init(argc, argv, "client");

    // 2.实例化ROS句柄
    ros::NodeHandle nh;

    // 3.实例化客户端对象
    // 参数1为服务名称
    ros::ServiceClient client = nh.serviceClient<std_srvs::SetBool>("service_chatter");

    // 4.定义请求数据
    std_srvs::SetBool srv;
    srv.request.data = atoll(argv[1]);

    // 6.发送请求开关已打开
    bool success = client.call(srv);

    // 7.处理响应
    if (success)
    {
        ROS_INFO("请求成功,收到反馈数据:%d --- %s", srv.response.success, srv.response.message.c_str());
    }
    else
    {
        ROS_ERROR("请求失败");
        return 1;
    }

    return 0;
}

配置 CMakeLists.txt

# 节点构建选项,配置可执行文件
add_executable(server src/server.cpp)
add_executable(client src/client.cpp)

# 节点构建选项,配置目标链接库
target_link_libraries(server
  ${catkin_LIBRARIES}
)
target_link_libraries(client
  ${catkin_LIBRARIES}
)

编译和运行

使用Ctrl+Shift+B进行编译,然后先使用roscore命令启动主节点。source下环境变量,然后运行服务端。在新终端source下环境变量,然后运行客户端,并附带请求参数。
在这里插入图片描述

问题扩展

话题的发布者和订阅者是没有启动顺序的要求的,因为消息是在循环发送,所以谁先启动谁后启动都能发送、接收消息。服务通信有所区别,需要先启动服务端,再启动客户端,否则会提示 “请求失败”。
在这里插入图片描述
我们可以使用消息阻塞函数来优化此问题,如果客户端先启动则一直阻塞,直到服务端启动后再继续执行。

在客户端发送请求之前添加下面的代码:

ros::service::waitForService("service_chatter");

在这里插入图片描述


自定义消息

ROS的服务类型可以在这里查看。

需求描述:客户端提交两个整数至服务端,服务端进行求和计算并响应结果给客户端。

自定义srv文件

srv 文件内的可用数据类型与 msg 文件一致,不同的是,srv文件中的数据分成两部分,使用 “- - -” 隔开,上面是请求数据,下面是响应数据。

首先,在service_test包中创建srv文件夹,然后,在文件夹中创建消息文件(AddTwoInts.srv)。

int64 A
int64 B
---
int64 Sum

在这里插入图片描述

编辑配置文件

  • 在package.xml中添加编译依赖与执行依赖
<!-- 编译时依赖 -->
<build_depend>message_generation</build_depend>
<!-- 运行时依赖 -->
<exec_depend>message_runtime</exec_depend>
  • 在CMakeLists.txt编辑 srv相关配置
# catkin构建时依赖的组件包,前3个创建ROS包时已经自动生成了,这里添加了message_generation
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)

# 配置srv源文件,FILES将引用当前功能包目录的srv目录中的*.srv文件,自动生成一个头文件(*.h)
add_service_files(
  FILES
  AddTwoInts.srv
)

# 生成消息时依赖于std_msgs
generate_messages(
  DEPENDENCIES
  std_msgs
)

# 运行时依赖,描述了库、catkin构建依赖项和系统依赖的功能包
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES topic_test
 CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
#  DEPENDS system_lib
)

执行编译

使用Ctrl+Shift+B进行编译,然后可以在/devel/service_test目录下看到自动生成的头文件AddTwoInts.h,此时便可以代后面的代码中进行引用了。
在这里插入图片描述
PS: 在修改代码之前,先在c_cpp_properties.json文件中添加下头文件路径,否则在代码中引用头文件时会出现找不到的情况。之前已经添加过的话,这里就不需要再重复添加了。
在这里插入图片描述

修改服务端和客户端代码

也可参考ROS WIKI中的代码示例。

下面我们在代码中使用自定义的srv来进行通信。

  • 服务端(server.cpp)
#include "ros/ros.h"
#include "service_test/AddTwoInts.h"

bool processRequest(service_test::AddTwoInts::Request  &req,
                service_test::AddTwoInts::Response &res)
{
    res.Sum = req.A + req.B;
    ROS_INFO("收到请求数据: x=%ld, y=%ld", (long int)req.A, (long int)req.B);
    ROS_INFO("响应的计算结果为: [%ld]", (long int)res.Sum);
    return true;
}

int main(int argc, char **argv)
{
    // 设置编码
    setlocale(LC_ALL, "");

    // 1.初始化ROS节点
    ros::init(argc, argv, "server");

    // 2.实例化ROS句柄
    ros::NodeHandle nh;

    // 3.实例化服务端对象
    // 参数1为服务名称,参数2为处理请求的回调函数
    ros::ServiceServer server = nh.advertiseService("service_chatter", processRequest);

    ros::spin();

    return 0;
}
  • 客户端(client.cpp)
#include "ros/ros.h"
#include "service_test/AddTwoInts.h"
#include <cstdlib>

int main(int argc, char **argv)
{
    // 设置编码
    setlocale(LC_ALL, "");

    // 1.初始化ROS节点
    // 参数3为节点名称,全局唯一
    ros::init(argc, argv, "client");

    // 这里的形参argc表示第二个形参数组argv中字符串的个数,即命令行传递的参数个数
    // argv[0]:文件路径,/home/zbw/robot_test_ws/devel/lib/service_test/client
    // argv[1]:传入的参数1,A
    // argv[2]:传入的参数2,B
    if (argc != 3)
    {
        ROS_ERROR("请提交两个整数");
        return 1;
    }

    // 2.实例化ROS句柄
    ros::NodeHandle nh;

    // 3.实例化客户端对象
    // 参数1为服务名称
    ros::ServiceClient client = nh.serviceClient<service_test::AddTwoInts>("service_chatter");

    // 等待服务端启动
    ros::service::waitForService("service_chatter");

    // 4.定义请求数据
    service_test::AddTwoInts srv;
    srv.request.A = atoll(argv[1]);
    srv.request.B = atoll(argv[2]);

    // 6.发送请求开关已打开
    bool success = client.call(srv);

    // 7.处理响应
    if (success)
    {
        ROS_INFO("请求成功,收到反馈数据:%ld", (long int)srv.response.Sum);
    }
    else
    {
        ROS_ERROR("请求失败");
        return 1;
    }

    return 0;
}

编译和运行

使用Ctrl+Shift+B进行编译,然后使用roscore命令启动主节点,source下环境变量,分别运行服务端和客户端即可看到通信数据的打印输出。
在这里插入图片描述
☝ ★★★ — 返回 《ROS机器人开发笔记汇总》总目录 — ★★★ ☝

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ROS通信机制(二) —— 服务(service)与srv文件 的相关文章

  • Spring Boot RestController,错误状态响应主体,错误消息为空

    在我的 Spring Boot RestController 上 我想通过抛出自定义异常来将自定义错误消息传递到响应正文 我正在遵循指南https dzone com articles spring rest service excepti
  • 如何绑定到正在运行的android服务?

    我希望这更多的是代码问题 而不是其他问题 我希望有人可以帮助解决这个问题 我还有其他使用 startService 启动服务的代码 并且当调试器点击 DecoderService 的 onCreate 函数时 我可以验证该服务是否已启动 但
  • Windows服务之间如何通信

    我有 2 个使用 C 创建的 Windows 服务 我希望其中一个服务调用第二个 Windows 服务中的函数 我该怎么做呢 EDIT 问题是我必须运行该应用程序 我不需要它们 相反服务进程也很好 但我need这2个应用程序进行通信 这2个
  • PhoneStateListener onSignalStrengthsChanged 停止在 Service 中调用

    我正在编写包含两个元素的简单应用程序 服务和活动 活动仅用于启动和停止服务 服务用途PhoneStateListener获取有关当前 CellID LAC 和 RSSI 信号强度 的信息并将其记录到文件中 当手机未休眠时一切正常 我注意到
  • 如何在 Android 中与正在运行的线程进行服务通信

    我的目标是推出一项能够满足所有应用程序网络需求的服务 我想也许打开2个套接字用于数据传输 我希望异步处理数据 所以我想我应该在两个单独的线程中运行它们 每个线程针对每个套接字 这样数据就可以在两个不同的 链接 异步中进行流式传输 所以 我希
  • 如何每天中午以及每次启动时运行服务

    在我的应用程序中 我有一个 SQLite 数据库 其中有一个表 其中包含以毫秒为单位的日期行 我希望每天显示一条通知IF自上次存储在我的数据库中的日期值以来已经过去了 30 天 服务似乎是完成此检查的好方法 我遇到了 Commonsware
  • Angular2 - 多个依赖的顺序 http api 调用

    我正在构建一个 Angular2 应用程序 其中一个组件需要进行多个 API 调用 这些调用依赖于之前的调用 我目前有一项服务可以调用 API 来获取电视节目列表 对于每个节目 我需要多次调用不同的 API 来逐步检查该结构 以确定该节目是
  • Spring - 如何注入具体的接口实现?

    我需要通过 Autowired 注入服务类的具体实现 服务接口 public interface PostService 执行 Service postServiceImpl public class PostServiceImpl imp
  • ServiceRoute + WebServiceHostFactory 杀死 WSDL 生成?如何使用 ?wsdl 创建无扩展的 WCF 服务

    我正在尝试使用无扩展名 无 svc WCF 服务 其他人可以确认或否认我遇到的问题吗 我在代码中使用路由 并在 global asax cs 的 Application Start 中执行此操作 RouteTable Routes Add
  • 作为 Windows 服务运行时的 PCSC.InvalidContextException

    我一直在使用 pcsc sharp 库开发一个小型智能卡扫描仪应用程序 该应用程序作为控制台应用程序运行时工作正常 代码如下 using System using System Collections Generic using Syste
  • 如何查看定位服务是否开启?

    如何检查用户是否关闭了定位服务 这样我就可以提示他 她打开它才能使用我的应用程序 谢谢 The CLLocationManager提供类方法来确定位置服务的可用性 BOOL locationServicesEnabled for lt iO
  • 如何在 Docker-Compose 中一起使用主机网络和任何其他用户定义的网络?

    我想将 Docker Compose 文件中定义的两个 Docker 容器相互连接 app and db 其中之一 app 也应该连接到host网络 容器应连接到通用的用户定义网络 appnet or default 使用嵌入式DNS来自
  • OSGi 应用程序设计 - 我是否滥用服务框架? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 在我们正在开发的应用程序中 我有一个供数据提供程序组件实现的通用接口 并且我将这些提供程序作为服务连接起来 我的一位同事建议 最好只创建一项可以
  • AngularJS 服务 http 成功函数使用错误的“this”范围

    a 的成功函数 http put无权访问this内部调用的服务的范围 我需要在 PUT 请求的回调中更新服务的属性 这是我在服务中尝试做的事情的简化示例 var myApp angular module myApp function rou
  • 使用 MediaRecorder 录制屏幕特定视图

    我想录制特定的屏幕视频View链接只想记录里面执行的动作LinearLayout 现在 MediaRecorder正在录制整个屏幕 如何录制屏幕的特定部分 MediaRecorder 通过媒体投影API 记录整个屏幕 至少从 Android
  • 多个Android IntentService可以同时运行吗?

    据我了解 一个IntentService一次只能处理一个 Intent 因为它共享一个工作线程来完成所有工作 但如果我有多个IntentService在我的应用程序中 它们可以并行运行 还是都共享单个工作线程 但是如果我的应用程序中有多个
  • 独立 Symfony2 包内的功能测试

    我需要直接在独立包中进行一些功能测试 我不想测试控制器 只是测试真实服务之间的一些交互 我想知道是否有标准 最佳方法可以做到这一点 我用一种方法做到了 但想知道是否有更好的方法 这是我自己的解决方案 我总结了在独立包中测试的所有过程 1 首
  • aSmack 即服务

    基本上我有一个运行整个项目的主类 该代码运行完美 尽管一旦应用程序失去焦点 它就会变得不活动 我想知道如何将其变成一项服务 一个会在启动时启动的 该应用程序将是一个用于通知的单向消息系统 IE 桌面客户端 gt Openfire 服务器 g
  • 无法从 Windows 7 上的 Windows 服务启动桌面应用程序

    HI 我在 Windows 7 上有 C WCF Windows 服务 以具有管理员权限的用户身份登录 我正在尝试在服务启动后启动桌面应用程序 我发现的所有讨论都是关于 Windows 工作站和桌面 我创建了一个单独的线程 设置线程工作站和
  • 使用taskkill停止Windows服务

    我需要帮助来使用 C 终止 Windows 服务 现在要终止该服务 请使用以下选项 从命令 sc queryex ServiceName 发现后PID服务的 taskkill pid 1234 exemple f 为了便于阅读 但如果您明白

随机推荐

  • python socket小结

    Python socket 简单编程小结 首先创建服务器端的socket socket server import socket 定义变量 HOST 61 34 localhost 34 PORT 61 是数字类型 xff0c 不是字符串类
  • STM32 IO口模拟I2C+驱动MPU6050

    一年前写的博客 xff0c 没有把驱动代码分享出来是我疏忽了 xff0c 可以到网盘下载驱动代码 链接 xff1a https pan baidu com s 1SDVQfyoOoycCY 6eSXamlQ 密码 xff1a ipj7 之后
  • git常用操作(branch tag)

    git日常总结 1 初次在本地下载分支代码操作2 初次下载远程代码3 创建分支3 1 创建本地分支3 2创建远程分支 4 删除分支4 1 删除本地分支4 2删除远程分支 5 删除文件5 1 删除本地文件5 2 删除远程文件 6 删除提交6
  • openwrt出现md5sum mismatch错误

    原文地址 xff1a http catinmay com openwrt E5 87 BA E7 8E B0md5sum mismatch E9 94 99 E8 AF AF 刚刚帮人搞路由器一运行安装命令就会出现此错误 xff0c 错误提
  • python——系统交互subprocess

    目录 一 os与commands模块 1 os system 函数实例 2 os popen 函数实例 3 commands getstatusoutput 函数实例 二 subprocess模块 1 subprocess模块中的常用函数
  • strcat 你真的懂吗?

    http blog chinaunix net uid 26914516 id 4215338 html 声明 xff1a 使用GCC编译 strcat xff08 连接两字符串 xff09 函数定义 xff1a char strcat c
  • HTTP超全详解

    HTTP协议简介 超文本传输协议 xff08 英文 xff1a HyperText Transfer Protocol xff0c 缩写 xff1a HTTP xff09 是一种用于分布式 协作式和超媒体信息系统的应用层协议 HTTP是一个
  • 【Linux】三次握手和四次挥手详解

    三次握手和四次挥手 TCP 协议提供的是 xff1a 面向连接 可靠的 字节流服务 使用 TCP 协议通信的双发必须先建立连接 xff0c 然后才能开始数据的读写 双方都必须为该连接分配必要的内核资源 xff0c 以管理连接的状态和连接上数
  • Sion 450行的c++ HttpClient

    Sion Sion是一个轻量级的c 43 43 http客户端 xff0c 仅单头文件450行 xff0c 自带std string的扩展Sion is a lightweight C 43 43 HTTP Client with only
  • bluerov软件调试教程(一 )

    bluerov是目前最流行的一款消费级ROV xff0c 其架构简单 xff0c 可操作性强 xff0c 比同类型的ROV体验感要高很多 xff0c 本系列教程将从bluerov的软件调试 xff0c 硬件调试 xff0c 装舱等三个方面去
  • 安装完成Ubuntu20.04之后要做的事:基础配置、界面美化、异常处理与常用软件的安装

    文章目录 一 换源1 1 通过软件更新1 2 通过修改源文件 二 安裝显卡驱动2 1 解决显卡驱动安装的错误 三 设置新建文件模板四 卸载系统软件五 安裝中文输入法5 1 安装5 2 解决键盘短暂失灵和延迟的问题 六 双系统时间同步七 修改
  • 【C++】Windows客户端与Ubuntu服务器基于socket的简易网络编程

    C 43 43 Windows客户端与Ubuntu服务器基于socket的简易网络编程 服务器端 本人使用的clion远程连接的ubuntu服务器来运行以下代码 xff0c 理论上也可以直接放在服务器上 创建项目后 xff0c 先运行以下代
  • Python中pyusb的开发及使用

    Python中pyusb的开发及使用 因为项目的需求 xff0c 需要将FPGA端的图像像素数据经过USB2 0协议传输到PC端 xff0c 因此需要使用python的pyusb库来进行数据的发送和接收 以下纪录在使用pyusb库的方法和所
  • NVIDIA Jetson Xavier NX 实现官方Jeston-inference深度学习样例

    一 jetson inference相关项目组件的下载 首先附上官方提供的jetson inference项目文件的Github仓库地址 xff0c 大家可以自行前往下载 jetson inference下载地址 xff1a https g
  • CMake 混编c和c++代码

    准备工作 wsl 或者 有linux 系统 购买阿里云或者其他云服务器 xff09 cmake gcc git 等一些必要的软件安装 环境 windows 下 的 wsl wsl 安装下载 例子 拿 Unix网络编程 举例 作者对原生接口进
  • Ros中使用find_object_2d快速实现物体的检测识别

    运行环境 xff1a Ubuntu16 04 ros kinetic版本 准备工作 xff1a 需要r提前安装的webcam的驱动 xff0c 这里推荐使用两种usb cam和uvc camera xff08 1 xff09 usb cam
  • ubuntu16.04多版本Python任意切换(亲测有效)

    UBUNTU16 04的系统安装ROS之后 xff0c 自带的是Python2 7 12的版本 然后实际使用中 xff0c 可能用到其他工具需要装Python3 5以上的版本 安装完之后 xff0c 可能经常在编译某些工程的时候出现一些代码
  • ROS中级教程学习笔记1-手动创建ROS package

    之前在基础教程中 xff0c 我们使用的是catkin create pkg自动创建ros包 xff0c 实际上就是一个package xml文件和CMakeLists txt文件加文件夹 xff0c 所以手动创建看看能不能呢和自动创建有一
  • 串口分类

    串口一般来说就是UART xff0c 它实际只定义了数据链路层的规范 xff0c 也就是起始位 数据位 停止位 但是在不同的物理层又分为 xff1a TTL串口 RS232串口 RS485串口等 TTL串口 xff1a 它是MCU芯片之间进
  • ROS通信机制(二) —— 服务(service)与srv文件

    文章目录 简述特点相关常用命令通信模型核心元素通信过程代码示例 xff08 服务端和客户端 xff09 服务端 xff08 server cpp xff09 客户端 xff08 client cpp xff09 配置 CMakeLists