00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #include <nav_core2/exceptions.h>
00036 #include <dwb_local_planner/dwb_local_planner.h>
00037 #include <dwb_local_planner/backwards_compatibility.h>
00038 #include <dwb_local_planner/illegal_trajectory_tracker.h>
00039 #include <nav_2d_utils/conversions.h>
00040 #include <nav_2d_utils/tf_help.h>
00041 #include <nav_2d_msgs/Twist2D.h>
00042 #include <dwb_msgs/CriticScore.h>
00043 #include <pluginlib/class_list_macros.h>
00044 #include <string>
00045 #include <vector>
00046 #include <algorithm>
00047
00048 namespace dwb_local_planner
00049 {
00050
00051 DWBLocalPlanner::DWBLocalPlanner() :
00052 traj_gen_loader_("dwb_local_planner", "dwb_local_planner::TrajectoryGenerator"),
00053 goal_checker_loader_("dwb_local_planner", "dwb_local_planner::GoalChecker"),
00054 critic_loader_("dwb_local_planner", "dwb_local_planner::TrajectoryCritic")
00055 {
00056 }
00057
00058 void DWBLocalPlanner::initialize(const ros::NodeHandle& parent, const std::string& name,
00059 TFListenerPtr tf, nav_core2::Costmap::Ptr costmap)
00060 {
00061 tf_ = tf;
00062 costmap_ = costmap;
00063 planner_nh_ = ros::NodeHandle(parent, name);
00064
00065
00066 planner_nh_.param("update_costmap_before_planning", update_costmap_before_planning_, true);
00067
00068 planner_nh_.param("prune_plan", prune_plan_, true);
00069 planner_nh_.param("prune_distance", prune_distance_, 1.0);
00070 planner_nh_.param("short_circuit_trajectory_evaluation", short_circuit_trajectory_evaluation_, true);
00071 planner_nh_.param("debug_trajectory_details", debug_trajectory_details_, false);
00072 pub_.initialize(planner_nh_);
00073
00074
00075 std::string traj_generator_name;
00076 planner_nh_.param("trajectory_generator_name", traj_generator_name,
00077 getBackwardsCompatibleDefaultGenerator(planner_nh_));
00078 ROS_INFO_NAMED("DWBLocalPlanner", "Using Trajectory Generator \"%s\"", traj_generator_name.c_str());
00079 traj_generator_ = std::move(traj_gen_loader_.createUniqueInstance(traj_generator_name));
00080 traj_generator_->initialize(planner_nh_);
00081
00082 std::string goal_checker_name;
00083 planner_nh_.param("goal_checker_name", goal_checker_name, std::string("dwb_plugins::SimpleGoalChecker"));
00084 ROS_INFO_NAMED("DWBLocalPlanner", "Using Goal Checker \"%s\"", goal_checker_name.c_str());
00085 goal_checker_ = std::move(goal_checker_loader_.createUniqueInstance(goal_checker_name));
00086 goal_checker_->initialize(planner_nh_);
00087
00088 loadCritics(name);
00089 }
00090
00091 std::string DWBLocalPlanner::resolveCriticClassName(std::string base_name)
00092 {
00093 if (base_name.find("Critic") == std::string::npos)
00094 {
00095 base_name = base_name + "Critic";
00096 }
00097
00098 if (base_name.find("::") == std::string::npos)
00099 {
00100 for (unsigned int j = 0; j < default_critic_namespaces_.size(); j++)
00101 {
00102 std::string full_name = default_critic_namespaces_[j] + "::" + base_name;
00103 if (critic_loader_.isClassAvailable(full_name))
00104 {
00105 return full_name;
00106 }
00107 }
00108 }
00109 return base_name;
00110 }
00111
00112 void DWBLocalPlanner::loadCritics(const std::string name)
00113 {
00114 planner_nh_.param("default_critic_namespaces", default_critic_namespaces_);
00115 if (default_critic_namespaces_.size() == 0)
00116 {
00117 default_critic_namespaces_.push_back("dwb_critics");
00118 }
00119
00120 if (!planner_nh_.hasParam("critics"))
00121 {
00122 loadBackwardsCompatibleParameters(planner_nh_);
00123 }
00124
00125 std::vector<std::string> critic_names;
00126 planner_nh_.getParam("critics", critic_names);
00127 for (unsigned int i = 0; i < critic_names.size(); i++)
00128 {
00129 std::string plugin_name = critic_names[i];
00130 std::string plugin_class;
00131 planner_nh_.param(plugin_name + "/class", plugin_class, plugin_name);
00132 plugin_class = resolveCriticClassName(plugin_class);
00133
00134 TrajectoryCritic::Ptr plugin = std::move(critic_loader_.createUniqueInstance(plugin_class));
00135 ROS_INFO_NAMED("DWBLocalPlanner", "Using critic \"%s\" (%s)", plugin_name.c_str(), plugin_class.c_str());
00136 critics_.push_back(plugin);
00137 plugin->initialize(planner_nh_, plugin_name, costmap_);
00138 }
00139 }
00140
00141 bool DWBLocalPlanner::isGoalReached(const nav_2d_msgs::Pose2DStamped& pose, const nav_2d_msgs::Twist2D& velocity)
00142 {
00143 if (goal_pose_.header.frame_id == "")
00144 {
00145 ROS_WARN_NAMED("DWBLocalPlanner", "Cannot check if the goal is reached without the goal being set!");
00146 return false;
00147 }
00148
00149
00150 goal_pose_.header.stamp = pose.header.stamp;
00151
00152 bool ret = goal_checker_->isGoalReached(transformPoseToLocal(pose), transformPoseToLocal(goal_pose_), velocity);
00153 if (ret)
00154 {
00155 ROS_INFO_THROTTLE_NAMED(1.0, "DWBLocalPlanner", "Goal reached!");
00156 }
00157 return ret;
00158 }
00159
00160 void DWBLocalPlanner::setGoalPose(const nav_2d_msgs::Pose2DStamped& goal_pose)
00161 {
00162 ROS_INFO_NAMED("DWBLocalPlanner", "New Goal Received.");
00163 goal_pose_ = goal_pose;
00164 traj_generator_->reset();
00165 goal_checker_->reset();
00166 for (TrajectoryCritic::Ptr critic : critics_)
00167 {
00168 critic->reset();
00169 }
00170 }
00171
00172 void DWBLocalPlanner::setPlan(const nav_2d_msgs::Path2D& path)
00173 {
00174 pub_.publishGlobalPlan(path);
00175 global_plan_ = path;
00176 }
00177
00178 nav_2d_msgs::Twist2DStamped DWBLocalPlanner::computeVelocityCommands(const nav_2d_msgs::Pose2DStamped& pose,
00179 const nav_2d_msgs::Twist2D& velocity)
00180 {
00181 std::shared_ptr<dwb_msgs::LocalPlanEvaluation> results = nullptr;
00182 if (pub_.shouldRecordEvaluation())
00183 {
00184 results = std::make_shared<dwb_msgs::LocalPlanEvaluation>();
00185 }
00186
00187 try
00188 {
00189 nav_2d_msgs::Twist2DStamped cmd_vel = computeVelocityCommands(pose, velocity, results);
00190 pub_.publishEvaluation(results);
00191 return cmd_vel;
00192 }
00193 catch (const nav_core2::PlannerException& e)
00194 {
00195 pub_.publishEvaluation(results);
00196 throw;
00197 }
00198 }
00199
00200 void DWBLocalPlanner::prepare(const nav_2d_msgs::Pose2DStamped& pose, const nav_2d_msgs::Twist2D& velocity)
00201 {
00202 if (update_costmap_before_planning_)
00203 {
00204 costmap_->update();
00205 }
00206
00207 nav_2d_msgs::Path2D transformed_plan = transformGlobalPlan(pose);
00208 pub_.publishTransformedPlan(transformed_plan);
00209
00210
00211 goal_pose_.header.stamp = pose.header.stamp;
00212
00213 geometry_msgs::Pose2D local_start_pose = transformPoseToLocal(pose),
00214 local_goal_pose = transformPoseToLocal(goal_pose_);
00215
00216 pub_.publishInputParams(costmap_->getInfo(), local_start_pose, velocity, local_goal_pose);
00217
00218 for (TrajectoryCritic::Ptr critic : critics_)
00219 {
00220 if (!critic->prepare(local_start_pose, velocity, local_goal_pose, transformed_plan))
00221 {
00222 ROS_WARN_NAMED("DWBLocalPlanner", "Critic \"%s\" failed to prepare", critic->getName().c_str());
00223 }
00224 }
00225 }
00226
00227 nav_2d_msgs::Twist2DStamped DWBLocalPlanner::computeVelocityCommands(const nav_2d_msgs::Pose2DStamped& pose,
00228 const nav_2d_msgs::Twist2D& velocity, std::shared_ptr<dwb_msgs::LocalPlanEvaluation>& results)
00229 {
00230 if (results)
00231 {
00232 results->header.frame_id = pose.header.frame_id;
00233 results->header.stamp = ros::Time::now();
00234 }
00235
00236 prepare(pose, velocity);
00237
00238 try
00239 {
00240 dwb_msgs::TrajectoryScore best = coreScoringAlgorithm(pose.pose, velocity, results);
00241
00242
00243 nav_2d_msgs::Twist2DStamped cmd_vel;
00244 cmd_vel.header.stamp = ros::Time::now();
00245 cmd_vel.velocity = best.traj.velocity;
00246
00247
00248 for (TrajectoryCritic::Ptr critic : critics_)
00249 {
00250 critic->debrief(cmd_vel.velocity);
00251 }
00252
00253 pub_.publishLocalPlan(pose.header, best.traj);
00254 pub_.publishCostGrid(costmap_, critics_);
00255
00256 return cmd_vel;
00257 }
00258 catch (const NoLegalTrajectoriesException& e)
00259 {
00260 nav_2d_msgs::Twist2D empty_cmd;
00261 dwb_msgs::Trajectory2D empty_traj;
00262
00263 for (TrajectoryCritic::Ptr critic : critics_)
00264 {
00265 critic->debrief(empty_cmd);
00266 }
00267 pub_.publishLocalPlan(pose.header, empty_traj);
00268 pub_.publishCostGrid(costmap_, critics_);
00269
00270 throw;
00271 }
00272 }
00273
00274 dwb_msgs::TrajectoryScore DWBLocalPlanner::coreScoringAlgorithm(const geometry_msgs::Pose2D& pose,
00275 const nav_2d_msgs::Twist2D velocity,
00276 std::shared_ptr<dwb_msgs::LocalPlanEvaluation>& results)
00277 {
00278 nav_2d_msgs::Twist2D twist;
00279 dwb_msgs::Trajectory2D traj;
00280 dwb_msgs::TrajectoryScore best, worst;
00281 best.total = -1;
00282 worst.total = -1;
00283 IllegalTrajectoryTracker tracker;
00284
00285 traj_generator_->startNewIteration(velocity);
00286 while (traj_generator_->hasMoreTwists())
00287 {
00288 twist = traj_generator_->nextTwist();
00289 traj = traj_generator_->generateTrajectory(pose, velocity, twist);
00290
00291 try
00292 {
00293 dwb_msgs::TrajectoryScore score = scoreTrajectory(traj, best.total);
00294 tracker.addLegalTrajectory();
00295 if (results)
00296 {
00297 results->twists.push_back(score);
00298 }
00299 if (best.total < 0 || score.total < best.total)
00300 {
00301 best = score;
00302 if (results)
00303 {
00304 results->best_index = results->twists.size() - 1;
00305 }
00306 }
00307 if (worst.total < 0 || score.total > worst.total)
00308 {
00309 worst = score;
00310 if (results)
00311 {
00312 results->worst_index = results->twists.size() - 1;
00313 }
00314 }
00315 }
00316 catch (const nav_core2::IllegalTrajectoryException& e)
00317 {
00318 if (results)
00319 {
00320 dwb_msgs::TrajectoryScore failed_score;
00321 failed_score.traj = traj;
00322
00323 dwb_msgs::CriticScore cs;
00324 cs.name = e.getCriticName();
00325 cs.raw_score = -1.0;
00326 failed_score.scores.push_back(cs);
00327 failed_score.total = -1.0;
00328 results->twists.push_back(failed_score);
00329 }
00330 tracker.addIllegalTrajectory(e);
00331 }
00332 }
00333
00334 if (best.total < 0)
00335 {
00336 if (debug_trajectory_details_)
00337 {
00338 ROS_ERROR_NAMED("DWBLocalPlanner", "%s", tracker.getMessage().c_str());
00339 for (auto const& x : tracker.getPercentages())
00340 {
00341 ROS_ERROR_NAMED("DWBLocalPlanner", "%.2f: %10s/%s", x.second, x.first.first.c_str(), x.first.second.c_str());
00342 }
00343 }
00344 throw NoLegalTrajectoriesException(tracker);
00345 }
00346
00347 return best;
00348 }
00349
00350 dwb_msgs::TrajectoryScore DWBLocalPlanner::scoreTrajectory(const dwb_msgs::Trajectory2D& traj,
00351 double best_score)
00352 {
00353 dwb_msgs::TrajectoryScore score;
00354 score.traj = traj;
00355
00356 for (TrajectoryCritic::Ptr critic : critics_)
00357 {
00358 dwb_msgs::CriticScore cs;
00359 cs.name = critic->getName();
00360 cs.scale = critic->getScale();
00361
00362 if (cs.scale == 0.0)
00363 {
00364 score.scores.push_back(cs);
00365 continue;
00366 }
00367
00368 double critic_score = critic->scoreTrajectory(traj);
00369 cs.raw_score = critic_score;
00370 score.scores.push_back(cs);
00371 score.total += critic_score * cs.scale;
00372 if (short_circuit_trajectory_evaluation_ && best_score > 0 && score.total > best_score)
00373 {
00374
00375 break;
00376 }
00377 }
00378
00379 return score;
00380 }
00381
00382 double getSquareDistance(const geometry_msgs::Pose2D& pose_a, const geometry_msgs::Pose2D& pose_b)
00383 {
00384 double x_diff = pose_a.x - pose_b.x;
00385 double y_diff = pose_a.y - pose_b.y;
00386 return x_diff * x_diff + y_diff * y_diff;
00387 }
00388
00389 nav_2d_msgs::Path2D DWBLocalPlanner::transformGlobalPlan(const nav_2d_msgs::Pose2DStamped& pose)
00390 {
00391 nav_2d_msgs::Path2D transformed_plan;
00392 if (global_plan_.poses.size() == 0)
00393 {
00394 throw nav_core2::PlannerException("Received plan with zero length");
00395 }
00396
00397
00398 nav_2d_msgs::Pose2DStamped robot_pose;
00399 if (!nav_2d_utils::transformPose(tf_, global_plan_.header.frame_id, pose, robot_pose))
00400 {
00401 throw nav_core2::PlannerTFException("Unable to transform robot pose into global plan's frame");
00402 }
00403
00404 transformed_plan.header.frame_id = costmap_->getFrameId();
00405 transformed_plan.header.stamp = pose.header.stamp;
00406
00407
00408 double dist_threshold = std::max(costmap_->getWidth(), costmap_->getHeight()) * costmap_->getResolution() / 2.0;
00409 double sq_dist_threshold = dist_threshold * dist_threshold;
00410 nav_2d_msgs::Pose2DStamped stamped_pose;
00411 stamped_pose.header.frame_id = global_plan_.header.frame_id;
00412
00413 for (unsigned int i = 0; i < global_plan_.poses.size(); i++)
00414 {
00415 bool should_break = false;
00416 if (getSquareDistance(robot_pose.pose, global_plan_.poses[i]) > sq_dist_threshold)
00417 {
00418 if (transformed_plan.poses.size() == 0)
00419 {
00420
00421 continue;
00422 }
00423 else
00424 {
00425
00426 should_break = true;
00427 }
00428 }
00429
00430
00431 stamped_pose.pose = global_plan_.poses[i];
00432 transformed_plan.poses.push_back(transformPoseToLocal(stamped_pose));
00433 if (should_break) break;
00434 }
00435
00436
00437
00438
00439 if (prune_plan_)
00440 {
00441
00442 nav_2d_msgs::Pose2DStamped costmap_pose;
00443 if (!nav_2d_utils::transformPose(tf_, transformed_plan.header.frame_id, pose, costmap_pose))
00444 {
00445 throw nav_core2::PlannerTFException("Unable to transform robot pose into costmap's frame");
00446 }
00447
00448 ROS_ASSERT(global_plan_.poses.size() >= transformed_plan.poses.size());
00449 std::vector<geometry_msgs::Pose2D>::iterator it = transformed_plan.poses.begin();
00450 std::vector<geometry_msgs::Pose2D>::iterator global_it = global_plan_.poses.begin();
00451 double sq_prune_dist = prune_distance_ * prune_distance_;
00452 while (it != transformed_plan.poses.end())
00453 {
00454 const geometry_msgs::Pose2D& w = *it;
00455
00456 if (getSquareDistance(costmap_pose.pose, w) < sq_prune_dist)
00457 {
00458 ROS_DEBUG_NAMED("DWBLocalPlanner", "Nearest waypoint to <%f, %f> is <%f, %f>\n",
00459 costmap_pose.pose.x, costmap_pose.pose.y, w.x, w.y);
00460 break;
00461 }
00462 it = transformed_plan.poses.erase(it);
00463 global_it = global_plan_.poses.erase(global_it);
00464 }
00465 pub_.publishGlobalPlan(global_plan_);
00466 }
00467
00468 if (transformed_plan.poses.size() == 0)
00469 {
00470 throw nav_core2::PlannerException("Resulting plan has 0 poses in it.");
00471 }
00472 return transformed_plan;
00473 }
00474
00475 geometry_msgs::Pose2D DWBLocalPlanner::transformPoseToLocal(const nav_2d_msgs::Pose2DStamped& pose)
00476 {
00477 return nav_2d_utils::transformStampedPose(tf_, pose, costmap_->getFrameId());
00478 }
00479
00480 }
00481
00482
00483
00484 PLUGINLIB_EXPORT_CLASS(dwb_local_planner::DWBLocalPlanner, nav_core2::LocalPlanner)