uint32_t TebLocalPlannerROS::computeVelocityCommands(const geometry_msgs::PoseStamped& pose, const geometry_msgs::TwistStamped& velocity, geometry_msgs::TwistStamped &cmd_vel, std::string &message) { // check if plugin initialized if(!initialized_) { ROS_ERROR("teb_local_planner has not been initialized, please call initialize() before using this planner"); message = "teb_local_planner has not been initialized"; return mbf_msgs::ExePathResult::NOT_INITIALIZED; } //初始化返回的速度命令 static uint32_t seq = 0; cmd_vel.header.seq = seq++; cmd_vel.header.stamp = ros::Time::now(); cmd_vel.header.frame_id = robot_base_frame_; cmd_vel.twist.linear.x = cmd_vel.twist.linear.y = cmd_vel.twist.angular.z = 0; goal_reached_ = false; // Get robot pose 获取小车位置 geometry_msgs::PoseStamped robot_pose; costmap_ros_->getRobotPose(robot_pose); robot_pose_ = PoseSE2(robot_pose.pose); // Get robot velocity 从里程计中获取小车当前速度 geometry_msgs::PoseStamped robot_vel_tf; odom_helper_.getRobotVel(robot_vel_tf); robot_vel_.linear.x = robot_vel_tf.pose.position.x; robot_vel_.linear.y = robot_vel_tf.pose.position.y; robot_vel_.angular.z = tf2::getYaw(robot_vel_tf.pose.orientation); //修剪plan,去掉已经行驶过的路径 // prune global plan to cut off parts of the past (spatially before the robot) pruneGlobalPlan(*tf_, robot_pose, global_plan_, cfg_.trajectory.global_plan_prune_distance); //从全局规划中获取在当前位姿的局部转换全局规划transformed_plan(离当前位姿小于某个距离的邻接点)。 //下面局部规划器将在transformed_plan的限制下进行局部轨迹规划。即从全局的粗规划得到当前的局部细规划。 // Transform global plan to the frame of interest (w.r.t. the local costmap) std::vector<geometry_msgs::PoseStamped> transformed_plan; int goal_idx; geometry_msgs::TransformStamped tf_plan_to_global; if (!transformGlobalPlan(*tf_, global_plan_, robot_pose, *costmap_, global_frame_, cfg_.trajectory.max_global_plan_lookahead_dist, transformed_plan, &goal_idx, &tf_plan_to_global)) { ROS_WARN("Could not transform the global plan to the frame of the controller"); message = "Could not transform the global plan to the frame of the controller"; return mbf_msgs::ExePathResult::INTERNAL_ERROR; } //把当前transformed_plan经过的点添加进via_points_ // update via-points container if (!custom_via_points_active_) updateViaPointsContainer(transformed_plan, cfg_.trajectory.global_plan_viapoint_sep); // check if global goal is reached //通过计算当前机器人位姿robot_pose_和全局目标global_goal的欧几里得距离,检查是否到目的点,包括位置(小于xy_goal_tolerance)和方向(小于yaw_goal_tolerance)。 //如果距离小于容错距离并且容错配置不用完成全局规划(!cfg_.goal_tolerance.complete_global_plan)或者当前经过点为空(via_points_.size() == 0),则认为目标到达,退出局部规划器。 geometry_msgs::PoseStamped global_goal; tf2::doTransform(global_plan_.back(), global_goal, tf_plan_to_global); double dx = global_goal.pose.position.x - robot_pose_.x(); double dy = global_goal.pose.position.y - robot_pose_.y(); double delta_orient = g2o::normalize_theta( tf2::getYaw(global_goal.pose.orientation) - robot_pose_.theta() ); if(fabs(std::sqrt(dx*dx+dy*dy)) < cfg_.goal_tolerance.xy_goal_tolerance && fabs(delta_orient) < cfg_.goal_tolerance.yaw_goal_tolerance && (!cfg_.goal_tolerance.complete_global_plan || via_points_.size() == 0)) { goal_reached_ = true; return mbf_msgs::ExePathResult::SUCCESS; } // check if we should enter any backup mode and apply settings configureBackupModes(transformed_plan, goal_idx); //如果transformed_plan为空,则不能做局部规划,退出局部规划器。 // Return false if the transformed global plan is empty if (transformed_plan.empty()) { ROS_WARN("Transformed plan is empty. Cannot determine a local plan."); message = "Transformed plan is empty"; return mbf_msgs::ExePathResult::INVALID_PATH; } //获取当前局部规划器的机器人目标点 // Get current goal point (last point of the transformed plan) robot_goal_.x() = transformed_plan.back().pose.position.x; robot_goal_.y() = transformed_plan.back().pose.position.y; // Overwrite goal orientation if needed if (cfg_.trajectory.global_plan_overwrite_orientation) { robot_goal_.theta() = estimateLocalGoalOrientation(global_plan_, transformed_plan.back(), goal_idx, tf_plan_to_global); // overwrite/update goal orientation of the transformed plan with the actual goal (enable using the plan as initialization) tf2::Quaternion q; q.setRPY(0, 0, robot_goal_.theta()); tf2::convert(q, transformed_plan.back().pose.orientation); } else { robot_goal_.theta() = tf2::getYaw(transformed_plan.back().pose.orientation); } // overwrite/update start of the transformed plan with the actual robot position (allows using the plan as initial trajectory) if (transformed_plan.size()==1) // plan only contains the goal { transformed_plan.insert(transformed_plan.begin(), geometry_msgs::PoseStamped()); // insert start (not yet initialized) } transformed_plan.front() = robot_pose; // update start //清空当前的障碍数组obstacles_以待更新 // clear currently existing obstacles obstacles_.clear(); //从代价地图中添加障碍物到obstacles_中 // Update obstacle container with costmap information or polygons provided by a costmap_converter plugin if (costmap_converter_) updateObstacleContainerWithCostmapConverter(); else updateObstacleContainerWithCostmap(); //如果有自定义障碍,也添加到obstacles_中 // also consider custom obstacles (must be called after other updates, since the container is not cleared) updateObstacleContainerWithCustomObstacles(); // Do not allow config changes during the following optimization step boost::mutex::scoped_lock cfg_lock(cfg_.configMutex()); // Now perform the actual planning // 调用TebOptimalPlanner::plan方法进行局部规划,下节具体讲解这个方法。 // bool success = planner_->plan(robot_pose_, robot_goal_, robot_vel_, cfg_.goal_tolerance.free_goal_vel); // straight line init bool success = planner_->plan(transformed_plan, &robot_vel_, cfg_.goal_tolerance.free_goal_vel); if (!success) { planner_->clearPlanner(); // force reinitialization for next time ROS_WARN("teb_local_planner was not able to obtain a local plan for the current setting."); ++no_infeasible_plans_; // increase number of infeasible solutions in a row time_last_infeasible_plan_ = ros::Time::now(); last_cmd_ = cmd_vel.twist; message = "teb_local_planner was not able to obtain a local plan"; return mbf_msgs::ExePathResult::NO_VALID_CMD; } //更新机器人的内半径robot_inscribed_radius_和机器人的外半径robot_circumscribed_radius // Check feasibility (but within the first few states only) if(cfg_.robot.is_footprint_dynamic) { // Update footprint of the robot and minimum and maximum distance from the center of the robot to its footprint vertices. footprint_spec_ = costmap_ros_->getRobotFootprint(); costmap_2d::calculateMinAndMaxDistances(footprint_spec_, robot_inscribed_radius_, robot_circumscribed_radius); } //检查轨迹是否可行,比如拐弯必须满足机器人的内半径和外半径不能碰到障碍物,不能出地图。 bool feasible = planner_->isTrajectoryFeasible(costmap_model_.get(), footprint_spec_, robot_inscribed_radius_, robot_circumscribed_radius, cfg_.trajectory.feasibility_check_no_poses); if (!feasible) { cmd_vel.twist.linear.x = cmd_vel.twist.linear.y = cmd_vel.twist.angular.z = 0; // now we reset everything to start again with the initialization of new trajectories. planner_->clearPlanner(); ROS_WARN("TebLocalPlannerROS: trajectory is not feasible. Resetting planner..."); ++no_infeasible_plans_; // increase number of infeasible solutions in a row time_last_infeasible_plan_ = ros::Time::now(); last_cmd_ = cmd_vel.twist; message = "teb_local_planner trajectory is not feasible"; return mbf_msgs::ExePathResult::NO_VALID_CMD; } // Get the velocity command for this sampling interval //通过规划的轨迹,获取速度命令,包括x,y,z分量和方向,下节具体讲解这个方法。 if (!planner_->getVelocityCommand(cmd_vel.twist.linear.x, cmd_vel.twist.linear.y, cmd_vel.twist.angular.z, cfg_.trajectory.control_look_ahead_poses)) { planner_->clearPlanner(); ROS_WARN("TebLocalPlannerROS: velocity command invalid. Resetting planner..."); ++no_infeasible_plans_; // increase number of infeasible solutions in a row time_last_infeasible_plan_ = ros::Time::now(); last_cmd_ = cmd_vel.twist; message = "teb_local_planner velocity command invalid"; return mbf_msgs::ExePathResult::NO_VALID_CMD; } //使速度变得更加平滑 // Saturate velocity, if the optimization results violates the constraints (could be possible due to soft constraints). saturateVelocity(cmd_vel.twist.linear.x, cmd_vel.twist.linear.y, cmd_vel.twist.angular.z, cfg_.robot.max_vel_x, cfg_.robot.max_vel_y, cfg_.robot.max_vel_theta, cfg_.robot.max_vel_x_backwards); // convert rot-vel to steering angle if desired (carlike robot). // The min_turning_radius is allowed to be slighly smaller since it is a soft-constraint // and opposed to the other constraints not affected by penalty_epsilon. The user might add a safety margin to the parameter itself. if (cfg_.robot.cmd_angle_instead_rotvel) { cmd_vel.twist.angular.z = convertTransRotVelToSteeringAngle(cmd_vel.twist.linear.x, cmd_vel.twist.angular.z, cfg_.robot.wheelbase, 0.95*cfg_.robot.min_turning_radius); if (!std::isfinite(cmd_vel.twist.angular.z)) { cmd_vel.twist.linear.x = cmd_vel.twist.linear.y = cmd_vel.twist.angular.z = 0; last_cmd_ = cmd_vel.twist; planner_->clearPlanner(); ROS_WARN("TebLocalPlannerROS: Resulting steering angle is not finite. Resetting planner..."); ++no_infeasible_plans_; // increase number of infeasible solutions in a row time_last_infeasible_plan_ = ros::Time::now(); message = "teb_local_planner steering angle is not finite"; return mbf_msgs::ExePathResult::NO_VALID_CMD; } } // a feasible solution should be found, reset counter no_infeasible_plans_ = 0; // store last command (for recovery analysis etc.) last_cmd_ = cmd_vel.twist; // Now visualize everything planner_->visualize(); visualization_->publishObstacles(obstacles_); visualization_->publishViaPoints(via_points_); visualization_->publishGlobalPlan(global_plan_); return mbf_msgs::ExePathResult::SUCCESS; } |