ROS入门学习6:理解服务中Server与Client(Python)

  服务(Services)是ROS中一种同步通信模式,其中消息以请求/应答(Request/Reply)的方式传递。

  • 客户端(Client):请求服务后等待响应。
  • 服务器(Server): 收到服务请求后执行指定的任务,并发送响应。
  • 关系:Client-Server 多对一

  与话题不同,ROS中只允许有一个节点提供服务,但可以存在多个客户端。与话题不同,服务只连接一次,在执行请求和响应之后彼此断开连接。如果有必要,需要重新连接。

  例如,当客户端向服务器请求当前时间时,服务器将确认时间并作出响应。

(图)服务通信模式

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

服务

本例程放在ros_learning功能包script文件夹下,新建ros_learning功能包(如果已经创建,可以跳过):

$ cs
$ catkin_create_pkg ros_learning std_msgs rospy roscpp
$ cm

新建自定义服务

自定义服务消息数据格式一般为srv文件,如此次需要创建AddTwoInts.srv文件,一般放在功能包目录下srv文件夹下。

路径:catkin_ws/src/ros_learning/srv/AddTwoInts.srv

AddTwoInts.srv
1
2
3
4
int64 a
int64 b
---
int64 sum

注:数据域中的内容,请求与应答数据格式之间需要使用---分割。

修改package.xml和CMakeLists.txt

  • 修改package.xml文件,添加以下内容
package.xml
1
2
3
<build_depend>message_generation</build_depend>
<build_export_depend>message_generation</build_export_depend>
<exec_depend>message_runtime</exec_depend>
  • 修改CMakeLists.txt文件,添加以下内容
CMakeLists.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)

## Generate services in the 'srv' folder
add_service_files(
FILES
AddTwoInts.srv
)

## Generate added messages and services with any dependencies listed here
generate_messages(
DEPENDENCIES
std_msgs
)

catkin_package(
# INCLUDE_DIRS include
# LIBRARIES ros_learning
CATKIN_DEPENDS roscpp rospy std_msgs message_generation
# DEPENDS system_lib
)

创建客户端(Client)

路径:catkin_ws/src/ros_learning/script/client.py

client.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# 客户端clint 主动发送请求数据 如:rosrun ros_learning client.py 4 5

import sys
import rospy
from ros_learning.srv import * # 自定义服务数据

def add_two_ints_client(x, y):
# 客户端调用服务更简单,甚至可以不调用init_node()来初始化节点,直到服务名为add_two_ints的服务生效
rospy.wait_for_service('add_two_ints')
try:
add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts) # 创建实例来调用服务
resp1 = add_two_ints(x, y) # 因为我们已声明服务类型AddTwoInts,它生成AddTwoIntsRequest对象
return resp1.sum #返回值是一个AddTwoIntsResponse对象。
except rospy.ServiceException, e:
print "Service call failed: %s"%e

def usage():
return "%s [x y]"%sys.argv[0] # 异常返回 如:/home/tao/catkin_ws/src/ros_learning/src/client.py [x y]

if __name__ == "__main__":
if len(sys.argv) == 3:
x = int(sys.argv[1])
y = int(sys.argv[2])
else:
print usage()
sys.exit(1)
print "Requesting %s+%s"%(x, y)
print "%s + %s = %s"%(x, y, add_two_ints_client(x, y))

创建客户端完成后,使用命令sudo chmod +x XXX.py为Python文件添加可执行权限:

tao@ubuntu:~/catkin_ws/src/ros_learning/script$ sudo chmod +x client.py

创建服务器(Server)

路径:catkin_ws/src/ros_learning/script/server.py

server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from ros_learning.srv import *
import rospy

# 所有请求传递到handle_add_two_ints函数处理,并传递实例AddTwoIntsRequest和返回AddTwoIntsResponse实例。
def handle_add_two_ints(req):
print "Returning [%s + %s = %s]"%(req.a, req.b, (req.a + req.b))
return AddTwoIntsResponse(req.a + req.b)

def add_two_ints_server():
rospy.init_node('add_two_ints_server') # 初始化节点
s = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints) #声明一个名为add_two_ints新服务,使用AddTwoInts服务类型。
print "Ready to add two ints."
rospy.spin() # 类似订阅实例,rospy.spin()会保持代码不退出,直到服务关闭

if __name__ == "__main__":
add_two_ints_server()

创建服务器完成后,使用命令sudo chmod +x XXX.py为Python文件添加可执行权限:

tao@ubuntu:~/catkin_ws/src/ros_learning/script$ sudo chmod +x server.py

运行测试

  • 打开新终端,编译功能包,启动ROS:
$ cm
$ roscore
  • 打开新终端,运行服务器,可以看出服务在等待客户端请求:
$ rosrun ros_learning subscriber.py
(图)服务器(Server)
  • 打开新终端,运行客户端,可以看出,当发出两个整数相加请求后,服务器计算并返回所求的和:
$ rosrun ros_learning subscriber.py
(图)客户端(Client)

到此,已经简单了解一下服务的通信模式。

+