WSL2 Ubuntu 24.04 ROS2 Jazzy Action动作通信实战:从接口定义到全链路跑通(上)

发布时间:2026/10/8 13:30:47
WSL2 Ubuntu 24.04 ROS2 Jazzy Action动作通信实战:从接口定义到全链路跑通(上) 摘要:本文基于 WSL2 Ubuntu 24.04 + ROS2 Jazzy 实操环境,演示 Action 通信从接口定义到全链路跑通的避坑流程。通过一个「从 1 累加到 10、步长为 2」的最小化任务,带你完整掌握 Action 三段式接口定义、服务端与客户端节点的编写、节点入口注册与全量清缓存编译,以及运行验证的完整输出。文末总结了 5 个新手必踩的避坑点,帮你提前避开字段名不匹配、缓存残留等低级错误。关键词:ROS 2 Jazzy、Action 动作通信、接口定义、服务端节点、客户端节点已经掌握发布订阅、服务请求两个基础通信机制,接下来的 Action 是 ROS 2 里最贴近工业级机器人开发的核心通信能力——专门用来解决「长耗时任务需要实时进度、中途能随时取消」这类刚需,比如机器人导航、机械臂轨迹运动、点云批量处理等场景,是项目开发中绕不开的核心通信能力。这篇教程完全基于 WSL2 Ubuntu 24.04 + ROS 2 Jazzy 的实操环境,把从接口定义到跑通全链路的所有踩坑细节逐一复现,连新手必踩的字段名不匹配、缓存残留等低级错误都提前避开,与你之前发布的环境搭建、发布订阅、自定义服务系列教程风格保持一致。一、先搞懂:Action 到底比 Topic/Service 强在哪里很多新手分不清三种通信的边界,直接用一张表就能看清适用场景:通信类型核心特性典型场景天生缺陷Topic 发布订阅单向异步数据流,无确认传感器数据广播、底盘速度下发无响应、无法取消任务、拿不到执行进度Service 请求响应单次同步调用,等待返回结果单次开关设备、查询机器人状态同步阻塞,长任务会直接卡死,没有过程反馈Action 动作通信双向异步全链路:发目标 + 实时反馈 + 最终结果 + 随时取消导航到点、机械臂运动、批量数据计算接口定义稍复杂,新手容易踩字段不匹配的坑我们这次实操的累加任务就是最直观的最小化演示:从 1 累加到 10,步长为 2,中途每算一步就给客户端推送当前累加值和累加部分和,完整还原了机器人导航时「下发目标 → 实时上报当前位置 → 到达终点返回结果」的完整流程。二、前置环境准备已经按照系列第一篇教程装好了 WSL2 Ubuntu 24.04 + ROS 2 Jazzy 桌面版已经建好独立的自定义接口包my_first_interfaces,使用 ament_cmake 编译类型,专门用来存放所有自定义的 Msg/Srv/Action,避免把接口直接放进纯 Python 包,从而触发官方尚未修复的 Bug已经建好业务包my_first_pkg,用 ament_python 编译类型,之前的发布订阅、Service 节点已经可以正常运行三、定义规范的 Action 接口,一次写对避免后续改字段直接在my_first_interfaces/action路径下新建Accumulate.action,严格按照三段式语法写:# 请求段:客户端发给服务端的目标参数 int64 target_num int64 step --- # 结果段:任务执行完成后返回的最终结果 int64 sum --- # 反馈段:任务执行过程中实时推送的进度数据 int64 current_num # 当前正在累加的数字 int64 partial_sum # 当前步骤的累加部分和避坑提示:三段之间必须用三个短横线---分隔,不能多也不能少所有字段名在定义阶段就统一敲定,全链路统一使用,绝对不要中途改名,否则必然出现属性找不到的报错把所有自定义接口统一放在独立的 interfaces 包里,后续所有业务节点直接引用,不用重复定义,从根源上避免多包接口不一致的问题。修改my_first_interfaces包的CMakeLists.txt,开启 Action 生成:find_package(rosidl_default_generators REQUIRED) rosidl_generate_interfaces(${PROJECT_NAME} "action/Accumulate.action" DEPENDENCIES ) ament_package()四、写累加 Action 服务端节点在my_first_pkg/my_first_pkg目录下新建accumulate_action_server.py,完整代码如下:importrclpyfromrclpy.nodeimportNodefromrclpy.actionimportActionServerfrommy_first_interfaces.actionimportAccumulateclassAccumulateActionServer(Node):def__init__(self):super().__init__('accumulate_action_server')self._action_server=ActionServer(self,Accumulate,'accumulate',self.execute_callback)self.get_logger().info('累加Action服务端已启动')defexecute_callback(self,goal_handle):# 1. 拿到客户端下发的目标参数target=goal_handle.request.target_num step=goal_handle.request.step self