ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

ROS基础(二):ros通讯之服务(service)机制

2021-01-09 21:57:20  阅读:1594  来源: 互联网

标签:ROS service client srv rospy ros 节点


ROS基础(一):ROS通讯之话题(topic)通讯

目录

一、概念

service服务通讯机制是一种双向同步数据传输模式。基于客户端/服务器模型,两部分通信数据类型:一个用于请求,一个用于应答,类似web服务器。
ROS中只允许一个节点提供指定命名的服务。

与topic通讯机制的区别:

比较列表话题topic服务service
同步性异步同步
通信模型发布+订阅客户端+服务器端
反馈机制
缓冲区
节点关系多对多一(server)对多
传输数据格式*.msg*.srv
适合场景数据传输逻辑处理

二、实例

1. 小乌龟例程中的service

首先启动第一个小乌龟:

rosrun turtlesim turtlesim_node

然后查看能够调用的所有service:
在这里插入图片描述
调用其中名为/spawn的服务:
在这里插入图片描述

如上图红框部分所示,服务器返回指定生成的乌龟名字。同时,在小乌龟仿真中在指定位置出现第二只名为turtle7的小乌龟,如下图所示。这乌龟可以的,还在听音乐。

在这里插入图片描述
最后,我们查看该service的具体数据格式:

在这里插入图片描述
与我们的预期一致,—上方是client请求发出的内容,其中包括指定新生成乌龟的位置、朝向、名字,—下方是server返回的内容,string格式的乌龟名字,说明创建成功。

2. 自定义service

与topic话题中需要定义的message文件相似,当我们想要自定义某种服务时,需要提供对应的srv文件来对两个交互节点中具体传输的数据格式进行约束。

这里我们创建一个乘法运算案例,客户端提供两个整数a、b,服务器端计算完成后返回乘法结果到客户端。
将该srv文件命名为multinum.srv,该文件中的内容如下:

float32 a
float32 b
---
float64 c

将该文件放在名为srv的文件夹下,将文件夹放在功能包目录下。最终的功能包目录如下所示:

在这里插入图片描述

与定义message一样,我们同样要修改对应的package.xml文件和CmakeLists.txt文件的对应部分:

package.xml文件:

  <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>

CmakeLists.txt文件:

第一块:修改find_package部分,确保编译时找到对应文件


find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)

第二块:设置srv文件

 add_service_files(
   FILES
   multinum.srv
 )

 generate_messages(
   DEPENDENCIES
   std_msgs
 )

第三块:catkin依赖部分

如果我们编写的ros程序不打算给别人使用,这块就无所谓。

catkin_package(
...
  CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
...
)

最后,在工作空间编译,通过相关命令测试是否已经安装成功:
在这里插入图片描述如上图所示,我们的service服务查询得到,没问题。

3. 创建服务器节点与客户端节点(c++)

我们利用第二小节中自定义的service格式,来设计两个node进行service通讯。主要功能是client随机生成两个数字传递给service,然后service计算完成之后,将结果回传给client。具体的代码如下所示

server节点编写

#include <ros/ros.h>
#include <learn_service/multinum.h>

bool multi_callback(learn_service::multinum::Request &req, learn_service::multinum::Response &res){
    res.c = req.a * req.b;
    ROS_INFO("computing result is %f", res.c);
    return true;
}

int main(int argc, char **argv){
    ros::init(argc, argv, "multi_server");
    ros::NodeHandle nh;
    ros::ServiceServer server = nh.advertiseService("multi_two_num",multi_callback);
    ROS_INFO("waiting two numbers...");
    ros::spin();
    return 0;
}

client节点编写


#include <ros/ros.h>
#include <learn_service/multinum.h>
#include<cstdlib>
#include <time.h>
int main(int argc, char **argv){
    srand(int(time(0)));
    double a = rand()/(double(RAND_MAX)/100);
    double b = rand()/(double(RAND_MAX)/100);
    ros::init(argc, argv, "multi_client");
    ros::NodeHandle nh;
    ros::ServiceClient client = nh.serviceClient<learn_service::multinum>("multi_two_num");

    learn_service::multinum srv;
    srv.request.a = a;
    srv.request.b = b;

    if (client.call(srv)){
        ROS_INFO("%f * %f = %f", a, b, srv.response.c);
    }else{
        ROS_ERROR("Failed to call service");
    }
    return 0;
}

运行结果

首先我们将服务器节点开启,然后需要计算的时候调用client节点,服务器计算好结果之后回传给client。具体效果如下所示:
在这里插入图片描述

4. 创建服务器节点与客户端节点(python)

同样的东西,我们用python再实现一次。

server节点编写


#!/usr/bin/env python
# coding:utf-8

import rospy
from learn_service.srv import *

def callback_func(req):
    answer = req.a*req.b
    rospy.loginfo("result is %f", answer)
    return multinumResponse(answer)


def server():
    rospy.init_node("python_server")
    s = rospy.Service("multi_2num",multinum,callback_func)
    rospy.loginfo("Waiting 2 numbers...")
    rospy.spin()

if __name__=="__main__":
    server()

重点:

  1. 启动服务的函数接口:rospy.Service("service名", srv数据类型, 回调函数)
  2. 回调函数中传入的是请求request,返回的是response
  3. 返回的数据类型是自定义的,比如上例中,我们自定义的srv数据类型是multinum,那么对应的response数据类型为multinumResponse

client节点编写


#!/usr/bin/env python
# coding:utf-8

import rospy
from learn_service.srv import *
import random

def client():
    rospy.init_node("python_client")
    rospy.wait_for_service("multi_2num")
    try:
        c = rospy.ServiceProxy("multi_2num",multinum)
        a = random.random() * 100
        b = random.random() * 100
        response = c.call(a,b)

        rospy.loginfo("%f * %f = %f", a, b, response.c)
    except rospy.ServiceException, e:
        rospy.logerr("service call failed: %s", e)

if __name__=="__main__":
    client()

重点:

  1. rospy.wait_for_service("service名称")可以使得该client_node节点直到对应service服务器开始工作之后,代码才继续往下运行
  2. 启动client的函数接口:rospy.ServiceProxy("service名称",自定义srv数据类型)
  3. 向服务器发送请求使用2步骤中返回类的call函数:response = client.call(srv中request相关数据),call函数的参数为request的具体参数,本例中就是要乘的a,b两数;或者为对应的request数据格式,本例为multinumRequest(a,b)

运行结果

如下所示,跟预期一致,没有问题。
在这里插入图片描述

三、总结

到此,我们用两章的内容,基本涵盖ros通讯中的两大最常用的方式。ros还有一种action通讯方式,平时基本用不到。所以接下来教程也不会涉及到这部分内容。

下一章,我们来一起探索ros中的tf坐标变换。

标签:ROS,service,client,srv,rospy,ros,节点
来源: https://blog.csdn.net/allenhsu6/article/details/112384549

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有