ROS入门学习7:理解动作中Server与Client(Python)

  动作(Action)是ROS中一种同步通信模式,与服务非常相似,服务具有与请求和响应分别对应的目标(goal)和结果(result),除此之外动作中还多了反馈(feedback)。与服务不同,动作通常用于指导复杂的机器人任务,例如发送一个目标值之后,还可以在任意时刻发送取消目标的命令。ROS中已经包含actionlib功能包,用于实现动作通信机制。

  • 客户端(Client):用于请求发布任务目标(goal)或取消任务(cancel)。
  • 服务器(Server):用于通知客户端当前状态(status)、周期性反馈任务运行进度(feedback)以及发送任务执行结果(result)。结果只发送一次。
  • 关系:Client-Server 多对一

  例如,当客户端将家庭服务器设置为服务器时,服务器会时时地通知客户端洗碗、洗衣和清洁等进度,最后将结果值发送给客户端。

(图)动作通信模式

  消息传输方案本身与异步方式的话题(topic)相同。反馈在动作客户端(action client)和动作服务器(action server)之间执行异步双向消息通信,其中动作客户端设置动作目标(goal),而动作服务器根据目标执行指定的工作,并将动作反馈和动作结果发送给动作客户端。

动作

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

$ cs
$ catkin_create_pkg ros_learning std_msgs rospy roscpp
$ cm

新建自定义动作

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

路径:catkin_ws/src/ros_learning/action/DoDishes.action

DoDishes.action
1
2
3
4
5
6
7
8
# 定义目标
uint32 dish_goal
---
# 定义结果
uint32 dish_result
---
# 定义反馈消息
uint32 dish_progress

注:数据域中的内容,目标、结果和反馈消息数据格式之间需要使用---分割。

修改package.xml和CMakeLists.txt

  • 修改package.xml文件,添加以下内容
package.xml
1
2
3
4
5
6
<build_depend>actionlib_msgs</build_depend>
<build_depend>actionlib</build_depend>
<build_export_depend>actionlib_msgs</build_export_depend>
<build_export_depend>actionlib</build_export_depend>
<exec_depend>actionlib_msgs</exec_depend>
<exec_depend>actionlib</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
29
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
genmsg
actionlib_msgs
actionlib
)

## Generate actions in the 'action' folder
add_action_files(
FILES
DoDishes.action
)

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

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

创建动作客户端(Action Client)

可参考 Full API reference for the Python SimpleActionClient

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

dodishes_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
32
33
34
35
36
37
38
39
40
41
42
43
#! /usr/bin/env python
# -*- coding: utf-8 -*-

import roslib
roslib.load_manifest('ros_learning')
import rospy
import actionlib

from ros_learning.msg import DoDishesAction, DoDishesGoal, DoDishesFeedback, DoDishesResult

#激活动作回调
def active():
rospy.loginfo("开始洗盘子....")

#动作服务完成回调
def done(d,result):
rospy.loginfo("已经洗完[%d]个盘子"%result.dish_result)

#中间过程回调
def feedback(msg):
rospy.loginfo("当前进度[%d]"%msg.dish_progress)

def client():
rospy.init_node('do_dishes_client') # 初始化节点
action_client = actionlib.SimpleActionClient('do_dishes', DoDishesAction) # 初始化动作客户端

rospy.loginfo("连接洗盘子服务器")
action_client.wait_for_server() # 等待动作服务
rospy.loginfo("已连接,发送任务目标")

goal = DoDishesGoal() # 创建一个动作的任务目标
goal.dish_goal= 100 # 设置任务目标
rospy.loginfo("任务目标[%d]."%goal.dish_goal) #打印任务目标

action_client.send_goal(goal,done,active,feedback) #申请服务
rospy.spin() #循环等待


if __name__ == '__main__':
try:
client()
except rospy.ROSInterruptException:
pass

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

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

创建动作服务器(Action Server)

可参考 Full API reference for the Python SimpleActionServer

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

dodishes_server.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
32
33
34
35
36
37
38
#! /usr/bin/env python
# -*- coding: utf-8 -*-

import roslib
roslib.load_manifest('ros_learning')
import rospy
import actionlib
import time

from ros_learning.msg import DoDishesAction, DoDishesGoal, DoDishesFeedback, DoDishesResult

def execute(goal):
feedback = DoDishesFeedback() # 反馈进度
result = DoDishesResult() # 最终结果
rospy.loginfo("已收到任务目标[%d]."%goal.dish_goal) #打印任务目标

rate = rospy.Rate(10) # 10hz 每秒10次
num_goal = goal.dish_goal # 任务目标
num = 0 # 临时变量
num_add = 5 # 增量

while(True):
time.sleep(0.2) # 模拟耗时
num += num_add # 模拟进度
feedback.dish_progress = num #更新反馈进度
if num == num_goal:
result.dish_result = num #给最终结果赋值
break
action_server.publish_feedback(feedback) #反馈任务进度

rospy.loginfo("已经洗完[%d]个盘子"%result.dish_result) #打印最终结果
action_server.set_succeeded(result) #反馈最终结果

if __name__ == '__main__':
rospy.init_node('do_dishes_server') # 初始化节点
action_server=actionlib.SimpleActionServer("do_dishes",DoDishesAction,execute,auto_start=False) # 初始化动作服务,动作名DoDishesAction,回调函数execute
action_server.start() #启动动作服务
rospy.spin() #循环等待

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

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

运行测试

  • 打开新终端,编译功能包,启动ROS:
$ cm
$ roscore
  • 打开新终端,运行动作服务器,可以看出服务在等待动作客户端请求:
$ rosrun ros_learning dodishes_server.py
(图)动作服务器(Server)
  • 打开新终端,运行动作客户端,可以看出,当发出任务目标请求后,动作服务器周期反馈进度和任务结果:
$ rosrun ros_learning dodishes_client.py
(图)动作客户端(Server)
  • 查看话题列表rostopic list和节点列表rosnode list
tao@ubuntu:~$ rostopic list
/do_dishes/cancel
/do_dishes/feedback
/do_dishes/goal
/do_dishes/result
/do_dishes/status
/rosout
/rosout_agg
tao@ubuntu:~$ rosnode list
/do_dishes_client
/do_dishes_server
/rosout
  • 查看节点关系rqt_graph
(图)动作节点关系(Server)

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

+