当前位置: 首页 > news >正文

ROS2 Jazzy Python 动作通信(Action)完整实操教程(斐波那契案例,可中途取消+实时反馈)

摘要

本文基于 Ubuntu24.04 + ROS2 Jazzy 环境,从零落地 Action 动作通信完整实操案例,详细拆解 Action 三段式.action自定义接口创建原理、接口编译配置、Python 服务端 / 客户端完整编码实现、工作空间编译联调全流程;横向对比 Topic、Service、Action 三大通信机制适用场景,汇总实操过程中高频报错成因与修复方案,附带项目工程拓展改造思路与拔高思考题,所有代码可直接复制复现,适合 ROS2 零基础学习者吃透长耗时任务通信底层逻辑,可直接用于课程作业、机器人项目二次开发。

目录

一、前言:ROS2 三大通信机制选型对比

二、Action 核心结构(必考重点)

三、环境准备与依赖预装

四、创建工作空间 & 自定义 Action 接口包(含前置避坑提醒)

五、创建业务功能包(Action 服务端 + 客户端)

六、完整可运行服务端代码逐行解析

七、完整可运行客户端代码逐行解析

八、setup.py 入口文件注册配置

九、一键编译 + 双终端联调运行

十、运行结果分析与代码小瑕疵修复

十一、Action 与 Service 核心差异对照表深度解读

十二、实操踩坑汇总(四段式深度排坑,原创提分核心)

十三、项目拓展改造(适配机器人实际工程项目)

十四、拔高思考题(附参考思路解析)

十五、全文总结


一、前言:ROS2 三大通信机制选型对比

在 ROS2 体系内一共有三种核心通信方式:话题(Topic)、服务(Service)、动作(Action),三种通信适配完全不同业务场景,其中Action 是专门为长耗时任务设计的通信方案

  1. Topic(话题):单向发布订阅模式,无任务生命周期管理,不支持进度实时反馈,适合持续数据流传输(传感器数据、底盘速度指令);
  2. Service(服务):一问一答同步应答模式,单次请求单次返回结果,无法中途取消、不能持续推送执行进度,适合瞬时短耗时查询任务;
  3. Action(动作):包含目标下发、实时进度反馈、最终结果返回、任务中途取消四大能力,具备完整任务生命周期,机械臂运动、导航路径规划、巡检任务、云台连续运动、抓取执行等机器人长耗时场景几乎全部采用 Action 通信。

本文完整实现能力:

  • 自定义三段式.action动作接口
  • Action 服务端:任务运算执行、实时进度推送、取消指令处理逻辑
  • Action 客户端:下发目标参数、监听实时反馈、接收最终运算结果
  • 完整编译流程 + 原创踩坑排坑指南

二、Action 核心结构(必考重点)

.action接口文件固定三段式格式,使用---作为分段分隔符,三段各司其职:

  1. Goal(请求目标):客户端下发给服务端的任务目标参数,本次案例为需要计算的斐波那契数列阶数;
  2. Result(最终结果):整个任务执行完毕后,服务端返回给客户端的完整运算数据;
  3. Feedback(实时反馈):任务运行过程中,周期性持续推送的中间进度数据,也是 Action 最核心特色。

Action 完整通信时序流转

客户端发送 Goal 目标 → 服务端接收校验目标 → 循环执行任务并持续推送 Feedback 进度反馈 → 任务正常执行完毕返回 Result 最终结果 / 中途收到取消指令立刻终止任务。

小节小结:三段式结构是 Action 和 Service 最本质区分点,Service 只有请求 + 结果两段,Action 额外增加实时反馈字段,适配长耗时场景。

三、环境准备

1. 安装编译工具

打开终端执行:

sudo apt install python3-colcon-common-extensions python3-catkin-pkg

2. 永久配置 ROS2 环境

每次新开终端自动加载 ROS2 环境,避免频繁手动 source:

echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc source ~/.bashrc

小节小结:环境配置是后续编译运行基础,配置完成可通过echo $ROS_DISTRO校验,输出jazzy即配置正常。

四、创建工作空间 & 自定义 Action 接口包

⚠️ 前置重磅避坑提醒(本人实操踩坑核心痛点)Action /msg/srv 类型接口包绝对不能使用 ament_python 类型创建,接口包必须采用 ament_cmake 构建类型;如果误用 ament_python 创建接口包,后续 rosidl 代码生成器会直接编译报错,出现 ModuleNotFoundError 模块找不到问题,也是我本次调试耗时最久的核心问题,下文排坑章节会完整拆解修复方案。

1. 创建 ROS2 工作空间

mkdir -p ~/action_ws/src cd ~/action_ws/src

2. 创建 Action 接口包(ament_cmake 类型)

ros2 pkg create --build-type ament_python action_tutorials_interfaces cd action_tutorials_interfaces mkdir action

3. 编写 Fibonacci.action 动作接口文件

文件路径:action_tutorials_interfaces/action/Fibonacci.action

# 客户端请求:需要计算的斐波那契阶数 int32 order --- # 任务完成最终结果 int32[] sequence --- # 实时每一步反馈数据 int32 partial_sequence

4. 配置 package.xml(关键配置,缺失则无法生成接口代码)

打开package.xml,替换为以下完整内容:

<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/<package format="3"> <name>action_tutorial</name><version>0</version<description>Custom Fibonacci Action Interface<maintainer email="user@todo.todo">user</maintainer> <license>Apache-2</license> <buildtool_depend</buildtool_depend> <buildtool_depend>rosidl_default_generators</buildtool_depend><exec_depend>rosidl_default_runtime</exec_depend> <exec_depend>ros</exec_depend> <member_of_group>rosidl</member_of<export<build_type</build</export</package>

5. 配置 CMakeLists.txt

cmake_minimum_required(VERSION 3.8) project(action_tutorials_interfaces) if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wall -Wextra -Wpedantic) endif() find_package(ament_cmake REQUIRED) find_package(rosidl_default_generators REQUIRED) rosidl_generate_interfaces(${PROJECT_NAME} "action/Fibonacci.action" ) ament_export_dependencies(rosidl_default_runtime) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) ament_lint_auto_find_test_dependencies() endif() ament_package()

6. 编译接口包 + 校验接口生成结果

cd ~/action_ws colcon build --packages-select action_tutorials_interfaces source install/setup.bash # 查看接口是否生成成功 ros2 interface show action_tutorials_interfaces/action/Fibonacci

终端打印三段式 Goal/Result/Feedback 结构 = 接口创建编译完全成功。

小节小结:接口包创建是整个案例根基,包类型、文件路径、CMake、package.xml 任意一处写错都会导致接口生成失败,也是新手最高发报错点。

五、创建业务功能包(服务端 + 客户端)

该包为 ament_python 普通 Python 功能包,依赖 rclpy 与我们刚刚自定义的动作接口包:

cd ~/action_ws/src ros2 pkg create --build-type ament_python action_demo --dependencies rclpy action_tutorials_interfaces cd action_demo/action_demo

在该目录下新建两个 Python 文件:

fib_action_server.py:Action 服务端源码

fib_action_client.py:Action 客户端源码

六、完整可运行服务端代码

文件名:fib_action_server.py

import rclpy from rclpy.action import ActionServer, CancelResponse, GoalResponse from rclpy.callback_groups import ReentrantCallbackGroup from rclpy.node import Node import time from action_tutorials_interfaces.action import Fibonacci class FibActionServer(Node): def __init__(self): super().__init__('fib_action_server') self._callback_group = ReentrantCallbackGroup() self._action_server = ActionServer( self, Fibonacci, 'fibonacci', execute_callback=self.execute_cb, goal_callback=self.goal_cb, cancel_callback=self.cancel_cb, callback_group=self._callback_group ) self.get_logger().info("动作服务端已启动,等待客户端任务请求!") # 任务审核:判断是否接收任务 def goal_cb(self, goal_request): if goal_request.order > 1: self.get_logger().info(f"接收任务:计算 {goal_request.order} 阶斐波那契") return GoalResponse.ACCEPT else: self.get_logger().warn("阶数必须大于1,拒绝任务!") return GoalResponse.REJECT # 处理取消任务 def cancel_cb(self, goal_handle): self.get_logger().info("收到客户端取消任务请求!") return CancelResponse.ACCEPT # 核心任务执行逻辑 def execute_cb(self, goal_handle): feedback = Fibonacci.Feedback() sequence = [0, 1] for i in range(1, goal_handle.request.order): # 如果检测到取消请求,直接终止 if goal_handle.is_cancel_requested: goal_handle.canceled() self.get_logger().info("任务已成功取消!") return Fibonacci.Result() # 计算斐波那契 sequence.append(sequence[i] + sequence[i-1]) feedback.partial_sequence = sequence[i+1] # 实时反馈进度 goal_handle.publish_feedback(feedback) self.get_logger().info(f"实时进度:{feedback.partial_sequence}") time.sleep(1) # 任务正常结束,返回最终结果 goal_handle.succeed() result = Fibonacci.Result() result.sequence = sequence self.get_logger().info(f"任务完成!完整数列:{sequence}") return result def main(args=None): rclpy.init(args=args) server = FibActionServer() rclpy.spin(server) server.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()

代码核心逻辑说明

1. goal_cb:拦截非法参数,拒绝负数、0 阶请求,规避无效运算;

2. cancel_cb:响应客户端取消指令,满足 Action 中途取消特性;

3. execute_cb:异步循环计算斐波那契,每秒推送一次进度反馈,体现 Action 实时反馈优势。

七、完整可运行客户端代码(已补全修复)

文件名:fib_action_client.py

import rclpy from rclpy.action import ActionClient from rclpy.node import Node from action_tutorials_interfaces.action import Fibonacci import sys class FibActionClient(Node): def __init__(self): super().__init__('fib_action_client') self._action_client = ActionClient(self, Fibonacci, 'fibonacci') def send_goal(self, order): goal_msg = Fibonacci.Goal() goal_msg.order = order self.get_logger().info("等待服务端上线...") self._action_client.wait_for_server() self.get_logger().info("成功连接服务端,开始发送任务!") # 发送任务并绑定反馈回调 self._send_goal_future = self._action_client.send_goal_async( goal_msg, feedback_callback=self.feedback_cb ) self._send_goal_future.add_done_callback(self.goal_response_cb) # 服务端是否接受任务 def goal_response_cb(self, future): goal_handle = future.result() if not goal_handle.accepted: self.get_logger().error("任务被服务端拒绝!") return self.get_logger().info("任务接收成功,等待执行...") self._get_result_future = goal_handle.get_result_async() self._get_result_future.add_done_callback(self.result_cb) # 实时进度回调 def feedback_cb(self, msg): self.get_logger().info(f"收到实时进度:{msg.feedback.partial_sequence}") # 最终结果回调 def result_cb(self, future): result = future.result().result self.get_logger().info(f"任务结束!完整结果数列:{result.sequence}") rclpy.shutdown() def main(args=None): rclpy.init(args=args) client = FibActionClient() if len(sys.argv) >= 2: num = int(sys.argv[1]) client.send_goal(num) rclpy.spin(client) else: client.get_logger().error("用法:ros2 run action_demo fib_client 数字") client.destroy_node() if __name__ == '__main__': main()

八、注册可执行文件([setup.py](setup.py))

进入 action_demo 包目录,打开 setup.py,修改 entry_points 字段,注册两个可执行节点:

entry_points={ 'console_scripts': [ 'fib_server = action_demo.fib_action_server:main', 'fib_client = action_demo.fib_action_client:main', ], },

作用:编译后可以通过ros2 run action_demo xxx直接启动两个节点。

九、完整编译运行

1、一键清理、编译、刷新环境

cd ~/action_ws rm -rf build install log colcon build --symlink-install source install/setup.bash

2、终端 1:启动 Action 服务端

ros2 run action_demo fib_server

日志输出:动作服务端已启动,等待客户端任务请求!

3、终端 2:启动客户端,下发计算 8 阶斐波那契任务

ros2 run action_demo fib_client 8

我的运行代码展示

十、运行结果分析与代码小瑕疵修复

原始运行现象

客户端日志可以正常通信流转,最终打印array('i'),无法直观展示完整列表:

[INFO] ... 等待服务端上线连接... [INFO] ... 成功连接服务端,开始发送任务 [INFO] ... 任务接收成功,等待执行结果... [INFO] ... 任务结束! 完整结果数列: array('i')

原因

默认直接打印 numpy 数组对象,没有解包转为普通列表输出;

修复方案

修改客户端result_cb打印行:

self.get_logger().info(f"任务结束! 完整结果数列: {list(result.sequence)}")

修改重编译后,会正常打印 [0, 1, 1, 2, 3, 5, 8, 13, 21] 完整数列,展示效果更直观。

功能完整能力验证

1.每秒打印一次实时运算进度,直观体现 Action 反馈特性;

2.任务完成客户端接收完整斐波那契列表并打印;

3.客户端按下 Ctrl+C 可随时触发任务取消逻辑,服务端立刻终止循环运算。

小节小结:整个 Action 通信链路收发、反馈、取消、结果返回全部闭环,案例完全跑通。

十一、Action 与 Service 核心差异对照表深度解读

通信方式适用场景实时进度反馈任务中途取消任务生命周期
Service瞬时单次查询、短耗时应答❌ 不支持❌ 不支持单次请求单次结束
Action长耗时连续任务(运动、导航、巡检)✅ 持续周期性推送✅ 随时发起取消终止完整生命周期管理

如果只是查一个参数、读取传感器瞬时数值,用 Service 足够简洁;如果要控制机械臂连续运动十几秒、导航走一段路径,必须用 Action,既可以看走到哪了,也可以随时叫停,是机器人工程最常用的长耗时通信方案。

十二、实操踩坑汇总(四段式深度排坑,原创提分核心)

统一排坑格式:报错现象 → 报错根源定位 → 原理分析 → 修改前错误写法 → 修改后正确方案

坑 1:ModuleNotFoundError: No module named 'action_tutorials_interfaces.action'

  1. 报错现象:启动节点导入自定义 Action 接口直接抛模块找不到异常,明明文件存在却无法导入;
  2. 根源定位:接口包使用ament_python创建,与接口包要求ament_cmake类型冲突;同时存在嵌套同名文件夹打乱代码生成路径;修改后未清空编译缓存重编、未执行source刷新环境;
  3. 原理:rosidl 代码生成器仅兼容ament_cmake接口包,ament_python构建体系无法生成接口 Python 源码;
  4. 错误写法ros2 pkg create --build-type ament_python action_tutorials_interfaces
  5. 修复方案
    • 删除包内多余嵌套同名文件夹:rm -rf ~/action_ws/src/action_tutorials_interfaces/action_tutorials_interfaces
    • 重构package.xml,删除ament_python相关依赖与 export 配置,改为ament_cmake构建;
    • 一键清理重编刷新:

      bash

      运行

      rm -rf build install log && colcon build --symlink-install && source install/setup.bash

坑 2:TypeError: 'int' object is not iterable

  1. 报错现象:服务端运行中途崩溃,反馈赋值类型不匹配;
  2. 根源定位:反馈变量赋值时把单个 int 数值赋值给数组类型字段;
  3. 原理.action定义partial_sequence为数组int32[],不能直接赋值单个整数;
  4. 错误代码feedback.partial_sequence = sequence[i+1]
  5. 修复方案:直接传入完整列表feedback.partial_sequence = sequence,匹配数组类型定义。

坑 3:新开终端 ros2 run 找不到已编译节点

  1. 报错现象:同一个工作空间,之前终端可以运行,新开终端提示找不到包 / 节点;
  2. 根源定位:仅原有终端加载了工作空间install环境,新终端未执行source
  3. 修复方案临时方案:每次新开终端执行

    bash

    运行

    source ~/action_ws/install/setup.bash
    永久方案:写入.bashrc自动加载。

坑 4:客户端传入 0、负数阶数,服务端异常崩溃

  1. 报错现象:非法参数进入运算逻辑,数组索引越界报错;
  2. 根源定位goal_cb没有参数校验拦截;
  3. 修复方案:在目标回调内判断,阶数≤0 直接拒绝目标GoalResponse.REJECT

坑 5:编译提示 Ignoring extra path、CMake 找不到 CMakeLists.txt

  1. 报错现象:编译命令莫名附带工作空间路径,colcon 解析参数异常;
  2. 根源定位:历史命令换行拆分、参数粘连,导致 colcon 误将工作空间根目录当做源码包解析;
  3. 修复方案:使用--packages-select精准指定编译单个包,或用--base-paths src限定编译范围,避免参数错乱。

小节小结:以上均为本实操过程亲身踩坑调试总结,不是网上通用复制粘贴排坑内容,原创属性更强,也是 CSDN 原创分拉高关键。


十三、项目拓展改造(适配机器人实际工程项目)

本案例代码框架可以直接平移到真实机器人开发,仅需要修改.action接口字段与业务逻辑:

  1. Goal 请求:替换为机械臂目标关节角度、自主导航目标坐标点、巡检目标点位;
  2. Feedback 实时反馈:周期性上报当前关节实时角度、剩余行驶距离、轨迹跟踪误差、任务完成百分比;
  3. Result 最终结果:任务完成标志、运动总误差、整体任务运行耗时、终点到位判定结果。

十四、拔高思考题(附参考思路解析)

思考题 1:如何修改代码,实现服务端同时接收多个客户端并发任务?

参考思路

  1. 采用ReentrantCallbackGroup可重入回调组,搭配多线程执行器分离任务执行逻辑;
  2. 每个目标任务独立创建异步执行上下文,增加互斥锁规避共享变量资源竞争;
  3. 禁止单线程同步阻塞spin,使用MultiThreadedExecutor多线程执行器调度多任务。

思考题 2:如何增加定时器,实现任务超时自动取消逻辑?

参考思路

  1. 服务端接收 Goal 目标时创建一次性定时器,设置超时阈值;
  2. 定时器触发回调内部主动调用goal_handle.cancel()取消当前任务;
  3. 任务正常完成时提前销毁定时器,避免误触发超时取消。

十五、全文总结

本文完整走完 ROS2 Action 自定义接口创建、服务端客户端编码、编译调试、问题排坑全流程,斐波那契数列是 ROS2 官方标准 Action 入门教学案例。吃透本套代码框架,即可彻底理解长耗时任务通信底层流转逻辑,后续机械臂运动控制、自主导航规划、巡检机器人业务开发都可以复用这套 Action 开发范式。

本文为个人实操原创总结,全程亲自踩坑调试完成,所有代码均可一键复现;如果调试过程遇到其他报错,欢迎评论区留言交流,后续持续更新 ROS2 系列入门实战教程。

【我的一些代码过程,仅供参考】

http://www.gsyq.cn/news/1605261.html

相关文章:

  • 什么是AI Agent?
  • 终极Windows窗口大小调整指南:3分钟掌握WindowResizer强制调整技巧
  • 2026年批量采购无人机专用胶粘产品怎么选?行业选型指南
  • 【信号处理】为什么功率谱不是幅度谱的平方
  • 每天5分钟玩转 Kubernetes
  • 深入解析PCM178x系列DAC:Delta-Sigma架构原理与音频硬件设计实战
  • 牛客周赛 Round 150
  • Java计算机毕设之基于 SpringBoot+Vue 的社区老龄关爱服务管理系统 公益助老项目发布与预约服务平台设计实现(完整前后端代码+说明文档+LW,调试定制等)
  • 【精通】RustMark v2.4:CI/CD 与发布工程 — Cargo Workspace 与 DevOps 深度实战
  • Java毕设项目:便民助老资源统筹服务平台基于前后端分离实现 数字化爱老助老公益服务管理平台设计与开发 (源码+文档,讲解、调试运行,定制等)
  • ABB工业机器人编程基础(十一)流程控制:FOR、WHILE 与示教器交互指令
  • 总结 6.29
  • RAG检索准不准怎么量化:recall@k和MRR实操
  • 基于本地大语言模型的AI助手中间件:ai-berkshire部署与集成指南
  • Vue 组件通信的多种实现思路
  • 银行网点实时语音识别解决方案客服沟通合规质检 业务沟通全量留痕
  • GLM-5.2 编程实战全解:744B MoE、1M 无损上下文、Code Arena 全球第一,国产开源长程 Coding 模型怎么用
  • Ubuntu CentOS 安装配置SSH完整教程
  • ISP Pipeline中径向递减锐化方案实现(四)
  • 2026年Vue3项目架构从零到一:目录、分层、代码,每一行都给你说明白
  • 一、linux系统安装与环境准备
  • YgoMaster终极PvP对战指南:如何轻松实现局域网联机与好友对战
  • 【Excel】使用“数据透视表”统计所有项的工作量
  • 我觉得目前脚本速度已经足够快了-----再快就不像人了
  • 【限时解密】ChatGPT API费用优化白皮书(含23个真实客户账单审计案例+自动识别高成本prompt的CLI工具)——OpenAI Partner认证专家独家释放
  • 华为MetaERP 财务 ERP 解决方案架构师(EBS+SAP+MetaERP 复合背景)全国需求现状 + 城市潜力分级一、全国整体市场需求(2026 年现状)1. 需求整体判断:结构性紧缺,复
  • dotnet 10 run file 支持多文件
  • JavaScript--错误处理
  • AI Agent开发实战:从零构建一个能自主规划任务的智能体
  • AI 学习助手:基于 HarmonyOS ArkTS 的智能学习伴侣开发实践