ROS2 C++中async_cancel_goal无法触发目标取消问题求助
ROS2 C++ Action目标取消失败的排查与修复
问题现象
在客户端的feedback_callback中,当feedback->current_total >=4.5时调用async_cancel_goal,但服务器未响应取消请求,目标持续执行,客户端result_callback的CANCELED分支也未触发,无对应日志输出。
核心问题分析
- 异步取消请求被提前销毁:
async_cancel_goal返回的cancel_future是局部变量,离开feedback_callback作用域后立即销毁,导致异步取消操作未完成。 - 未限制重复取消请求:
is_lap_completed_在触发取消后未置为true,会导致每次反馈到达时重复发起取消请求,可能干扰正常流程。 - 目标句柄有效性未验证:仅判断
goal_handle_非空,但未确认其是否处于活跃状态,可能使用了已失效的句柄。 - 服务器取消检查时机滞后:原代码中先发布反馈再检查取消请求,若取消请求在
sleep期间到达,需等到下一次循环才能响应。 - 里程计累计值未更新:服务器
execute函数中未看到total_distance_的更新逻辑,可能导致feedback->current_total永远无法达到触发条件。
修复步骤
1. 客户端feedback_callback修复
将取消请求的future保存为类成员,避免局部销毁;添加活跃状态检查,并触发后立即设置标志位:
// 在类定义中添加成员变量 rclcpp::Client<rclcpp_action::CancelGoal>::SharedFuture cancel_future_; void feedback_callback( GoalHandleRecord::SharedPtr, const std::shared_ptr<const OdomRecord::Feedback> feedback) { RCLCPP_INFO(this->get_logger(), "Feedback received: %f", feedback->current_total); if (feedback->current_total >= 4.5 && !is_lap_completed_) { // 验证目标句柄是否处于活跃状态 if (this->goal_handle_ && this->goal_handle_->is_active()) { RCLCPP_INFO(this->get_logger(), "Attempting to cancel goal..."); // 保存future到类成员,确保异步操作完成 cancel_future_ = action_client_->async_cancel_goal(this->goal_handle_); // 设置标志位,防止重复触发取消 is_lap_completed_ = true; } } }
2. 服务器execute函数优化
调整循环顺序,优先检查取消请求,确保及时响应;同时补充total_distance_的更新逻辑(根据实际里程计数据计算):
void execute(const std::shared_ptr<GoalHandleRecord> goal_handle) { RCLCPP_INFO(this->get_logger(), "Executing goal"); auto feedback = std::make_shared<OdomRecord::Feedback>(); auto &message = feedback->current_total; auto result = std::make_shared<OdomRecord::Result>(); rclcpp::Rate loop_rate(1); // Reset distance and initialization flag at the start of the goal total_distance_ = 0.0; is_initial_position_set_ = false; while (rclcpp::ok()) { // 优先检查取消请求,确保及时响应 if (goal_handle->is_canceling()) { result->list_of_odoms = this->list_of_odoms_; goal_handle->canceled(result); RCLCPP_INFO(this->get_logger(), "Goal canceled"); return; } // 补充:根据里程计数据更新total_distance_ // 示例:获取最新里程计消息,计算累计距离 // if (latest_odom_) { // total_distance_ += calculate_distance(latest_odom_); // } // Publish feedback message = total_distance_; goal_handle->publish_feedback(feedback); loop_rate.sleep(); } if (rclcpp::ok()) { result->list_of_odoms = this->list_of_odoms_; goal_handle->succeed(result); RCLCPP_INFO(this->get_logger(), "Goal succeeded"); } }
3. 确认目标句柄的正确获取
确保goal_handle_是从服务器返回的有效句柄,在send_goal_options的回调中正确设置:
auto send_goal_options = rclcpp_action::Client<OdomRecord>::SendGoalOptions(); send_goal_options.goal_response_callback = [this](const GoalHandleRecord::SharedPtr & goal_handle) { if (!goal_handle) { RCLCPP_ERROR(this->get_logger(), "Goal was rejected by server"); } else { RCLCPP_INFO(this->get_logger(), "Goal accepted by server, waiting for result"); this->goal_handle_ = goal_handle; } }; send_goal_options.feedback_callback = std::bind(&YourClientClass::feedback_callback, this, std::placeholders::_1, std::placeholders::_2); send_goal_options.result_callback = std::bind(&YourClientClass::result_callback, this, std::placeholders::_1); auto goal_future = action_client_->async_send_goal(goal, send_goal_options);
验证要点
- 运行时查看服务器日志,确认是否打印
Received request to cancel goal,若未打印则说明客户端取消请求未发送成功。 - 检查服务器
total_distance_的更新逻辑,确保feedback->current_total能达到4.5的触发条件。 - 确认客户端
goal_handle_在目标活跃期间未被覆盖或失效。
内容的提问来源于stack exchange,提问作者Zesk
相关产品推荐
相关产品推荐

