Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
dwb_local_planner.cpp
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2017, Locus Robotics
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of the copyright holder nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include <algorithm>
36 #include <memory>
37 #include <string>
38 #include <utility>
39 #include <vector>
40 
41 #include "dwb_core/dwb_local_planner.hpp"
42 #include "dwb_core/exceptions.hpp"
43 #include "dwb_core/illegal_trajectory_tracker.hpp"
44 #include "dwb_msgs/msg/critic_score.hpp"
45 #include "nav_2d_msgs/msg/twist2_d.hpp"
46 #include "nav_2d_utils/conversions.hpp"
47 #include "nav2_util/geometry_utils.hpp"
48 #include "nav2_ros_common/lifecycle_node.hpp"
49 #include "nav2_core/controller_exceptions.hpp"
50 #include "pluginlib/class_list_macros.hpp"
51 #include "nav_msgs/msg/path.hpp"
52 #include "geometry_msgs/msg/twist_stamped.hpp"
53 #include "nav2_ros_common/tf2_factories.hpp"
54 
55 using nav2_util::geometry_utils::euclidean_distance;
56 
57 namespace dwb_core
58 {
59 
61 : traj_gen_loader_("dwb_core", "dwb_core::TrajectoryGenerator"),
62  critic_loader_("dwb_core", "dwb_core::TrajectoryCritic")
63 {
64 }
65 
67  const nav2::LifecycleNode::WeakPtr & parent,
68  std::string name, nav2::TransformBuffer::SharedPtr tf,
69  std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
70 {
71  node_ = parent;
72  auto node = node_.lock();
73 
74  logger_ = node->get_logger();
75  clock_ = node->get_clock();
76  costmap_ros_ = costmap_ros;
77  tf_ = tf;
78  dwb_plugin_name_ = name;
79 
80  debug_trajectory_details_ = node->declare_or_get_parameter(
81  dwb_plugin_name_ + ".debug_trajectory_details", false);
82  std::string traj_generator_name = node->declare_or_get_parameter(
83  dwb_plugin_name_ + ".trajectory_generator_name",
84  std::string("dwb_plugins::StandardTrajectoryGenerator"));
85  short_circuit_trajectory_evaluation_ = node->declare_or_get_parameter(
86  dwb_plugin_name_ + ".short_circuit_trajectory_evaluation", true);
87 
88  pub_ = std::make_unique<DWBPublisher>(node, dwb_plugin_name_);
89  pub_->on_configure();
90 
91  traj_generator_ = traj_gen_loader_.createUniqueInstance(traj_generator_name);
92 
93  traj_generator_->initialize(node, dwb_plugin_name_);
94 
95  try {
96  loadCritics();
97  } catch (const std::exception & e) {
98  RCLCPP_ERROR(logger_, "Couldn't load critics! Caught exception: %s", e.what());
100  "Couldn't load critics! Caught exception: " +
101  std::string(e.what()));
102  }
103 }
104 
105 void
107 {
108  pub_->on_activate();
109  traj_generator_->activate();
110 }
111 
112 void
114 {
115  pub_->on_deactivate();
116  traj_generator_->deactivate();
117 }
118 
119 void
121 {
122  pub_->on_cleanup();
123 
124  traj_generator_.reset();
125 }
126 
127 std::string
129 {
130  if (base_name.find("Critic") == std::string::npos) {
131  base_name = base_name + "Critic";
132  }
133 
134  if (base_name.find("::") == std::string::npos) {
135  for (unsigned int j = 0; j < default_critic_namespaces_.size(); j++) {
136  std::string full_name = default_critic_namespaces_[j] + "::" + base_name;
137  if (critic_loader_.isClassAvailable(full_name)) {
138  return full_name;
139  }
140  }
141  }
142  return base_name;
143 }
144 
145 void
147 {
148  auto node = node_.lock();
149  if (!node) {
150  throw std::runtime_error{"Failed to lock node"};
151  }
152 
153  default_critic_namespaces_ = node->declare_or_get_parameter(
154  dwb_plugin_name_ + ".default_critic_namespaces",
155  std::vector<std::string>());
156  if (default_critic_namespaces_.empty()) {
157  default_critic_namespaces_.emplace_back("dwb_critics");
158  }
159 
160  std::vector<std::string> critic_names =
161  node->declare_or_get_parameter<std::vector<std::string>>(
162  dwb_plugin_name_ + ".critics");
163  if (critic_names.empty()) {
164  throw std::runtime_error("No critics defined for " + dwb_plugin_name_);
165  }
166 
167  for (unsigned int i = 0; i < critic_names.size(); i++) {
168  std::string critic_plugin_name = critic_names[i];
169 
170  std::string plugin_class = node->declare_or_get_parameter(
171  dwb_plugin_name_ + "." + critic_plugin_name + ".class",
172  critic_plugin_name);
173 
174  plugin_class = resolveCriticClassName(plugin_class);
175 
176  TrajectoryCritic::Ptr plugin = critic_loader_.createUniqueInstance(plugin_class);
177  RCLCPP_INFO(
178  logger_,
179  "Using critic \"%s\" (%s)", critic_plugin_name.c_str(), plugin_class.c_str());
180  critics_.push_back(plugin);
181  try {
182  plugin->initialize(node, critic_plugin_name, dwb_plugin_name_, costmap_ros_);
183  } catch (const std::exception & e) {
184  RCLCPP_ERROR(logger_, "Couldn't initialize critic plugin!");
186  "Couldn't initialize critic plugin: " +
187  std::string(e.what()));
188  }
189  RCLCPP_INFO(logger_, "Critic plugin initialized");
190  }
191 }
192 
193 void
194 DWBLocalPlanner::newPathReceived(const nav_msgs::msg::Path & /*raw_global_path*/)
195 {
196  for (TrajectoryCritic::Ptr & critic : critics_) {
197  critic->reset();
198  }
199  traj_generator_->reset();
200 }
201 
202 geometry_msgs::msg::TwistStamped
204  const geometry_msgs::msg::PoseStamped & pose,
205  const geometry_msgs::msg::Twist & velocity,
206  nav2_core::GoalChecker * /*goal_checker*/,
207  const nav_msgs::msg::Path & transformed_global_plan,
208  const geometry_msgs::msg::PoseStamped & global_goal)
209 {
210  std::shared_ptr<dwb_msgs::msg::LocalPlanEvaluation> results = nullptr;
211  if (pub_->shouldRecordEvaluation()) {
212  results = std::make_shared<dwb_msgs::msg::LocalPlanEvaluation>();
213  }
214 
215  try {
216  nav_2d_msgs::msg::Twist2DStamped cmd_vel2d = computeVelocityCommands(
217  pose,
218  nav_2d_utils::twist3Dto2D(velocity), results, transformed_global_plan, global_goal);
219  pub_->publishEvaluation(results);
220  geometry_msgs::msg::TwistStamped cmd_vel;
221  cmd_vel.twist = nav_2d_utils::twist2Dto3D(cmd_vel2d.velocity);
222  return cmd_vel;
223  } catch (const nav2_core::ControllerTFError & e) {
224  pub_->publishEvaluation(results);
225  throw e;
226  } catch (const nav2_core::InvalidPath & e) {
227  pub_->publishEvaluation(results);
228  throw e;
229  } catch (const nav2_core::NoValidControl & e) {
230  pub_->publishEvaluation(results);
231  throw e;
232  } catch (const nav2_core::ControllerException & e) {
233  pub_->publishEvaluation(results);
234  throw e;
235  }
236 }
237 
238 nav_2d_msgs::msg::Twist2DStamped
240  const geometry_msgs::msg::PoseStamped & pose,
241  const nav_2d_msgs::msg::Twist2D & velocity,
242  std::shared_ptr<dwb_msgs::msg::LocalPlanEvaluation> & results,
243  const nav_msgs::msg::Path & transformed_global_plan,
244  const geometry_msgs::msg::PoseStamped & global_goal)
245 {
246  if (results) {
247  results->header.frame_id = pose.header.frame_id;
248  results->header.stamp = clock_->now();
249  }
250 
251  nav2_costmap_2d::Costmap2D * costmap = costmap_ros_->getCostmap();
252  std::unique_lock<nav2_costmap_2d::Costmap2D::mutex_t> lock(*(costmap->getMutex()));
253 
254  for (TrajectoryCritic::Ptr & critic : critics_) {
255  if (!critic->prepare(pose.pose, velocity, global_goal.pose, transformed_global_plan)) {
256  RCLCPP_WARN(rclcpp::get_logger("DWBLocalPlanner"), "A scoring function failed to prepare");
257  }
258  }
259 
260  try {
261  dwb_msgs::msg::TrajectoryScore best = coreScoringAlgorithm(pose.pose, velocity, results);
262 
263  // Return Value
264  nav_2d_msgs::msg::Twist2DStamped cmd_vel;
265  cmd_vel.header.stamp = clock_->now();
266  cmd_vel.velocity = best.traj.velocity;
267 
268  // debrief stateful scoring functions
269  for (TrajectoryCritic::Ptr & critic : critics_) {
270  critic->debrief(cmd_vel.velocity);
271  }
272 
273  lock.unlock();
274 
275  pub_->publishLocalPlan(pose.header, best.traj);
276  pub_->publishCostGrid(costmap_ros_, critics_);
277 
278  return cmd_vel;
279  } catch (const dwb_core::NoLegalTrajectoriesException & e) {
280  nav_2d_msgs::msg::Twist2D empty_cmd;
281  dwb_msgs::msg::Trajectory2D empty_traj;
282  // debrief stateful scoring functions
283  for (TrajectoryCritic::Ptr & critic : critics_) {
284  critic->debrief(empty_cmd);
285  }
286 
287  lock.unlock();
288 
289  pub_->publishLocalPlan(pose.header, empty_traj);
290  pub_->publishCostGrid(costmap_ros_, critics_);
291 
293  "Could not find a legal trajectory: " +
294  std::string(e.what()));
295  }
296 }
297 
298 dwb_msgs::msg::TrajectoryScore
300  const geometry_msgs::msg::Pose & pose,
301  const nav_2d_msgs::msg::Twist2D velocity,
302  std::shared_ptr<dwb_msgs::msg::LocalPlanEvaluation> & results)
303 {
304  nav_2d_msgs::msg::Twist2D twist;
305  dwb_msgs::msg::Trajectory2D traj;
306  dwb_msgs::msg::TrajectoryScore best, worst;
307  best.total = -1;
308  worst.total = -1;
309  IllegalTrajectoryTracker tracker;
310 
311  traj_generator_->startNewIteration(velocity);
312  while (traj_generator_->hasMoreTwists()) {
313  twist = traj_generator_->nextTwist();
314  traj = traj_generator_->generateTrajectory(pose, velocity, twist);
315 
316  try {
317  dwb_msgs::msg::TrajectoryScore score = scoreTrajectory(traj, best.total);
318  tracker.addLegalTrajectory();
319  if (results) {
320  results->twists.push_back(score);
321  }
322  if (best.total < 0 || score.total < best.total) {
323  best = score;
324  if (results) {
325  results->best_index = results->twists.size() - 1;
326  }
327  }
328  if (worst.total < 0 || score.total > worst.total) {
329  worst = score;
330  if (results) {
331  results->worst_index = results->twists.size() - 1;
332  }
333  }
334  } catch (const dwb_core::IllegalTrajectoryException & e) {
335  if (results) {
336  dwb_msgs::msg::TrajectoryScore failed_score;
337  failed_score.traj = traj;
338 
339  dwb_msgs::msg::CriticScore cs;
340  cs.name = e.getCriticName();
341  cs.raw_score = -1.0;
342  failed_score.scores.push_back(cs);
343  failed_score.total = -1.0;
344  results->twists.push_back(failed_score);
345  }
346  tracker.addIllegalTrajectory(e);
347  }
348  }
349 
350  if (best.total < 0) {
351  if (debug_trajectory_details_) {
352  RCLCPP_ERROR(rclcpp::get_logger("DWBLocalPlanner"), "%s", tracker.getMessage().c_str());
353  for (auto const & x : tracker.getPercentages()) {
354  RCLCPP_ERROR(
355  rclcpp::get_logger(
356  "DWBLocalPlanner"), "%.2f: %10s/%s", x.second,
357  x.first.first.c_str(), x.first.second.c_str());
358  }
359  }
360  throw NoLegalTrajectoriesException(tracker);
361  }
362 
363  return best;
364 }
365 
366 dwb_msgs::msg::TrajectoryScore
368  const dwb_msgs::msg::Trajectory2D & traj,
369  double best_score)
370 {
371  dwb_msgs::msg::TrajectoryScore score;
372  score.traj = traj;
373 
374  for (TrajectoryCritic::Ptr & critic : critics_) {
375  dwb_msgs::msg::CriticScore cs;
376  cs.name = critic->getName();
377  cs.scale = critic->getScale();
378 
379  if (cs.scale == 0.0) {
380  score.scores.push_back(cs);
381  continue;
382  }
383 
384  double critic_score = critic->scoreTrajectory(traj);
385  cs.raw_score = critic_score;
386  score.scores.push_back(cs);
387  score.total += critic_score * cs.scale;
388  if (short_circuit_trajectory_evaluation_ && best_score > 0 && score.total > best_score) {
389  // since we keep adding positives, once we are worse than the best, we will stay worse
390  break;
391  }
392  }
393 
394  return score;
395 }
396 
397 } // namespace dwb_core
398 
399 // Register this controller as a nav2_core plugin
400 PLUGINLIB_EXPORT_CLASS(
Plugin-based flexible controller.
void newPathReceived(const nav_msgs::msg::Path &raw_global_path) override
nav2_core newPathReceived - Receives a new plan from the Planner Server
geometry_msgs::msg::TwistStamped computeVelocityCommands(const geometry_msgs::msg::PoseStamped &pose, const geometry_msgs::msg::Twist &velocity, nav2_core::GoalChecker *, const nav_msgs::msg::Path &transformed_global_plan, const geometry_msgs::msg::PoseStamped &global_goal) override
nav2_core computeVelocityCommands - calculates the best command given the current pose and velocity
virtual dwb_msgs::msg::TrajectoryScore coreScoringAlgorithm(const geometry_msgs::msg::Pose &pose, const nav_2d_msgs::msg::Twist2D velocity, std::shared_ptr< dwb_msgs::msg::LocalPlanEvaluation > &results)
Iterate through all the twists and find the best one.
void cleanup() override
Cleanup lifecycle node.
std::string resolveCriticClassName(std::string base_name)
try to resolve a possibly shortened critic name with the default namespaces and the suffix "Critic"
void deactivate() override
Deactivate lifecycle node.
void configure(const nav2::LifecycleNode::WeakPtr &parent, std::string name, nav2::TransformBuffer::SharedPtr tf, std::shared_ptr< nav2_costmap_2d::Costmap2DROS > costmap_ros) override
DWBLocalPlanner()
Constructor that brings up pluginlib loaders.
virtual dwb_msgs::msg::TrajectoryScore scoreTrajectory(const dwb_msgs::msg::Trajectory2D &traj, double best_score=-1)
Score a given command. Can be used for testing.
virtual void loadCritics()
Load the critic parameters from the namespace.
void activate() override
Activate lifecycle node.
Thrown when one of the critics encountered a fatal error.
Definition: exceptions.hpp:50
Thrown when all the trajectories explored are illegal.
controller interface that acts as a virtual base class for all controller plugins
Definition: controller.hpp:60
Function-object for checking whether a goal has been reached.
A 2D costmap provides a mapping between points in the world and their associated "costs".
Definition: costmap_2d.hpp:69