前言:
动作使用话题实现,其本质上相当于一个规定了一系列话题(目标、结果、反馈等)的组合使用方法的高层协议。相较于服务的同步通信机制,动作机制则是异步的。动作适用于大部分请求/响应式交互场景,尤其是执行过程不能立即完成。
catkin_ws工作空间下my_code程序包里的文件目录结构:
1.定义一个动作服务器:
#! /usr/bin/env python
import rospy
import time
import actionlib
from my_code.msg import TimerAction, TimerGoal, TimerResult, TimerFeedback
def do_timer(goal):
start_time = time.time()
#统计反馈信息的次数
update_count = 0
#请求时长大于60s,终止当前目标
if goal.time_to_wait.to_sec() > 60.0:
result = TimerResult() #TimerResult变量声明
result.time_elapsed = rospy.Duration.from_sec(time.time() - start_time) #time_elapsed属性赋值
result.updates_sent = update_count #updates_sent属性赋值
server.set_aborted(result, "Timer aborted due to too-long wait") #向客户端发送终止结果
return
while (time.time() - start_time) < goal.time_to_wait.to_sec():
#判断是否发生终端
if server.is_preempt_requested():
result = TimerResult()
#time_elapsed需从秒转换为ROS的Duration类型
result.time_elapsed = rospy.Duration.from_sec(time.time() - start_time)
result.updates_sent = update_count
server.set_preempted(result, "Timer preempted")
return
#反馈信息给客户端
feedback = TimerFeedback() #TimerFeedback变量声明
feedback.time_elapsed = rospy.Duration.from_sec(time.time() - start_time)
feedback.time_remaining = goal.time_to_wait - feedback.time_elapsed #time_remaining属性赋值
server.publish_feedback(feedback) #向客户端发送反馈信息
update_count += 1
time.sleep(1.0)
result = TimerResult()
result.time_elapsed = rospy.Duration.from_sec(time.time() - start_time)
result.updates_sent = update_count
server.set_succeeded(result, "Timer completed successfully")
rospy.init_node('timer_action_server') #节点初始化
#创建动作服务器(名称、动作类型、回调函数、False:关闭动作服务器的自动启动)
server = actionlib.SimpleActionServer('timer', TimerAction, do_timer, False)
server.start()
rospy.spin()
这里使用自定义服务类型Timer.action
2.定义一个动作客户端:
#! /usr/bin/env python
import rospy
import time
import actionlib
from my_code.msg import TimerAction, TimerGoal, TimerResult, TimerFeedback
#定义一个回调函数,收到反馈时执行
def feedback_cb(feedback):
print('[Feedback] Time elapsed: %f'%(feedback.time_elapsed.to_sec()))
print('[Feedback] Time remaining: %f'%(feedback.time_remaining.to_sec()))
rospy.init_node('timer_action_client') #节点初始化
client = actionlib.SimpleActionClient('timer', TimerAction) #创建一个动作客户端(名称、动作类型)
client.wait_for_server() #等待服务器启动
goal = TimerGoal() #TimeGoal变量声明
goal.time_to_wait = rospy.Duration.from_sec(5.0) #time_to_wait属性赋值
#用于测试服务端的等待时常大于60s的终止请求
#goal.time_to_wait = rospy.Duration.from_sec(500.0)
#将回调函数作为feedback_cb关键词的参数,传入send_goal
client.send_goal(goal, feedback_cb=feedback_cb)
#用于测试服务端中断
#time.sleep(3.0)
#client.cancel_goal()
client.wait_for_result()
print('[Result] State: %d'%(client.get_state()))
print('[Result] Status: %s'%(client.get_goal_status_text()))
print('[Result] Time elapsed: %f'%(client.get_result().time_elapsed.to_sec()))
print('[Result] Updates sent: %d'%(client.get_result().updates_sent))
3.运行服务端节点:
增加fancy_action_server.py可执行权限:
chmod +x fancy_action_server.py
运行ros:
roscore
新开一个终端,启动服务器节点:(my_code为ROS程序包名)
rosrun my_code fancy_action_server.py
4.运行客户端节点:
再打开另一个终端,增加service_client.py可执行权限:
chmod +x fancy_action_client.py
启动客户端节点:
rosrun my_code fancy_action_client.py
5.运行效果:
取消终止注释:
goal.time_to_wait = rospy.Duration.from_sec(500.0)
取消中断注释:
time.sleep(3.0)
client.cancel_goal()