| name | ros2-action-communication |
| description | ROS2 Action 通讯技能 - ActionServer/ActionClient 实现、目标执行、反馈获取、任务取消 |
| user-invocable | true |
| argument-hint | 创建 action OR ros2 action OR 动作客户端 OR action server client |
ROS2 Action Communication Skill
ROS2 Action 异步任务执行完整指南
何时使用
当需要以下帮助时使用此技能:
- 实现长时间运行的任务
- 需要进度反馈的操作
- 任务取消和抢占
- 异步目标执行
- 任务状态跟踪
快速参考
基础架构
Goal (目标) ──> ActionServer <─── Execution
^ |
│ v
Feedback <────── Progress
^
│
Result <──────── Completion
应用场景
- 导航 (Nav2)
- 机械臂运动控制
- 无人机起飞/降落
- 摄像头标定
- 数据采集任务
C++ ActionServer
基本服务端
#include <rclcpp/rclcpp.hpp>
#include <rclcpp_action/rclcpp_action.hpp>
#include <example_interfaces/action/fibonacci.hpp>
class FibonacciServer : public rclcpp::Node {
public:
using Fibonacci = example_interfaces::action::Fibonacci;
using GoalHandle = rclcpp_action::ServerGoalHandle<Fibonacci>;
FibonacciServer() : Node("fibonacci_server") {
action_server_ = rclcpp_action::create_server<Fibonacci>(
this,
"fibonacci",
std::bind(&FibonacciServer::handle_goal, this, std::placeholders::_1, std::placeholders::_2),
std::bind(&FibonacciServer::handle_cancel, this, std::placeholders::_1),
std::bind(&FibonacciServer::handle_accepted, this, std::placeholders::_1));
RCLCPP_INFO(this->get_logger(), "Action server ready");
}
private:
rclcpp_action::Server<Fibonacci>::SharedPtr action_server_;
rclcpp_action::GoalResponse handle_goal(
const rclcpp_action::GoalUUID& uuid,
std::shared_ptr<const Fibonacci::Goal> goal) {
RCLCPP_INFO(this->get_logger(), "Received goal with order: %d", goal->order);
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
}
rclcpp_action::CancelResponse handle_cancel(
const std::shared_ptr<GoalHandle> goal_handle) {
RCLCPP_INFO(this->get_logger(), "Received cancel request");
return rclcpp_action::CancelResponse::ACCEPT;
}
void handle_accepted(const std::shared_ptr<GoalHandle> goal_handle) {
std::thread([this, goal_handle]() { execute(goal_handle); }).detach();
}
void execute(const std::shared_ptr<GoalHandle> goal_handle) {
RCLCPP_INFO(this->get_logger(), "Executing goal");
auto feedback = std::make_shared<Fibonacci::Feedback>();
auto& sequence = feedback->partial_sequence;
sequence.push_back(0);
sequence.push_back(1);
auto result = std::make_shared<Fibonacci::Result>();
for (int i = 1; i < goal_handle->get_goal()->order; ++i) {
if (goal_handle->is_canceling()) {
result->sequence = sequence;
goal_handle->canceled(result);
RCLCPP_INFO(this->get_logger(), "Goal canceled");
return;
}
sequence.push_back(sequence[i] + sequence[i-1]);
goal_handle->publish_feedback(feedback);
RCLCPP_INFO(this->get_logger(), "Published feedback");
std::this_thread::sleep_for(1s);
}
result->sequence = sequence;
goal_handle->succeed(result);
RCLCPP_INFO(this->get_logger(), "Goal succeeded");
}
};
C++ ActionClient
基本客户端
class FibonacciClient : public rclcpp::Node {
public:
using Fibonacci = example_interfaces::action::Fibonacci;
using GoalHandle = rclcpp_action::ClientGoalHandle<Fibonacci>;
FibonacciClient() : Node("fibonacci_client") {
action_client_ = rclcpp_action::create_client<Fibonacci>(
this,
"fibonacci");
if (!action_client_->wait_for_action_server(10s)) {
RCLCPP_ERROR(this->get_logger(), "Action server not available");
return;
}
send_goal();
}
void send_goal() {
auto goal = Fibonacci::Goal();
goal.order = 10;
auto send_goal_options =
rclcpp_action::Client<Fibonacci>::SendGoalOptions();
send_goal_options.goal_response_callback = [this](
std::shared_ptr<GoalHandle> handle) {
if (!handle) {
RCLCPP_ERROR(this->get_logger(), "Goal was rejected");
} else {
RCLCPP_INFO(this->(), );
goal_handle_ = handle;
}
};
send_goal_options.feedback_callback = [](
std::shared_ptr<GoalHandle> handle,
std::shared_ptr<Fibonacci::Feedback> feedback) {
(->(), ,
feedback->partial_sequence.());
};
send_goal_options.result_callback = [](
rclcpp_action::ClientGoalHandle<Fibonacci>::WrappedResult& result) {
(result.code) {
rclcpp_action::ResultCode::SUCCEEDED:
(->(), );
;
rclcpp_action::ResultCode::CANCELED:
(->(), );
;
rclcpp_action::ResultCode::ABORTED:
(->(), );
;
}
};
action_client_->(goal, send_goal_options);
}
:
rclcpp_action::Client<Fibonacci>::SharedPtr action_client_;
std::shared_ptr<GoalHandle> goal_handle_;
};
取消目标
void cancel_goal() {
if (goal_handle_) {
auto future = goal_handle_->async_cancel();
if (rclcpp::spin_until_future_complete(this->get_node_base_interface(),
future) == rclcpp::FutureReturnCode::SUCCESS) {
RCLCPP_INFO(this->get_logger(), "Cancel request sent");
}
}
}
Python 实现
服务端
import rclpy
from rclpy.node import Node
from rclpy.action import ActionServer
from rclpy.action.server import GoalResponse, CancelResponse
from example_interfaces.action import Fibonacci
class FibonacciServer(Node):
def __init__(self):
super().__init__('fibonacci_server')
self._action_server = ActionServer(
self, Fibonacci, 'fibonacci',
self.execute_callback,
goal_callback=self.goal_callback,
cancel_callback=self.cancel_callback)
def goal_callback(self, goal_handle):
self.get_logger().info(f'Received goal: {goal_handle.request.order}')
return GoalResponse.ACCEPT_AND_EXECUTE
def cancel_callback(self, goal_handle):
self.get_logger().info('Received cancel request')
return CancelResponse.ACCEPT
def execute_callback(self, goal_handle):
self.get_logger().info('Executing goal')
feedback = Fibonacci.Feedback()
feedback.partial_sequence = [0, 1]
i (, goal_handle.request.order):
goal_handle.is_canceling():
goal_handle.canceled()
Fibonacci.Result()
feedback.partial_sequence.append(
feedback.partial_sequence[i] + feedback.partial_sequence[i-])
goal_handle.publish_feedback(feedback)
goal_handle.succeed()
result = Fibonacci.Result()
result.sequence = feedback.partial_sequence
result
客户端
class FibonacciClient(Node):
def __init__(self):
super().__init__('fibonacci_client')
self._action_client = ActionClient(self, Fibonacci, 'fibonacci')
def send_goal(self, order=10):
goal = Fibonacci.Goal()
goal.order = order
self._action_client.wait_for_server()
self._send_goal_future = self._action_client.send_goal_async(
goal, feedback_callback=self.feedback_callback)
self._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:
self.get_logger().info('Goal rejected')
return
self.get_logger().info('Goal accepted')
self._result_future = goal_handle.get_result_async()
self._result_future.add_done_callback(self.result_callback)
def feedback_callback(self, feedback_msg):
self.get_logger().info(f'Feedback: {feedback_msg.feedback.partial_sequence}')
():
result = future.result().result
.get_logger().info()
Action 定义
自定义 Action 文件
geometry_msgs/Pose target_pose
float64 speed
---
float64 progress
string status
---
bool success
string message
geometry_msgs/Pose final_pose
CMakeLists.txt
rosidl_generate_interfaces(${PROJECT_NAME}
"action/MyAction.action"
)
package.xml
<member_of_group>rosidl_interface_packages</member_of_group>
命令行工具
ros2 action list
ros2 action type /fibonacci
ros2 action send_goal /fibonacci example_interfaces/action/Fibonacci "{order: 10}"
ros2 action info /fibonacci
最佳实践
- 异步处理: Action 任务应在独立线程执行
- 取消支持: 实现取消回调支持任务中断
- 进度反馈: 定期发布反馈让客户端了解进度
- 超时处理: 设置合理的超时时间
- 状态追踪: 记录目标状态变化便于调试