ROS2-服务

在前面学习信息传递时我们用到了话题,其好处是可以适合多个节点相互传输数据、定时周期传输数据等功能,其传递是单向的、异步的

对于一个复杂的机器人系统,我们需要一个双向的、同步的数据传输方法,即服务。

注:一个服务中服务端只能有一个,客户端可以有多个

服务基本代码:从加法请求开始

服务端

在节点中创建一个服务对象:self.serv=self.create_service(AddTwoInts,#{服务名}#{回调函数})

class serviceNode(Node):
    def __init__(self,name):
        super().__init__(name)
        self.serv=self.create_service(AddTwoInts,'service_a',self.serviceCallBack)
    def serviceCallBack(self,request,response):
        response.sum=request.a+request.b
        self.get_logger().info('%d+%d=%d' %(request.a,request.b,response.sum))
        return response
def main(args=None):
    rclpy.init(args=args)
    node=serviceNode('ServiceNode')
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

可以看到,与话题的实现类似,服务的实现通过调用从Node继承而来的方法create_service创建了该节点中作为服务端的实例self.serv。

在方法create_service中,三个参数分别为服务类型、服务名称、回调函数

其中服务名称与话题名称相类似,服务端与客户端需要进行统一。回调函数也与话题中类似,都是代表接到信息后执行的函数

此处AddTwoInts比较特殊,作为接口,它与服务具体执行有关,以后会再做讲解

在该服务的回调函数中,我们的参数在self以外又加了request、response,顾名思义,就是该服务的请求与响应,类似于web开发中的请求体。在这里,serv实例已经为我们编写好了回调函数的参数设置,不需要我们自己进行编写

作为请求响应的实例,我们需要调用request中的变量对response中的对象进行赋值,即语句response.sum=request.a+request.b。返回值中直接将response实例返回即可,ros2已经为我们准备好了如何将其带给请求体

客户端

客户端需要完成的事项是将数据打包发送给服务端,并等待其返回数据。具体实现代码如下:

class clientNode(Node):
    def __init__(self,name):
        super().__init__(name)
        self.clie=self.create_client(AddTwoInts,'service_a')
        while (not self.clie.wait_for_service(timeout_sec=1)):
            self.get_logger().info('error! failed to find service')
        self.request=AddTwoInts.Request()
    def sendRequest(self):
        self.request.a=1
        self.request.b=2
        self.future=self.clie.call_async(self.request)
def main(args=None):
    rclpy.init(args=args)
    node=clientNode('ClientNode')
    node.sendRequest()
    while(rclpy.ok()):
        rclpy.spin_once(node)
        if node.future.done():
            try:
                response=node.future.result()
            except Exception as err:
                node.get_logger().info('error! can not call service!'+err)
            else:
                node.get_logger().info('get Result:%d' % response.sum)
    node.destroy_node()
    rclpy.shutdown()

这段代码中,我们创建了作为客户端的类clientNode,在这个节点类中的构造函数中,我们创建节点的客户端(参数同服务端)

while (not self.client.wait_for_service(timeout_sec=1)):
            self.get_logger().info('error! failed to find service')

该语句调用node父类继承得到的client类中wait_for_service函数来判断是否连接到服务端,等待时间为1秒。当1秒后仍未连接到服务端时执行报错代码

最后在构造函数中声明了一个AddTwoInts接口的request实现类,作为请求体填充数据后向服务端进行请求

在sendRequest函数中,我们首先在构建函数中声明的请求体中进行赋值,声明了该请求体中的两个变量a和b,使用声明的clie请求类中方法call_async,以request类作为参数,将请求赋值给变量future,后续我们会调用future实例进行对结果的询问

在主函数中,我们在正常的执行语句创建对象、调用方法sendRequest发送请求后开始试图收到response体。

在这里,我们的编程思维是:反复寻找response,直到找到响应后查看其结果,若结果符合要求,则将其打印。

实际实现过程中,我们调用spin_once函数执行一次node节点(可以理解为刷新),后调用future请求类中的done方法查看是否完成,若未完成重复执行node节点,若已完成尝试调用future请求类中的result方法将得到的值赋值给response,如果在这步出现错误则返回错误,否则直接将被赋值的变量打印即可。

接口

我们在上述的代码中使用到了AddTwoInt接口,这个接口描述的是该服务的过程,现在我们需要对该接口进行配置。

建立AddTwoInts.srv文件,在其中输入以下代码

int64 a
int64 b
---
int64 sum

这段接口是写给服务的,在服务类接口中我们的请求和响应用"---"区分,以上为请求,以下为响应。

在这里我们的请求是a和b,代表两个数,对应客户端中的代码

self.request=AddTwoInts.Request()
self.request.a=1
self.request.b=2

而响应是sum,对应服务端中的代码

response.sum=request.a+request.b

需要注意的是,在完成以上操作后,计算机并不能直接识别我们所写出的接口文档,需要将其编译为代码,在这里ros2已经帮我们完成了这一过程,我们需要做的只是在CMakeLists.txt文档中修改成对应位置如下代码:

rosidl_generate_interfaces(${PROJECT_NAME}
  "AddTwoInts.srv"
 )

注:引号中写的实际上是该接口文件的相对路径,若在文件夹中需要描述其文件夹以及文件名,形如srv/AddTwoInts.srv

另外,在使用到该接口的文件中,需要用from#{目录}import#{接口名}语句引入该接口

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值