基于ROS 服务通信模式详解

ROS 服务通信模式

摘自《ROS机器人开发实践》

服务(services)是节点之间通讯的另一种方式。服务允许节点发送请求(request) 并获得一个响应(response)

AddTwoInts.h文件是根据AddTwoInts.srv文件生成的

还会自动生成

AddTwoIntsRequest.h

AddTwoIntsResponse.h

AddTwoInts.h所在的目录是

\catkin_ws\devel

AddTwoInts.srv

int64 a
int64 b
---
int64 sum

server.cpp

/**
 * AddTwoInts Server
 */

#include "ros/ros.h"
#include "learning_communication/AddTwoInts.h"

// service回调函数,输入参数req,输出参数res
bool add(learning_communication::AddTwoInts::Request &req,
     learning_communication::AddTwoInts::Response &res)
{
  // 将输入参数中的请求数据相加,结果放到应答变量中
  res.sum = req.a + req.b;
  ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
  ROS_INFO("sending back response: [%ld]", (long int)res.sum);

  return true;
}

int main(int argc, char **argv)
{
  // ROS节点初始化
  ros::init(argc, argv, "add_two_ints_server");

  // 创建节点句柄
  ros::NodeHandle n;

  // 创建一个名为add_two_ints的server,注册回调函数add()
  ros::ServiceServer service = n.advertiseService("add_two_ints", add);

  // 循环等待回调函数
  ROS_INFO("Ready to add two ints.");
  ros::spin();

  return 0;
}

client.cpp

/**
 * AddTwoInts Client
 */

#include <cstdlib>
#include "ros/ros.h"
#include "learning_communication/AddTwoInts.h"

int main(int argc, char **argv)
{
  // ROS节点初始化
  ros::init(argc, argv, "add_two_ints_client");

  // 从终端命令行获取两个加数
  if (argc != 3)
  {
    ROS_INFO("usage: add_two_ints_client X Y");
    return 1;
  }

  // 创建节点句柄
  ros::NodeHandle n;

  // 创建一个client,请求add_two_int service
  // service消息类型是learning_communication::AddTwoInts
  ros::ServiceClient client = n.serviceClient<learning_communication::AddTwoInts>("add_two_ints");

  // 创建learning_communication::AddTwoInts类型的service消息
  learning_communication::AddTwoInts srv;
  srv.request.a = atoll(argv[1]);
  srv.request.b = atoll(argv[2]);

  // 发布service请求,等待加法运算的应答结果
  if (client.call(srv))
  {
    ROS_INFO("Sum: %ld", (long int)srv.response.sum);
  }
  else
  {
    ROS_ERROR("Failed to call service add_two_ints");
    return 1;
  }

  return 0;
}

CMakeLists.txt

add_executable(server src/server.cpp)
target_link_libraries(server ${catkin_LIBRARIES})
add_dependencies(server ${PROJECT_NAME}_gencpp)

add_executable(client src/client.cpp)
target_link_libraries(client ${catkin_LIBRARIES})
add_dependencies(client ${PROJECT_NAME}_gencpp)

package.xml

<?xml version="1.0"?>
<package>
 <name>learning_communication</name>
 <version>0.0.0</version>
 <description>The learning_communication package</description>
 <maintainer email="hcx@todo.todo">hcx</maintainer>

 <license>TODO</license>

 <buildtool_depend>catkin</buildtool_depend>
 <build_depend>geometry_msgs</build_depend>
 <build_depend>roscpp</build_depend>
 <build_depend>rospy</build_depend>
 <build_depend>std_msgs</build_depend>
 <run_depend>geometry_msgs</run_depend>
 <run_depend>roscpp</run_depend>
 <run_depend>rospy</run_depend>
 <run_depend>std_msgs</run_depend>

 <build_depend>message_generation</build_depend>
 <run_depend>message_runtime</run_depend>

 <!-- The export tag contains other, unspecified, tags -->
 <export>
  <!-- Other tools can request additional information be placed here -->

 </export>
</package>

文件夹的主要功能

1)config:放置功能包中的配置文件,由用户创建,文件名可以不同。
2)include:放置功能包中需要用到的头文件。
3)scripts:放置可以直接运行的Python脚本。
4)src:放置需要编译的C++代码。
5)launch:放置功能包中的所有启动文件。
6)msg:放置功能包自定义的消息类型。
7)srv:放置功能包自定义的服务类型。
8)action:放置功能包自定义的动作指令。
9)CMakeLists.txt:编译器编译功能包的规则。

如何自定义服务数据

与话题消息类似,ROS中的服务数据可以通过srv文件进行语言无关的接口定义,一般放置在功能包根目录下的srv文件夹中。该文件包含请求与应答两个数据域,数据域中的内容与话题消息的数据类型相同,只是在请求与应答的描述之间,需要使用“—”三个横线进行分割。

针对加法运算例程中的服务需求,创建一个定义服务数据类型的srv文件learning_communication/srv/AddTwoInts.sint64 a

int64 b

int64 sumv文件的内容较为简单,在服务请求的数据域中定义了两个int64类型的变量a和b,用来存储两个加数; 又在服务应答的数据域中定义了一个int64类型的变量sum,用来存储“a+b”的结果。

完成服务数据类型的描述后,与话题消息一样,还需要在功能包的package.xml和CMakeLists.txt文件中配置依赖与编译规则,在编译过程中将该描述文件转换成编程语言所能识别的代码。

打开package.xml文件,添加以下依赖配置

message_generation
message_runtime

打开CMakeLists.txt文件,添加如下配置

find_package(catkin REQUIRED COMPONENTS
geometry_msgs
roscpp
rospy
std_msgs
message_generation
)

add_service_files(
FILES
AddTwoInts.srv
)

message_generation包不仅可以针对话题消息产生相应的代码,还可以根据服务消息的类型描述文件产生相关的代码。 功能包编译成功后,在服务的Server节点和Client节点的代码实现中就可以直接调用这些定义好的服务消息了。 接下来我们就编写Server和Client节点的代码,完成两数相加求和的服务过程。

代码解释

Server节点实现过程

1. 头文件

#include "learning-communication/AddTwoInts.h"

使用ROS中的服务,必须包含服务数据类型的头文件,这里使用的头文件是learning_communication/AddTwoInts.h,该头文件根据我们之前创建的服务数据类型的描述文件AddTwoInts.srv自动生成。

2.主函ros::ServiceServer service = n.advertiseService(“add_two_ints”, add);部分相对简单,先初始化节点,创建节点句柄,重点是要创建一个服务的Server,指定服务的名称以及接收到服务数据后的回调函数。然后开始 循环等待服务请求;一旦有服务请求,Server就跳入回调函数进行处理。

3.回调函数部分

bool add(learning_communication::AddTwoInts::Request &req,learning_communication::AddTwoInts::Response &res)

回调函数是真正实现服务功能的部分,也是设计的重点。add()函数用于完成两个变量相加的功能,其传入参数便是我们在服务数据类型描述文件中声明的请求与应答的数据结构。

{
res.sum = req.a + req.b;
ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
ROS_INFO("sending back response: [%ld]", (long int)res.sum);
ROS_INFO("sending back response: [%ld]", (long int)res.sum);
return true;
}

在完成加法运算后,求和结果会放到应答数据中,反馈到Client,回调函数返回true。

服务中的Server实现流程如下:

·初始化ROS节点。

·创建Server实例。

·循环等待服务请求,进入回调函数。

·在回调函数中完成服务功能的处理并反馈应答数据。

Client节点的实现过程。

1.创建Client

 ros::ServiceClient client = n.serviceClient<learning_communication::AddTwoInts> ("add_two_ints");

首先需要创建一个add_two_ints的Client实例,指定服务类型为learning_communication:AddTwoInts。

2.发布服务请求

learning_communication::AddTwoInts srv;
srv.request.a = atoll(argv[1]);
srv.request.b = atoll(argv[2]);

然后实例化一个服务数据类型的变量,该变量包含两个成员:request和response。将节点运行时输入的两个参数作为需要相加的两个整型数存储到变量中。

if (client.call(srv))

接着进行服务调用。该调用过程会发生阻塞,调用成功后返回true,访问srv.reponse即可获取服务请求的结果。如果调用失败会返回false,srv.reponse则不可使用。

服务中的Client实现流程如下:

·初始化ROS节点。

·创建一个Client实例。

·发布服务请求数据。

·等待Server处理之后的应答结果。

编译功能包

编辑CMakeLists.txt文件,加入如下编译规则:

add_executable(server src/server.cpp)
target_link_libraries(server ${catkin_LIBRARIES})
add_dependencies(server ${PROJECT_NAME}_gencpp)
add_executable(client src/client.cpp)
target_link_libraries(client ${catkin_LIBRARIES})
add_dependencies(client ${PROJECT_NAME}_gencpp)

现在就可以使用catkin_make命令编译功能包了。

运行Server和Client

运行编译生成的Server和Client节点。

1.启动roscore

在运行节点之前,首先需要确保ROS Master已经成功启动:

roscore2.运行Server节点打开终端,使用如下命令运行Server节点:roscore

2.运行Server节点打开终端,使用如下命令运行

Server节点: rosrun learning_communication server

3.运行Client节点

打开一个新的终端,运行Client节点,同时需要输入加法运算的两个加数值:

$ rosrun learning_communication client 3 5

Client启动后发布服务请求,并成功接收到反馈结果

Server接收到服务调用后完成加法求解,并将结果反馈给Client

wiki

rosservice list     输出可用服务的信息
rosservice call     调用带参数的服务
rosservice type     输出服务类型
rosservice find     依据类型寻找服务find services by service type
rosservice uri     输出服务的ROSRPC uri

以上这篇基于ROS 服务通信模式详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 基于ROS 服务通信模式详解

    ROS 服务通信模式 摘自<ROS机器人开发实践> 服务(services)是节点之间通讯的另一种方式.服务允许节点发送请求(request) 并获得一个响应(response) AddTwoInts.h文件是根据AddTwoInts.srv文件生成的 还会自动生成 AddTwoIntsRequest.h AddTwoIntsResponse.h AddTwoInts.h所在的目录是 \catkin_ws\devel AddTwoInts.srv int64 a int64 b --- int

  • 基于RocketMQ推拉模式详解

    消费者客户端有两种方式从消息中间件获取消息并消费.严格意义上来讲,RocketMQ并没有实现PUSH模式,而是对拉模式进行一层包装,名字虽然是 Push 开头,实际在实现时,使用 Pull 方式实现. 通过 Pull 不断轮询 Broker 获取消息.当不存在新消息时,Broker 会挂起请求,直到有新消息产生,取消挂起,返回新消息. 1.概述 1.1.PULL方式 由消费者客户端主动向消息中间件(MQ消息服务器代理)拉取消息:采用Pull方式,如何设置Pull消息的拉取频率需要重点去考虑,举个

  • 基于RabbitMQ几种Exchange 模式详解

    AMQP协议中的核心思想就是生产者和消费者隔离,生产者从不直接将消息发送给队列.生产者通常不知道是否一个消息会被发送到队列中,只是将消息发送到一个交换机.先由Exchange来接收,然后Exchange按照特定的策略转发到Queue进行存储.同理,消费者也是如此.Exchange 就类似于一个交换机,转发各个消息分发到相应的队列中. RabbitMQ提供了四种Exchange模式:fanout,direct,topic,header . header模式在实际使用中较少,本文只对前三种模式进行比

  • 在Ubuntu上搭建一个基于webrtc的多人视频聊天服务实例代码详解

    WebRTC,即Web Real-Time Communication,web实时通信技术.简单地说就是在web浏览器里面引入实时通信,包括音视频通话等. 在疫情期间哪里也去不了,在家没事就研究webrtc视频直播技术,网上找了些教程最终都不太能顺利跑起来的,可能是文章写的比较老,使用的一些开源组件已经更新了,有些配置已经不太一样了,所以按照以前的步骤会有问题.折腾了一阵终于跑起来了,记录一下. 一个简单的聊天室html页面 这个页面使用simple-webrtc来实现webrtc的通讯,sim

  • 基于RabbitMQ的简单应用(详解)

    虽然后台使用了读写分离技术,能够在一定程度上抗击高并发,但是如果并发量特别巨大时,主数据库不能同时处理高并发的请求,这时数据库容易宕机. 问题: 现在的问题是如何既能保证数据库正常运行,又能实现用户数据的入库操作? 解决方案: 引入rabbitMQ技术: 说明: 当数据库的访问压力过载时,这时会将过载以后的数据先保存到rabbitMQ中.其中的数据结构是队列的形式,先进先出.这时数据库从队列中取数据执行.一直到队列中的数据全部操作完成为止. RabbitMQ就是消息的中间件. RabbitMQ介

  • 基于Jexus-5.6.3使用详解

    一.Jexus Web Server配置 在 jexus 的工作文件夹中(一般是"/usr/jexus")有一个基本的配置文件,文件名是"jws.conf". jws.conf 中至少有 SiteConfigDir 和 SiteLogDir 两行信息: SiteConfigDir=siteconf #指的是存放网站配置文件放在siteconf这个文件夹中,可以使用基于jws.exe文件的相对路径 SiteLogDir=log #指的是jexus日志文件放在log这个

  • 基于JWT.NET的使用(详解)

    JWT是什么 JWT全称是Json Web Token,是一种用于双方之间传递安全信息的简洁的.URL安全的表述性声明规范.JWT作为一个开放的标准( RFC 7519 ),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息.因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名. JWT的结构 JWT一般由三段构成,用.号分隔开,第一段是header,第二段是payload,第三段是signature,例如: eyJ0eX

  • Linux下sshd服务及服务管理命令详解

    sshd SSH为Secure Shell的缩写,是应用层的安全协议.SSH是目前较可靠,专为远程登陆会话和其他网络服务提供安全性的协议.利用SSH协议可以有效防止远程管理过程中的信息泄露问题. openssh-server 功能:让远程主机可以通过网络访问sshd服务,开始一个安全shell 客户端连接方式 ssh 远程主机用户@远程主机ip 先rm -rf /root/.ssh/清掉之前的配置 ssh 远程主机用户@远程主机ip -X 调用远程主机图形工具 ssh 远程主机用户@远程主机ip

  • Go语言中的数据竞争模式详解

    目录 前言 Go在goroutine中通过引用来透明地捕获自由变量 切片会产生难以诊断的数据竞争 并发访问Go内置的.不安全的线程映射会导致频繁的数据竞争 Go开发人员常在pass-by-value时犯错并导致non-trivial的数据竞争 消息传递(通道)和共享内存的混合使用使代码变得复杂且易受数据竞争的影响 Add和Done方法的错误放置会导致数据竞争 并发运行测试会导致产品或测试代码中的数据竞争 小结 前言 本文主要基于在Uber的Go monorepo中发现的各种数据竞争模式,分析了其

  • nginx sticky实现基于cookie负载均衡示例详解

    目录 前言 思考 1.cookie_jsessionid 负载均衡 1.1 后端准备 1.2 hash $cookie_jsessionid;配置 2.nginx sticky 负载均衡 2.1 下载 sticky 2.2 重新编译升级nginx 2.3 upstream 配置 sticky 2.4 修改后端不再创建session 2.5 再次 多次请求 3.sticky 其他用法 总结 前言 sticky 是一个nginx的第三方模块 它不在nginx发行版中 需要额外编译这个模块的, 它的思

随机推荐