ROS与Python入门教程-深入服务

**说明

本节深入介绍服务定义,请求信息和响应信息

服务定义,请求信息和响应信息

服务是通过srv文件定义,它包含请求信息和响应信息

这些都是用与ROS的主题信息相同,参考主题信息

rospy将这些SRV文件转换成Python源代码并创建三个类:服务的定义,请求消息和响应消息。

这些类的名称直接来自SRV文件名:

my_package/srv/Foo.srv → my_package.srv.Foo

my_package/srv/Foo.srv → my_package.srv.FooRequest

my_package/srv/Foo.srv → my_package.srv.FooResponse

服务定义

服务是个容器,包含请求信息和响应信息。创建和调用服务都要用到

您需要导入服务定义并传递给适当的服务初始化方法:

add_two_ints = rospy.ServiceProxy('service_name', my_package.srv.Foo)

服务的请求信息

请求消息被用来调用适当的服务。你通常不需要直接使用这些,因为rospy的服务调用方式(稍后介绍)允许你绕过直接使用它们,但在有些情况下,您可能希望使用这些信息。

服务的响应信息

响应消息用于包含适当的服务的返回值。服务处理程序必须返回正确类型的响应消息实例。

Service proxies(服务代理)

通过 rospy.ServiceProxy函数指定服务名来调用服务。

通过rospy.wait_for_service()函数来阻塞直到服务有效。

如果当请求服务时发生错误, 就会抛出rospy.ServiceException异常内容。它包含错误所有的信息。

示例代码:

rospy.wait_for_service('add_two_ints')

add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts)

try:

resp1 = add_two_ints(x, y)

except rospy.ServiceException as exc:

print("Service did not process request: " + str(exc))

服务调用

rospy.serviceproxy实例可被调用,这意味着你可以调用它们就像跟方法一样

例如:

add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts)

add_two_ints(1, 2)

有三种方式传递参数:

显式:创建*Request实例传递即可

req = rospy_tutorials.srv.AddTwoIntsRequest(1, 2)

resp = add_two_ints(req)

有循序的隐式 :创建Message实例需带有参数,参数的循序和信息字段的循序一样。

如rospy_tutorials.srv.AddTwoIntsRequest有两个整型字段

resp = add_two_ints(1, 2)

带关键词的隐式:只需初始化有值的关键词,剩下的使用默认。

如rospy_tutorials.srv.AddTwoIntsRequest有两个整型字段:a和b

resp = add_two_ints(a=1)

三种异常

TypeError,请求不是有效的类型

ServiceException ,与远程服务通讯失败

ROSSerializationException,这通常表示一个字段中的一个类型错误。

Persistent connections(持久连接)

ROS允许持久连接到服务,在持久连接下,客户端会一直保持连接,否则每次都需要查找和重连。

假设每次查找都是不同节点,即调用可能连接不同的节点

需要谨慎使用持久连接,它大大提高了重复请求的性能,但他们也使您的客户端更脆弱容易出现服务失败。使用持久连接的客户端应该在持久连接失败的事件中实现自己的重连逻辑。

创建持久连接使用 persistent=True,例如:

rospy.ServiceProxy(name, service_class, persistent=True)

调用close()去关闭持久连接。

Providing services(提供服务)

通过创建rospy.Service实例并指定回调函数来提供服务,但新请求到达就会触发回调函数处理。

每个请求都会有独立线程,所以服务必定是线程安全的。

函数定义:rospy.Service(name, service_class, handler, buff_size=65536)

name, 服务名称

service_class,是服务类型(自动生成服务类)

handler,回调函数,请求到达就触发调用。

buff_size,缓冲区大小

示例代码:

def add_two_ints(req):

return rospy_tutorials.srv.AddTwoIntsResponse(req.a + req.b)

def add_two_ints_server():

rospy.init_node('add_two_ints_server')

s = rospy.Service('add_two_ints', rospy_tutorials.srv.AddTwoInts, add_two_ints)

rospy.spin()

您也可以简化服务处理程序的返回值,这样您就不必手动创建响应实例了。

从处理程序返回类型的有效组:

None (failure)

ServiceResponse (see above)

tuple or list

dict

处理器能返回tuple或list来创建响应实例。

def add_two_ints(req):

return [req.a + req.b]

AddTwoIntsResponse只取单一参数,简化为返回和值:

def add_two_ints(req):

return req.a + req.b

处理器同样能返回带关键词参数的字典来创建响应对象,例如:

def add_two_ints(req):

return {'sum': req.a + req.b}

(Waiting for) shutdown(关闭或等待关闭)

有两个常用的方法关闭服务:

调用shutdown() 函数

调用spin()函数,有两个spin()函数可调用,服务的和节点的。

调用shutdown():

s = rospy.Service('add_two_ints', rospy_tutorials.srv.AddTwoInts, add_two_ints)

...

s.shutdown('shutdown reason')

调用spin():

s = rospy.Service('add_two_ints', rospy_tutorials.srv.AddTwoInts, add_two_ints)

s.spin() #returns when either service or node is shutdown

Service connection headers(服务连接头信息)

连接头功能可用于服务和主题。

两个节点之间的初始连接,可发送额外的元数据。

ROS使用连接头传递基本信息如客户连接的callerid。

在服务,这个功能可自定义执行高级特性,比如sessions(i.e. cookies)

服务客户端可以传递自己的元数据,识别相关的请求。

在客户端,ServiceProxy传递额外的headers参数,这个参数是key和value对的字典。

定义:rospy.ServiceProxy(name, service_class, headers=header_dictionary),例如:

h = { 'cookies' : 'peanut butter' }

s = rospy.ServiceProxy('foo', Foo, headers=h)

在服务端,可通过_connection_header字段获取请求信息。

定义:request._connection_header, 例如:

def add_two_ints(req):

who = req._connection_header['callerid']

if 'cookies' in req._connection_header:

cookies = req._connection_header['cookies']

return AddTwoIntsResponse(req.a + req.b)

def add_two_ints_server():

rospy.init_node(NAME)

s = rospy.Service('add_two_ints', AddTwoInts, add_two_ints)