ROS1 Action通信深度解析从actionlib库到自定义消息实战在机器人系统开发中任务执行往往需要长时间运行并伴随中间状态反馈。传统的服务通信Service虽然简单直接但面对复杂任务时显得力不从心。这就是ROS1 Action通信机制大显身手的地方——它不仅支持任务取消、进度反馈还能优雅地处理长时间运行的任务。本文将带您深入actionlib库的核心实现并通过一个完整的自定义Action项目展示如何构建灵活、可靠的机器人任务交互系统。1. Action通信机制深度剖析Action通信是ROS中处理长时间运行任务的黄金标准。与简单的服务调用不同它采用客户端-服务器模型通过目标Goal、结果Result和反馈Feedback三种消息类型实现丰富交互。核心优势对比特性服务通信(Service)Action通信任务取消不支持支持反馈机制无多频次反馈适用场景瞬时任务长时间运行任务状态追踪无完整状态机资源占用低中等Action通信底层基于ROS消息和服务实现其状态机包含以下几个关键状态enum StateEnum { PENDING, // 任务排队中 ACTIVE, // 任务执行中 PREEMPTED, // 任务被抢占 SUCCEEDED, // 任务成功完成 ABORTED, // 任务异常终止 REJECTED, // 任务被拒绝 RECALLED, // 任务被取消 LOST // 连接丢失 };实际应用中Action通信特别适合以下场景机器人导航路径规划机械臂轨迹执行长时间数据采集任务需要用户干预的复杂流程2. 自定义Action消息实战创建自定义Action消息是构建复杂交互的第一步。让我们以AddInts.action为例展示完整的创建流程。文件结构规范action_demo/ ├── CMakeLists.txt ├── package.xml └── action/ └── AddInts.actionAddInts.action文件采用三段式结构# 目标定义 int32 target_number --- # 结果定义 int32 final_result float64 computation_time --- # 反馈定义 float64 progress string status_message关键配置步骤在CMakeLists.txt中# 添加Action文件 add_action_files( FILES AddInts.action ) # 生成消息依赖 generate_messages( DEPENDENCIES actionlib_msgs std_msgs ) # 包配置 catkin_package( CATKIN_DEPENDS actionlib actionlib_msgs roscpp std_msgs )编译后系统会自动生成6个关键消息类型AddIntsAction.msg完整Action定义AddIntsActionGoal.msg目标消息AddIntsActionResult.msg结果消息AddIntsActionFeedback.msg反馈消息AddIntsGoal.msg目标定义AddIntsFeedback.msg反馈定义提示自定义Action消息后需要先执行catkin_make生成消息代码再在其他节点中引用。3. Action服务端深度实现Action服务端是任务执行的核心我们基于actionlib::SimpleActionServer构建可靠的服务端实现。核心架构组件目标回调函数处理新任务请求预处理检查验证目标可行性任务执行循环包含进度反馈结果处理成功/失败处理完整服务端实现代码#include ros/ros.h #include actionlib/server/simple_action_server.h #include action_demo/AddIntsAction.h class AddIntsActionServer { public: AddIntsActionServer(std::string name) : as_(nh_, name, boost::bind(AddIntsActionServer::executeCB, this, _1), false), action_name_(name) { as_.start(); ROS_INFO(%s: Action server started, action_name_.c_str()); } void executeCB(const action_demo::AddIntsGoalConstPtr goal) { ros::Rate r(10); bool success true; // 初始化反馈 action_demo::AddIntsFeedback feedback; ROS_INFO(%s: Executing, computing sum of first %d integers, action_name_.c_str(), goal-target_number); // 开始执行 int progress 0; int result 0; ros::Time start_time ros::Time::now(); for(int i1; igoal-target_number; i) { // 检查是否被抢占 if (as_.isPreemptRequested() || !ros::ok()) { ROS_INFO(%s: Preempted, action_name_.c_str()); as_.setPreempted(); success false; break; } // 计算并更新反馈 result i; progress (i * 100.0) / goal-target_number; feedback.progress progress; feedback.status_message Processing...; as_.publishFeedback(feedback); r.sleep(); } // 返回最终结果 if(success) { action_demo::AddIntsResult res; res.final_result result; res.computation_time (ros::Time::now() - start_time).toSec(); ROS_INFO(%s: Succeeded, action_name_.c_str()); as_.setSucceeded(res); } } private: ros::NodeHandle nh_; actionlib::SimpleActionServeraction_demo::AddIntsAction as_; std::string action_name_; }; int main(int argc, char** argv) { ros::init(argc, argv, add_ints_server); AddIntsActionServer server(add_ints_action); ros::spin(); return 0; }关键优化点采用C类封装提高代码组织性实时检查任务抢占请求精确计算执行时间详细的执行状态反馈4. Action客户端高级技巧一个健壮的Action客户端需要考虑连接管理、超时处理和用户交互。以下是增强版客户端实现#include ros/ros.h #include actionlib/client/simple_action_client.h #include action_demo/AddIntsAction.h using namespace action_demo; void doneCb(const actionlib::SimpleClientGoalState state, const AddIntsResultConstPtr result) { if (state actionlib::SimpleClientGoalState::SUCCEEDED) { ROS_INFO(任务完成! 结果: %d, 耗时: %.2f秒, result-final_result, result-computation_time); } else { ROS_WARN(任务未完成: %s, state.toString().c_str()); } } void activeCb() { ROS_INFO(服务端已接受任务); } void feedbackCb(const AddIntsFeedbackConstPtr feedback) { ROS_INFO(当前进度: %.1f%%, 状态: %s, feedback-progress, feedback-status_message.c_str()); } int main(int argc, char** argv) { ros::init(argc, argv, add_ints_client); if (argc ! 2) { ROS_ERROR(请指定目标数字); return 1; } int target atoi(argv[1]); // 创建客户端 actionlib::SimpleActionClientAddIntsAction ac(add_ints_action, true); ROS_INFO(等待服务端启动...); if (!ac.waitForServer(ros::Duration(5.0))) { ROS_ERROR(服务端连接超时); return 1; } // 设置目标 AddIntsGoal goal; goal.target_number target; // 发送目标 ROS_INFO(发送目标: 计算前%d个整数的和, target); ac.sendGoal(goal, doneCb, activeCb, feedbackCb); // 交互控制 bool running true; while(running ros::ok()) { std::cout \n选项:\n 1. 查看任务状态\n 2. 取消任务\n 3. 退出\n 选择: ; int choice; std::cin choice; switch(choice) { case 1: { actionlib::SimpleClientGoalState state ac.getState(); ROS_INFO(当前状态: %s, state.toString().c_str()); break; } case 2: ac.cancelGoal(); ROS_INFO(已发送取消请求); running false; break; case 3: running false; break; default: ROS_WARN(无效选项); } } return 0; }客户端增强功能命令行参数解析连接超时处理交互式控制菜单完整的状态查询任务取消支持5. 高级应用与调试技巧在实际项目中Action通信的稳定性和可靠性至关重要。以下是几个关键实践多任务管理策略任务优先级队列实现资源冲突解决方案任务抢占处理流程# Python示例任务状态监控 import rospy import actionlib from actionlib_msgs.msg import GoalStatus def monitor_actions(): client actionlib.SimpleActionClient(add_ints_action, AddIntsAction) while not rospy.is_shutdown(): goals client.get_all_status() for goal in goals.goal_list: print(fGoal ID: {goal.goal_id.id}) print(fStatus: {GoalStatus.to_string(goal.status)}) print(fTime since start: {rospy.get_time() - goal.stamp.to_sec():.1f}s) rospy.sleep(1.0)性能优化表格优化方向具体措施预期效果网络传输压缩大型反馈消息降低带宽占用30%-50%线程模型使用AsyncSpinner处理回调提高响应速度20%资源管理实现任务队列和限流机制避免系统过载状态持久化定期保存任务检查点提高故障恢复能力日志记录详细记录状态转换便于问题诊断常见问题排查指南服务端不响应检查action名称是否匹配确认消息类型编译正确使用rostopic list验证通信反馈丢失增加客户端缓冲区大小降低反馈频率检查网络延迟状态转换异常验证预处理逻辑检查抢占处理流程添加状态转换日志注意在复杂系统中建议为每个Action定义超时机制避免长时间阻塞。典型的做法是在客户端设置waitForResult超时参数在服务端实现看门狗定时器。
ROS1 Action通信从入门到放弃?不,是到精通!详解actionlib库与自定义消息实战
发布时间:2026/5/26 6:11:42
ROS1 Action通信深度解析从actionlib库到自定义消息实战在机器人系统开发中任务执行往往需要长时间运行并伴随中间状态反馈。传统的服务通信Service虽然简单直接但面对复杂任务时显得力不从心。这就是ROS1 Action通信机制大显身手的地方——它不仅支持任务取消、进度反馈还能优雅地处理长时间运行的任务。本文将带您深入actionlib库的核心实现并通过一个完整的自定义Action项目展示如何构建灵活、可靠的机器人任务交互系统。1. Action通信机制深度剖析Action通信是ROS中处理长时间运行任务的黄金标准。与简单的服务调用不同它采用客户端-服务器模型通过目标Goal、结果Result和反馈Feedback三种消息类型实现丰富交互。核心优势对比特性服务通信(Service)Action通信任务取消不支持支持反馈机制无多频次反馈适用场景瞬时任务长时间运行任务状态追踪无完整状态机资源占用低中等Action通信底层基于ROS消息和服务实现其状态机包含以下几个关键状态enum StateEnum { PENDING, // 任务排队中 ACTIVE, // 任务执行中 PREEMPTED, // 任务被抢占 SUCCEEDED, // 任务成功完成 ABORTED, // 任务异常终止 REJECTED, // 任务被拒绝 RECALLED, // 任务被取消 LOST // 连接丢失 };实际应用中Action通信特别适合以下场景机器人导航路径规划机械臂轨迹执行长时间数据采集任务需要用户干预的复杂流程2. 自定义Action消息实战创建自定义Action消息是构建复杂交互的第一步。让我们以AddInts.action为例展示完整的创建流程。文件结构规范action_demo/ ├── CMakeLists.txt ├── package.xml └── action/ └── AddInts.actionAddInts.action文件采用三段式结构# 目标定义 int32 target_number --- # 结果定义 int32 final_result float64 computation_time --- # 反馈定义 float64 progress string status_message关键配置步骤在CMakeLists.txt中# 添加Action文件 add_action_files( FILES AddInts.action ) # 生成消息依赖 generate_messages( DEPENDENCIES actionlib_msgs std_msgs ) # 包配置 catkin_package( CATKIN_DEPENDS actionlib actionlib_msgs roscpp std_msgs )编译后系统会自动生成6个关键消息类型AddIntsAction.msg完整Action定义AddIntsActionGoal.msg目标消息AddIntsActionResult.msg结果消息AddIntsActionFeedback.msg反馈消息AddIntsGoal.msg目标定义AddIntsFeedback.msg反馈定义提示自定义Action消息后需要先执行catkin_make生成消息代码再在其他节点中引用。3. Action服务端深度实现Action服务端是任务执行的核心我们基于actionlib::SimpleActionServer构建可靠的服务端实现。核心架构组件目标回调函数处理新任务请求预处理检查验证目标可行性任务执行循环包含进度反馈结果处理成功/失败处理完整服务端实现代码#include ros/ros.h #include actionlib/server/simple_action_server.h #include action_demo/AddIntsAction.h class AddIntsActionServer { public: AddIntsActionServer(std::string name) : as_(nh_, name, boost::bind(AddIntsActionServer::executeCB, this, _1), false), action_name_(name) { as_.start(); ROS_INFO(%s: Action server started, action_name_.c_str()); } void executeCB(const action_demo::AddIntsGoalConstPtr goal) { ros::Rate r(10); bool success true; // 初始化反馈 action_demo::AddIntsFeedback feedback; ROS_INFO(%s: Executing, computing sum of first %d integers, action_name_.c_str(), goal-target_number); // 开始执行 int progress 0; int result 0; ros::Time start_time ros::Time::now(); for(int i1; igoal-target_number; i) { // 检查是否被抢占 if (as_.isPreemptRequested() || !ros::ok()) { ROS_INFO(%s: Preempted, action_name_.c_str()); as_.setPreempted(); success false; break; } // 计算并更新反馈 result i; progress (i * 100.0) / goal-target_number; feedback.progress progress; feedback.status_message Processing...; as_.publishFeedback(feedback); r.sleep(); } // 返回最终结果 if(success) { action_demo::AddIntsResult res; res.final_result result; res.computation_time (ros::Time::now() - start_time).toSec(); ROS_INFO(%s: Succeeded, action_name_.c_str()); as_.setSucceeded(res); } } private: ros::NodeHandle nh_; actionlib::SimpleActionServeraction_demo::AddIntsAction as_; std::string action_name_; }; int main(int argc, char** argv) { ros::init(argc, argv, add_ints_server); AddIntsActionServer server(add_ints_action); ros::spin(); return 0; }关键优化点采用C类封装提高代码组织性实时检查任务抢占请求精确计算执行时间详细的执行状态反馈4. Action客户端高级技巧一个健壮的Action客户端需要考虑连接管理、超时处理和用户交互。以下是增强版客户端实现#include ros/ros.h #include actionlib/client/simple_action_client.h #include action_demo/AddIntsAction.h using namespace action_demo; void doneCb(const actionlib::SimpleClientGoalState state, const AddIntsResultConstPtr result) { if (state actionlib::SimpleClientGoalState::SUCCEEDED) { ROS_INFO(任务完成! 结果: %d, 耗时: %.2f秒, result-final_result, result-computation_time); } else { ROS_WARN(任务未完成: %s, state.toString().c_str()); } } void activeCb() { ROS_INFO(服务端已接受任务); } void feedbackCb(const AddIntsFeedbackConstPtr feedback) { ROS_INFO(当前进度: %.1f%%, 状态: %s, feedback-progress, feedback-status_message.c_str()); } int main(int argc, char** argv) { ros::init(argc, argv, add_ints_client); if (argc ! 2) { ROS_ERROR(请指定目标数字); return 1; } int target atoi(argv[1]); // 创建客户端 actionlib::SimpleActionClientAddIntsAction ac(add_ints_action, true); ROS_INFO(等待服务端启动...); if (!ac.waitForServer(ros::Duration(5.0))) { ROS_ERROR(服务端连接超时); return 1; } // 设置目标 AddIntsGoal goal; goal.target_number target; // 发送目标 ROS_INFO(发送目标: 计算前%d个整数的和, target); ac.sendGoal(goal, doneCb, activeCb, feedbackCb); // 交互控制 bool running true; while(running ros::ok()) { std::cout \n选项:\n 1. 查看任务状态\n 2. 取消任务\n 3. 退出\n 选择: ; int choice; std::cin choice; switch(choice) { case 1: { actionlib::SimpleClientGoalState state ac.getState(); ROS_INFO(当前状态: %s, state.toString().c_str()); break; } case 2: ac.cancelGoal(); ROS_INFO(已发送取消请求); running false; break; case 3: running false; break; default: ROS_WARN(无效选项); } } return 0; }客户端增强功能命令行参数解析连接超时处理交互式控制菜单完整的状态查询任务取消支持5. 高级应用与调试技巧在实际项目中Action通信的稳定性和可靠性至关重要。以下是几个关键实践多任务管理策略任务优先级队列实现资源冲突解决方案任务抢占处理流程# Python示例任务状态监控 import rospy import actionlib from actionlib_msgs.msg import GoalStatus def monitor_actions(): client actionlib.SimpleActionClient(add_ints_action, AddIntsAction) while not rospy.is_shutdown(): goals client.get_all_status() for goal in goals.goal_list: print(fGoal ID: {goal.goal_id.id}) print(fStatus: {GoalStatus.to_string(goal.status)}) print(fTime since start: {rospy.get_time() - goal.stamp.to_sec():.1f}s) rospy.sleep(1.0)性能优化表格优化方向具体措施预期效果网络传输压缩大型反馈消息降低带宽占用30%-50%线程模型使用AsyncSpinner处理回调提高响应速度20%资源管理实现任务队列和限流机制避免系统过载状态持久化定期保存任务检查点提高故障恢复能力日志记录详细记录状态转换便于问题诊断常见问题排查指南服务端不响应检查action名称是否匹配确认消息类型编译正确使用rostopic list验证通信反馈丢失增加客户端缓冲区大小降低反馈频率检查网络延迟状态转换异常验证预处理逻辑检查抢占处理流程添加状态转换日志注意在复杂系统中建议为每个Action定义超时机制避免长时间阻塞。典型的做法是在客户端设置waitForResult超时参数在服务端实现看门狗定时器。