文章目录
- Action通信
- 编写Action服务端
- 编写Action客户端
Action通信
Action是ROS中最强大的一种核心通信机制,是ROS2在底层利用多个 Topic 和 Service 组合封装而成,专门用于长耗时、需反馈、可取消的任务。
下面使用ROS2内置的Fibonacci Action 接口进行实战测试。
在具体实现Topic通信的功能之前,先创建工作空间,具体可参考节点通信-创建工作空间。
编写Action服务端
在内层restaurant_robot中编写action_server.py,内容如下
importrclpyfromrclpy.nodeimportNodefromrclpy.actionimportActionServerfromexample_interfaces.actionimportFibonacciimporttimeclassFibonacciActionServer(Node):def__init__(self):super().__init__('fibonacci_action_server')# 创建 Action Server# 参数:节点实例, Action类型, Action名称, 执行回调函数self._action_server=ActionServer(self,Fibonacci,'fibonacci_action',self.execute_callback)self.get_logger().info('Action Server start...')defexecute_callback(self,goal_handle):# goal_handle 包含了客户端发来的目标信息order=goal_handle.request.order self.get_logger().info(f'received goal, calculate the first{order}items')feedback_msg=Fibonacci.Feedback()sequence=[0,1]# 模拟长耗时任务(循环计算)foriinrange(1,order):# 【核心机制 1】:检查客户端是否请求取消ifgoal_handle.is_cancel_requested:goal_handle.canceled()# 标记任务为已取消self.get_logger().info('goal canceled')returnFibonacci.Result()# 返回空结果# 计算下一项sequence.append(sequence[i]+sequence[i-1])feedback_msg.sequence=sequence# 【核心机制 2】:发布实时反馈self.get_logger().info(f'publish feedback{feedback_msg.sequence}')goal_handle.publish_feedback(feedback_msg)# 模拟计算耗时(每秒计算一项)time.sleep(1.0)# 【核心机制 3】:任务完成,标记为成功并返回最终结果goal_handle.succeed()result=Fibonacci.Result()result.sequence=sequence self.get_logger().info('任务完成,返回最终结果')returnresultdefmain(args=None):rclpy.init(args=args)node=FibonacciActionServer()try:rclpy.spin(node)exceptKeyboardInterrupt:passfinally:node.destroy_node()rclpy.shutdown()if__name__=='__main__':main()退回上一级目录,打开 setup.py,在 console_scripts 列表中添加:
'run_action_server = restaurant_robot.action_server:main',接下来在工作空间最上层文件夹编译并运行
colcon build source install/setup.bash ros2 run restaurant_robot run_action_server编写Action客户端
客户端发送目标后,需要注册三个回调函数:处理服务端的“接受/拒绝”响应、处理“实时反馈”、处理“最终结果”。
在同目录下,新建文件 action_client.py:
importrclpyfromrclpy.nodeimportNodefromrclpy.actionimportActionClientfromexample_interfaces.actionimportFibonacciclassFibonacciActionClient(Node):def__init__(self):super().__init__('fibonacci_action_client')# 创建 Action Clientself._action_client=ActionClient(self,Fibonacci,'fibonacci_action')defsend_goal(self,order):goal_msg=Fibonacci.Goal()goal_msg.order=order self.get_logger().info('wwait Action Server...')self._action_client.wait_for_server()self.get_logger().info(f'send goal: calculate the first{order}items')# 异步发送目标,并绑定反馈回调函数self._send_goal_future=self._action_client.send_goal_async(goal_msg,feedback_callback=self.feedback_callback)# 当服务端返回“是否接受目标”的响应时,触发 goal_response_callbackself._send_goal_future.add_done_callback(self.goal_response_callback)# 处理服务端的接受/拒绝响应defgoal_response_callback(self,future):goal_handle=future.result()ifnotgoal_handle.accepted:self.get_logger().error('goal reffused')rclpy.shutdown()returnself.get_logger().info('goal accepted')# 异步请求最终结果,绑定结果回调函数self._get_result_future=goal_handle.get_result_async()self._get_result_future.add_done_callback(self.res_callback)# 处理实时反馈(在任务执行过程中会被多次触发)deffeedback_callback(self,feedback_msg):self.get_logger().info(f'received feedback:{feedback_msg.feedback.sequence}')# 处理最终结果(任务结束时触发一次)defres_callback(self,future):res=future.result().result self.get_logger().info(f'received result:{res.sequence}')rclpy.shutdown()# 任务完成,关闭节点defmain(args=None):rclpy.init(args=args)node=FibonacciActionClient()# 发送目标:计算前 5 项node.send_goal(5)# 保持节点运行,以处理异步回调rclpy.spin(node)if__name__=='__main__':main()同样在setup.py的console_scripyts列表中添加
'run_action_client = restaurant_robot.action_client:main',至此,在两个终端里分别运行服务端和客户端
colcon build source install/setup.bash ros2 run restaurant_robot run_action_client运行结果如下
[INFO][1785120497.952368565][fibonacci_action_client]:waitAction Server...[INFO][1785120498.527255191][fibonacci_action_client]: send goal: calculate the first5iterms[INFO][1785120498.530859965][fibonacci_action_client]: goal accepted[INFO][1785120498.535033490][fibonacci_action_client]: received feedback:array('i',[0,1,1])[INFO][1785120499.540644497][fibonacci_action_client]: received feedback:array('i',[0,1,1,2])[INFO][1785120500.544228609][fibonacci_action_client]: received feedback:array('i',[0,1,1,2,3])[INFO][1785120501.546219522][fibonacci_action_client]: received feedback:array('i',[0,1,1,2,3,5])[INFO][1785120502.549324776][fibonacci_action_client]: receved result: array('i',[0,1,1,2,3,5])