bool MoveBase::executeCycle(geometry_msgs::PoseStamped& goal, std::vector<geometry_msgs::PoseStamped>& global_plan){ boost::recursive_mutex::scoped_lock ecl(configuration_mutex_); //we need to be able to publish velocity commands geometry_msgs::Twist cmd_vel; //update feedback to correspond to our curent position geometry_msgs::PoseStamped global_pose; getRobotPose(global_pose, planner_costmap_ros_); const geometry_msgs::PoseStamped& current_position = global_pose; //push the feedback out move_base_msgs::MoveBaseFeedback feedback; feedback.base_position = current_position; as_->publishFeedback(feedback); //check to see if we've moved far enough to reset our oscillation timeout if(distance(current_position, oscillation_pose_) >= oscillation_distance_) { last_oscillation_reset_ = ros::Time::now(); oscillation_pose_ = current_position; //if our last recovery was caused by oscillation, we want to reset the recovery index if(recovery_trigger_ == OSCILLATION_R) recovery_index_ = 0; } //check that the observation buffers for the costmap are current, we don't want to drive blind if(!controller_costmap_ros_->isCurrent()){ ROS_WARN("[%s]:Sensor data is out of date, we're not going to allow commanding of the base for safety",ros::this_node::getName().c_str()); publishZeroVelocity(); return false; } //if we have a new plan then grab it and give it to the controller if(new_global_plan_){ //make sure to set the new plan flag to false new_global_plan_ = false; ROS_DEBUG_NAMED("move_base","Got a new plan...swap pointers"); //do a pointer swap under mutex std::vector<geometry_msgs::PoseStamped>* temp_plan = controller_plan_; boost::unique_lock<boost::recursive_mutex> lock(planner_mutex_); controller_plan_ = latest_plan_; latest_plan_ = temp_plan; lock.unlock(); ROS_DEBUG_NAMED("move_base","pointers swapped!"); if(!tc_->setPlan(*controller_plan_)){ //ABORT and SHUTDOWN COSTMAPS ROS_ERROR("Failed to pass global plan to the controller, aborting."); resetState(); //disable the planner thread lock.lock(); runPlanner_ = false; lock.unlock(); as_->setAborted(move_base_msgs::MoveBaseResult(), "Failed to pass global plan to the controller."); return true; } //make sure to reset recovery_index_ since we were able to find a valid plan if(recovery_trigger_ == PLANNING_R) recovery_index_ = 0; } //the move_base state machine, handles the control logic for navigation switch(state_){ //if we are in a planning state, then we'll attempt to make a plan case PLANNING: { boost::recursive_mutex::scoped_lock lock(planner_mutex_); runPlanner_ = true; planner_cond_.notify_one(); } ROS_DEBUG_NAMED("move_base","Waiting for plan, in the planning state."); break; //if we're controlling, we'll attempt to find valid velocity commands case CONTROLLING: ROS_DEBUG_NAMED("move_base","In controlling state."); //check to see if we've reached our goal if(tc_->isGoalReached()){ ROS_DEBUG_NAMED("move_base","Goal reached!"); resetState(); //disable the planner thread boost::unique_lock<boost::recursive_mutex> lock(planner_mutex_); runPlanner_ = false; lock.unlock(); as_->setSucceeded(move_base_msgs::MoveBaseResult(), "Goal reached."); return true; } //check for an oscillation condition if(oscillation_timeout_ > 0.0 && last_oscillation_reset_ + ros::Duration(oscillation_timeout_) < ros::Time::now()) { publishZeroVelocity(); state_ = CLEARING; recovery_trigger_ = OSCILLATION_R; } { boost::unique_lock<costmap_2d::Costmap2D::mutex_t> lock(*(controller_costmap_ros_->getCostmap()->getMutex())); //调用computeVelocityCommands进行局部规划 if(tc_->computeVelocityCommands(cmd_vel)){ ROS_DEBUG_NAMED( "move_base", "Got a valid command from the local planner: %.3lf, %.3lf, %.3lf", cmd_vel.linear.x, cmd_vel.linear.y, cmd_vel.angular.z ); last_valid_control_ = ros::Time::now(); //make sure that we send the velocity command to the base vel_pub_.publish(cmd_vel); if(recovery_trigger_ == CONTROLLING_R) recovery_index_ = 0; } else { ROS_DEBUG_NAMED("move_base", "The local planner could not find a valid plan."); ros::Time attempt_end = last_valid_control_ + ros::Duration(controller_patience_); //check if we've tried to find a valid control for longer than our time limit if(ros::Time::now() > attempt_end){ //we'll move into our obstacle clearing mode publishZeroVelocity(); state_ = CLEARING; recovery_trigger_ = CONTROLLING_R; } else{ //otherwise, if we can't find a valid control, we'll go back to planning last_valid_plan_ = ros::Time::now(); planning_retries_ = 0; state_ = PLANNING; publishZeroVelocity(); //enable the planner thread in case it isn't running on a clock boost::unique_lock<boost::recursive_mutex> lock(planner_mutex_); runPlanner_ = true; planner_cond_.notify_one(); lock.unlock(); } } } break; //we'll try to clear out space with any user-provided recovery behaviors case CLEARING: ROS_DEBUG_NAMED("move_base","In clearing/recovery state"); //we'll invoke whatever recovery behavior we're currently on if they're enabled if(recovery_behavior_enabled_ && recovery_index_ < recovery_behaviors_.size()){ ROS_DEBUG_NAMED("move_base_recovery","Executing behavior %u of %zu", recovery_index_, recovery_behaviors_.size()); recovery_behaviors_[recovery_index_]->runBehavior(); //we at least want to give the robot some time to stop oscillating after executing the behavior last_oscillation_reset_ = ros::Time::now(); //we'll check if the recovery behavior actually worked ROS_DEBUG_NAMED("move_base_recovery","Going back to planning state"); last_valid_plan_ = ros::Time::now(); planning_retries_ = 0; state_ = PLANNING; //update the index of the next recovery behavior that we'll try recovery_index_++; } else{ ROS_DEBUG_NAMED("move_base_recovery","All recovery behaviors have failed, locking the planner and disabling it."); //disable the planner thread boost::unique_lock<boost::recursive_mutex> lock(planner_mutex_); runPlanner_ = false; lock.unlock(); ROS_DEBUG_NAMED("move_base_recovery","Something should abort after this."); if(recovery_trigger_ == CONTROLLING_R){ ROS_ERROR("Aborting because a valid control could not be found. Even after executing all recovery behaviors"); as_->setAborted(move_base_msgs::MoveBaseResult(), "Failed to find a valid control. Even after executing recovery behaviors."); } else if(recovery_trigger_ == PLANNING_R){ ROS_ERROR("Aborting because a valid plan could not be found. Even after executing all recovery behaviors"); as_->setAborted(move_base_msgs::MoveBaseResult(), "Failed to find a valid plan. Even after executing recovery behaviors."); } else if(recovery_trigger_ == OSCILLATION_R){ ROS_ERROR("Aborting because the robot appears to be oscillating over and over. Even after executing all recovery behaviors"); as_->setAborted(move_base_msgs::MoveBaseResult(), "Robot is oscillating. Even after executing recovery behaviors."); } resetState(); return true; } break; default: ROS_ERROR("This case should never be reached, something is wrong, aborting"); resetState(); //disable the planner thread boost::unique_lock<boost::recursive_mutex> lock(planner_mutex_); runPlanner_ = false; lock.unlock(); as_->setAborted(move_base_msgs::MoveBaseResult(), "Reached a case that should not be hit in move_base. This is a bug, please report it."); return true; } //we aren't done yet return false; } |