资讯动态

ROS2 Humble 实战(三)动作 Action:从概念到代码的完整行为管理

发布时间:2026/8/19 23:03:51 来源:尧图企业网站定制
1. 为什么需要动作Action想象一下你正在指挥一个机器人完成从仓库取货并送到前台的任务。如果用话题Topic实现机器人会不断发布自己的位置信息但你需要自己写代码判断是否到达仓库、是否拿到货物、是否返回前台——相当于你既要当指挥官又要当监工。如果用服务Service实现机器人会在完成任务后一次性告诉你结果但如果它卡在半路你根本不知道发生了什么。这就是动作Action要解决的问题。我在实际项目中发现动作特别适合这三种场景长时间任务比如机械臂完成10分钟的装配流程需要中途干预比如移动机器人导航时突然需要紧急停止实时进度反馈比如显示机械臂当前完成了百分之多少举个真实案例我们曾用纯话题实现机械臂控制结果代码里塞满了状态判断if arm_position pickup_point and not is_holding: publish_grasp_command() elif arm_position delivery_point and is_holding: publish_release_command() ...改用动作后代码清爽得像换了套架构goal PickAndPlace.Goal() goal.object_id A001 action_client.send_goal(goal)2. 动作的三大核心要素2.1 目标Goal就像给外卖小哥的订单需要明确告诉机器人要做什么。在ROS2中Goal用.action文件定义# MoveToPosition.action # Goal定义 float32 x # 目标位置X坐标 float32 y # 目标位置Y坐标 --- # Result定义 bool success # 是否成功到达 string message --- # Feedback定义 float32 distance_remaining # 剩余距离2.2 反馈Feedback相当于外卖小哥实时推送的已取餐、还有500米到达。这是动作最实用的特性def feedback_callback(feedback): print(f剩余距离: {feedback.distance_remaining:.2f}米) if feedback.distance_remaining 0.5: play_sound(即将到达.mp3)2.3 结果Result最终交付的餐品。与服务的Response不同动作结果可能包含更丰富的执行信息def result_callback(future): result future.result() if not result.success: send_alert(f送货失败: {result.message})3. 从零实现动作服务3.1 创建动作接口在功能包的action目录新建MoveToPosition.action文件后需要在CMakeLists.txt添加find_package(rosidl_default_generators REQUIRED) rosidl_generate_interfaces(${PROJECT_NAME} action/MoveToPosition.action )3.2 动作服务端实现核心是继承ActionServer并实现回调class MoveServer(Node): def __init__(self): super().__init__(move_server) self._action_server ActionServer( self, MoveToPosition, move_to_position, self.execute_callback) async def execute_callback(self, goal_handle): # 验证目标有效性 if goal_handle.request.x 100: goal_handle.abort() return MoveToPosition.Result(successFalse) # 执行移动 feedback MoveToPosition.Feedback() while not reached_target: move_one_step() feedback.distance_remaining calculate_distance() goal_handle.publish_feedback(feedback) await asyncio.sleep(0.1) # 返回结果 goal_handle.succeed() return MoveToPosition.Result(successTrue)3.3 动作客户端实现关键点在于处理异步响应class MoveClient(Node): def send_goal(self, x, y): goal MoveToPosition.Goal() goal.x x goal.y y self._client.wait_for_server() send_goal_future self._client.send_goal_async( goal, feedback_callbackself.feedback_received) send_goal_future.add_done_callback(self.goal_response_callback) def goal_response_callback(self, future): goal_handle future.result() if not goal_handle.accepted: print(目标被拒绝) return print(开始移动) get_result_future goal_handle.get_result_async() get_result_future.add_done_callback(self.get_result_callback)4. 实战移动机器人拍照任务我们实现一个完整案例控制TurtleBot3移动到指定位置后拍照。4.1 动作定义# PhotoShoot.action # Goal float32 target_x float32 target_y --- # Result bool success sensor_msgs/Image captured_image --- # Feedback string current_state # moving/positioning/shooting float32 progress4.2 服务端关键逻辑async def execute_callback(self, goal_handle): # 阶段1移动 self._publish_feedback(moving, 0.3) await self._move_to(goal_handle.request.target_x, goal_handle.request.target_y) # 阶段2调整姿态 self._publish_feedback(positioning, 0.6) await self._adjust_pose() # 阶段3拍照 self._publish_feedback(shooting, 0.9) image self._capture_image() # 返回结果 result PhotoShoot.Result() result.success True result.captured_image image goal_handle.succeed() return result4.3 客户端调用示例def main(): client PhotoShootClient() client.send_goal(x2.5, y3.0) # 非阻塞式等待 while not client.task_done: do_other_work() time.sleep(0.1) if client.result.success: save_image(client.result.captured_image)5. 调试技巧与常见问题5.1 动作命令工具ROS2提供了强大的命令行工具# 列出所有可用动作 ros2 action list # 查看动作详情 ros2 action info /move_to_position # 发送动作目标 ros2 action send_goal /move_to_position my_robot/action/MoveToPosition {x: 1.5, y: 2.0} --feedback5.2 常见错误排查动作服务器未启动# 检查服务是否注册 ros2 node info /move_server接口类型不匹配# 确认接口包名一致 from my_robot.action import MoveToPosition # 注意包名反馈频率过高# 控制反馈频率 if time.time() - last_feedback 0.2: publish_feedback()6. 动作与话题/服务的对比通过实际测试数据对比三种通信方式特性话题(Topic)服务(Service)动作(Action)执行时间无限制通常1秒分钟级反馈机制需要自行实现无内置反馈可中断性不可控不可中断支持预取消代码复杂度高(需状态管理)低中(自动状态机)适合场景实时数据流快速查询/操作长时间任务在机械臂控制项目中我们重构前后的对比旧方案(话题服务)2000行代码存在15个状态标志新方案(动作)600行代码状态机由ROS2自动维护7. 高级应用技巧7.1 动作组合实现取货-送货-返回的复合动作async def delivery_sequence(): # 取货 pick_result await pick_client.send_goal_async(pick_goal) if not pick_result.success: return False # 送货 deliver_result await deliver_client.send_goal_async(deliver_goal) # 返回 await return_client.send_goal_async(return_goal) return deliver_result.success7.2 超时处理try: await asyncio.wait_for( action_client.send_goal_async(goal), timeout10.0) except asyncio.TimeoutError: print(动作执行超时)7.3 进度条实现结合反馈信息显示美观的控制台进度条def feedback_callback(feedback): progress int(feedback.progress * 50) print(f\r[{#*progress}{-*(50-progress)}] {feedback.progress:.1%}, end)在真实项目中动作机制显著提升了我们机器人系统的可靠性。记得第一次部署时机械臂在动作执行过程中突然断电得益于动作的反馈机制系统能够准确知道断电前的位置恢复供电后自动继续未完成的任务。这种鲁棒性是用纯话题难以实现的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价