Nav2 Navigation Stack - rolling  main
ROS 2 Navigation Stack
smac_planner_lattice_impl.hpp
1 // Copyright (c) 2021, Samsung Research America
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License. Reserved.
14 
15 #ifndef NAV2_SMAC_PLANNER__SMAC_PLANNER_LATTICE_IMPL_HPP_
16 #define NAV2_SMAC_PLANNER__SMAC_PLANNER_LATTICE_IMPL_HPP_
17 
18 #include <algorithm>
19 #include <cmath>
20 #include <limits>
21 #include <memory>
22 #include <string>
23 #include <tuple>
24 #include <utility>
25 #include <vector>
26 
27 #include "nav2_ros_common/node_utils.hpp"
28 #include "nav2_smac_planner/smac_planner_lattice.hpp"
29 #include "nav2_ros_common/tf2_factories.hpp"
30 
31 // #define BENCHMARK_TESTING
32 
33 namespace nav2_smac_planner
34 {
35 
36 using namespace std::chrono; // NOLINT
37 using rcl_interfaces::msg::ParameterType;
38 
39 template<typename NodeT>
41 : _a_star(nullptr),
42  _collision_checker(nullptr, 1, nullptr),
43  _smoother(nullptr),
44  _costmap(nullptr)
45 {
46 }
47 
48 template<typename NodeT>
50 {
51  RCLCPP_INFO(
52  _logger, "Destroying plugin %s of type SmacPlannerLattice",
53  _name.c_str());
54 }
55 
56 template<typename NodeT>
58  const nav2::LifecycleNode::WeakPtr & parent,
59  std::string name, nav2::TransformBuffer::SharedPtr/*tf*/,
60  std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
61 {
62  _node = parent;
63  auto node = parent.lock();
64  _logger = node->get_logger();
65  _clock = node->get_clock();
66  _costmap = costmap_ros->getCostmap();
67  _costmap_ros = costmap_ros;
68  _name = name;
69  _global_frame = costmap_ros->getGlobalFrameID();
70 
71  RCLCPP_INFO(_logger, "Configuring %s of type SmacPlannerLattice", name.c_str());
72 
73  // General planner params
74 
75  _tolerance = static_cast<float>(node->declare_or_get_parameter(name + ".tolerance", 0.25));
76  _allow_unknown = node->declare_or_get_parameter(name + ".allow_unknown", true);
77  _max_iterations = node->declare_or_get_parameter(name + ".max_iterations", 1000000);
78  _max_on_approach_iterations =
79  node->declare_or_get_parameter(name + ".max_on_approach_iterations", 1000);
80  _terminal_checking_interval =
81  node->declare_or_get_parameter(name + ".terminal_checking_interval", 5000);
82  bool smooth_path = node->declare_or_get_parameter(name + ".smooth_path", true);
83 
84  // Default to a well rounded model: 16 bin, 0.4m turning radius, ackermann model
85  _search_info.lattice_filepath = node->declare_or_get_parameter(
86  name + ".lattice_filepath",
87  nav2::get_package_share_directory("nav2_smac_planner") +
88  "/sample_primitives/5cm_resolution/0.5m_turning_radius/ackermann/output.json");
89  _search_info.cache_obstacle_heuristic =
90  node->declare_or_get_parameter(name + ".cache_obstacle_heuristic", false);
91  _search_info.reverse_penalty =
92  node->declare_or_get_parameter(name + ".reverse_penalty", 2.0);
93  _search_info.change_penalty =
94  node->declare_or_get_parameter(name + ".change_penalty", 0.05);
95  _search_info.non_straight_penalty =
96  node->declare_or_get_parameter(name + ".non_straight_penalty", 1.05);
97  _search_info.cost_penalty =
98  node->declare_or_get_parameter(name + ".cost_penalty", 2.0);
99  _search_info.retrospective_penalty =
100  node->declare_or_get_parameter(name + ".retrospective_penalty", 0.015);
101  _search_info.rotation_penalty =
102  node->declare_or_get_parameter(name + ".rotation_penalty", 5.0);
103  _search_info.analytic_expansion_ratio =
104  node->declare_or_get_parameter(name + ".analytic_expansion_ratio", 3.5);
105  _search_info.analytic_expansion_max_cost =
106  node->declare_or_get_parameter(name + ".analytic_expansion_max_cost", 200.0);
107  _search_info.analytic_expansion_max_cost_override =
108  node->declare_or_get_parameter(name + ".analytic_expansion_max_cost_override", false);
109  _analytic_expansion_max_length_m =
110  node->declare_or_get_parameter(name + ".analytic_expansion_max_length", 3.0);
111  _search_info.use_quadratic_cost_penalty =
112  node->declare_or_get_parameter(name + ".use_quadratic_cost_penalty", false);
113  _search_info.downsample_obstacle_heuristic =
114  node->declare_or_get_parameter(name + ".downsample_obstacle_heuristic", true);
115 
116  _max_planning_time = node->declare_or_get_parameter(name + ".max_planning_time", 5.0);
117  _lookup_table_size = node->declare_or_get_parameter(name + ".lookup_table_size", 20.0);
118  _search_info.allow_reverse_expansion =
119  node->declare_or_get_parameter(name + ".allow_reverse_expansion", false);
120  _debug_visualizations = node->declare_or_get_parameter(name + ".debug_visualizations", false);
121 
122  std::string goal_heading_type =
123  node->declare_or_get_parameter(name + ".goal_heading_mode", std::string("DEFAULT"));
124  _goal_heading_mode = fromStringToGH(goal_heading_type);
125 
126  _coarse_search_resolution =
127  node->declare_or_get_parameter(name + ".coarse_search_resolution", 1);
128 
129  if (_goal_heading_mode == GoalHeadingMode::UNKNOWN) {
130  std::string error_msg = "Unable to get GoalHeader type. Given '" + goal_heading_type + "' "
131  "Valid options are DEFAULT, BIDIRECTIONAL, ALL_DIRECTION. ";
132  throw nav2_core::PlannerException(error_msg);
133  }
134 
135  _metadata = LatticeMotionTable::getLatticeMetadata(_search_info.lattice_filepath);
136  _search_info.analytic_expansion_max_length =
137  _analytic_expansion_max_length_m / _metadata.grid_resolution;
138  _search_info.minimum_turning_radius =
139  _metadata.min_turning_radius / (_metadata.grid_resolution);
140  _motion_model = MotionModel::STATE_LATTICE;
141 
142  if (_metadata.motion_model == "omni" && _search_info.allow_reverse_expansion) {
143  RCLCPP_WARN(
144  _logger,
145  "allow_reverse_expansion is not applicable for omnidirectional robots. Disabling.");
146  _search_info.allow_reverse_expansion = false;
147  }
148 
149  if (_max_on_approach_iterations <= 0) {
150  RCLCPP_INFO(
151  _logger, "On approach iteration selected as <= 0, "
152  "disabling tolerance and on approach iterations.");
153  _max_on_approach_iterations = std::numeric_limits<int>::max();
154  }
155 
156  if (_max_iterations <= 0) {
157  RCLCPP_INFO(
158  _logger, "maximum iteration selected as <= 0, "
159  "disabling maximum iterations.");
160  _max_iterations = std::numeric_limits<int>::max();
161  }
162 
163  if (_coarse_search_resolution <= 0) {
164  RCLCPP_WARN(
165  _logger, "coarse iteration resolution selected as <= 0, "
166  "disabling coarse iteration resolution search for goal heading"
167  );
168  _coarse_search_resolution = 1;
169  }
170 
171  if (_metadata.number_of_headings % _coarse_search_resolution != 0) {
172  std::string error_msg = "coarse iteration should be an increment of"
173  " the number of angular bins configured";
174  throw nav2_core::PlannerException(error_msg);
175  }
176 
177  float lookup_table_dim =
178  static_cast<float>(_lookup_table_size) /
179  static_cast<float>(_metadata.grid_resolution);
180 
181  // Make sure its a whole number
182  lookup_table_dim = static_cast<float>(static_cast<int>(lookup_table_dim));
183 
184  // Make sure its an odd number
185  if (static_cast<int>(lookup_table_dim) % 2 == 0) {
186  RCLCPP_INFO(
187  _logger,
188  "Even sized heuristic lookup table size set %f, increasing size by 1 to make odd",
189  lookup_table_dim);
190  lookup_table_dim += 1.0;
191  }
192 
193  // Initialize collision checker using 72 evenly sized bins instead of the lattice
194  // heading angles. This is done so that we have precomputed angles every 5 degrees.
195  // If we used the sparse lattice headings (usually 16), then when we attempt to collision
196  // check for intermediary points of the primitives, we're forced to round to one of the 16
197  // increments causing "wobbly" checks that could cause larger robots to virtually show collisions
198  // in valid configurations. This approximation helps to bound orientation error for all checks
199  // in exchange for slight inaccuracies in the collision headings in terminal search states.
200  _collision_checker = GridCollisionChecker(_costmap_ros, 72u, node);
201  _collision_checker.setFootprint(
202  costmap_ros->getRobotFootprint(),
203  costmap_ros->getUseRadius(),
204  findCircumscribedCost(costmap_ros));
205 
206  // Initialize A* template
207  _a_star = std::make_unique<AStarAlgorithm<NodeT>>(_motion_model, _search_info);
208  _a_star->initialize(
209  _allow_unknown,
210  _max_iterations,
211  _max_on_approach_iterations,
212  _terminal_checking_interval,
213  _max_planning_time,
214  lookup_table_dim,
215  _metadata.number_of_headings);
216 
217  // Initialize path smoother
218  SmootherParams params;
219  params.get(node, name);
220  if (_metadata.motion_model == "omni") {
221  params.holonomic_ = true;
222  }
223  if (smooth_path) {
224  _smoother = std::make_unique<Smoother>(params);
225  _smoother->initialize(_metadata.min_turning_radius);
226  }
227 
228  _raw_plan_publisher = node->create_publisher<nav_msgs::msg::Path>("unsmoothed_plan");
229 
230  if (_debug_visualizations) {
231  _expansions_publisher = node->create_publisher<geometry_msgs::msg::PoseArray>("expansions");
232  _planned_footprints_publisher = node->create_publisher<visualization_msgs::msg::MarkerArray>(
233  "planned_footprints");
234  _smoothed_footprints_publisher =
235  node->create_publisher<visualization_msgs::msg::MarkerArray>(
236  "smoothed_footprints");
237  }
238 
239  RCLCPP_INFO(
240  _logger, "Configured plugin %s of type SmacPlannerLattice with "
241  "maximum iterations %i, max on approach iterations %i, "
242  "and %s. Tolerance %.2f. Using motion model: %s. State lattice file: %s.",
243  _name.c_str(), _max_iterations, _max_on_approach_iterations,
244  _allow_unknown ? "allowing unknown traversal" : "not allowing unknown traversal",
245  _tolerance, toString(_motion_model).c_str(), _search_info.lattice_filepath.c_str());
246 }
247 
248 template<typename NodeT>
250 {
251  RCLCPP_INFO(
252  _logger, "Activating plugin %s of type SmacPlannerLattice",
253  _name.c_str());
254  _raw_plan_publisher->on_activate();
255  if (_debug_visualizations) {
256  _expansions_publisher->on_activate();
257  _planned_footprints_publisher->on_activate();
258  _smoothed_footprints_publisher->on_activate();
259  }
260  auto node = _node.lock();
261  // Add callback for dynamic parameters
262  _post_set_params_handler = node->add_post_set_parameters_callback(
263  std::bind(
265  this, std::placeholders::_1));
266  _on_set_params_handler = node->add_on_set_parameters_callback(
267  std::bind(
269  this, std::placeholders::_1));
270 }
271 
272 template<typename NodeT>
274 {
275  RCLCPP_INFO(
276  _logger, "Deactivating plugin %s of type SmacPlannerLattice",
277  _name.c_str());
278  _raw_plan_publisher->on_deactivate();
279  if (_debug_visualizations) {
280  _expansions_publisher->on_deactivate();
281  _planned_footprints_publisher->on_deactivate();
282  _smoothed_footprints_publisher->on_deactivate();
283  }
284  // shutdown dyn_param_handler
285  auto node = _node.lock();
286  if (_post_set_params_handler && node) {
287  node->remove_post_set_parameters_callback(_post_set_params_handler.get());
288  }
289  _post_set_params_handler.reset();
290  if (_on_set_params_handler && node) {
291  node->remove_on_set_parameters_callback(_on_set_params_handler.get());
292  }
293  _on_set_params_handler.reset();
294 }
295 
296 template<typename NodeT>
298 {
299  RCLCPP_INFO(
300  _logger, "Cleaning up plugin %s of type SmacPlannerLattice",
301  _name.c_str());
302  _a_star.reset();
303  _smoother.reset();
304  _raw_plan_publisher.reset();
305  if (_debug_visualizations) {
306  _expansions_publisher.reset();
307  _planned_footprints_publisher.reset();
308  _smoothed_footprints_publisher.reset();
309  }
310 }
311 
312 template<typename NodeT>
314  const geometry_msgs::msg::PoseStamped & start,
315  const geometry_msgs::msg::PoseStamped & goal,
316  const std::vector<geometry_msgs::msg::PoseStamped> & viapoints,
317  std::function<bool()> cancel_checker)
318 {
319  if (!viapoints.empty()) {
320  RCLCPP_WARN(_logger, "Received %zu viapoints, but this planner ignores them",
321  viapoints.size());
322  }
323 
324  std::lock_guard<std::mutex> lock_reinit(_mutex);
325  steady_clock::time_point a = steady_clock::now();
326 
327  std::unique_lock<nav2_costmap_2d::Costmap2D::mutex_t> lock(*(_costmap->getMutex()));
328 
329  if (std::abs(_costmap->getResolution() - _metadata.grid_resolution) > 1e-6) {
331  "Costmap resolution " + std::to_string(_costmap->getResolution()) +
332  " does not match lattice resolution " + std::to_string(_metadata.grid_resolution) +
333  ". Use primitives generated for the actual costmap resolution.");
334  }
335 
336  // Set collision checker and costmap information
337  _collision_checker.setFootprint(
338  _costmap_ros->getRobotFootprint(),
339  _costmap_ros->getUseRadius(),
340  findCircumscribedCost(_costmap_ros));
341  _a_star->setCollisionChecker(&_collision_checker);
342 
343  // Set starting point, in A* bin search coordinates
344  float mx_start, my_start, mx_goal, my_goal;
345  if (!_costmap->worldToMapContinuous(
346  start.pose.position.x,
347  start.pose.position.y,
348  mx_start,
349  my_start))
350  {
352  "Start Coordinates of(" + std::to_string(start.pose.position.x) + ", " +
353  std::to_string(start.pose.position.y) + ") was outside bounds");
354  }
355  unsigned int start_bin =
356  _a_star->getContext()->motion_table.getClosestAngularBin(tf2::getYaw(start.pose.orientation));
357  _a_star->setStart(mx_start, my_start, start_bin);
358 
359  // Set goal point, in A* bin search coordinates
360  if (!_costmap->worldToMapContinuous(
361  goal.pose.position.x,
362  goal.pose.position.y,
363  mx_goal,
364  my_goal))
365  {
367  "Goal Coordinates of(" + std::to_string(goal.pose.position.x) + ", " +
368  std::to_string(goal.pose.position.y) + ") was outside bounds");
369  }
370  unsigned int goal_bin =
371  _a_star->getContext()->motion_table.getClosestAngularBin(tf2::getYaw(goal.pose.orientation));
372  _a_star->setGoal(
373  mx_goal, my_goal, goal_bin,
374  _goal_heading_mode, _coarse_search_resolution);
375 
376  // Setup message
377  nav_msgs::msg::Path plan;
378  plan.header.stamp = _clock->now();
379  plan.header.frame_id = _global_frame;
380  geometry_msgs::msg::PoseStamped pose;
381  pose.header = plan.header;
382  pose.pose.position.z = 0.0;
383  pose.pose.orientation.x = 0.0;
384  pose.pose.orientation.y = 0.0;
385  pose.pose.orientation.z = 0.0;
386  pose.pose.orientation.w = 1.0;
387 
388  // Corner case of start and goal being on the same cell
389  if (std::floor(mx_start) == std::floor(mx_goal) &&
390  std::floor(my_start) == std::floor(my_goal) &&
391  start_bin == goal_bin)
392  {
393  pose.pose = start.pose;
394  pose.pose.orientation = goal.pose.orientation;
395  plan.poses.push_back(pose);
396 
397  // Publish raw path for debug
398  if (_raw_plan_publisher->get_subscription_count() > 0) {
399  auto msg = std::make_unique<nav_msgs::msg::Path>(plan);
400  _raw_plan_publisher->publish(std::move(msg));
401  }
402 
403  return plan;
404  }
405 
406  // Compute plan
407  typename NodeT::CoordinateVector path;
408  int num_iterations = 0;
409  std::string error;
410  std::unique_ptr<std::vector<std::tuple<float, float, float>>> expansions = nullptr;
411  if (_debug_visualizations) {
412  expansions = std::make_unique<std::vector<std::tuple<float, float, float>>>();
413  }
414 
415  // Note: All exceptions thrown are handled by the planner server and returned to the action
416  if (!_a_star->createPath(
417  path, num_iterations,
418  _tolerance / static_cast<float>(_costmap->getResolution()), cancel_checker, expansions.get()))
419  {
420  if (_debug_visualizations) {
421  auto now = _clock->now();
422  auto msg = std::make_unique<geometry_msgs::msg::PoseArray>();
423  geometry_msgs::msg::Pose msg_pose;
424  msg->header.stamp = now;
425  msg->header.frame_id = _global_frame;
426  for (auto & e : *expansions) {
427  msg_pose.position.x = std::get<0>(e);
428  msg_pose.position.y = std::get<1>(e);
429  msg_pose.orientation = getWorldOrientation(std::get<2>(e));
430  msg->poses.push_back(msg_pose);
431  }
432  _expansions_publisher->publish(std::move(msg));
433  }
434 
435  // Note: If the start is blocked only one iteration will occur before failure
436  if (num_iterations == 1) {
437  throw nav2_core::StartOccupied("Start occupied");
438  }
439 
440  if (num_iterations < _a_star->getMaxIterations()) {
441  throw nav2_core::NoValidPathCouldBeFound("no valid path found");
442  } else {
443  throw nav2_core::PlannerTimedOut("exceeded maximum iterations");
444  }
445  }
446 
447  // Convert to world coordinates
448  plan.poses.reserve(path.size());
449  geometry_msgs::msg::PoseStamped last_pose = pose;
450  for (int i = path.size() - 1; i >= 0; --i) {
451  pose.pose = getWorldCoords(path[i].x, path[i].y, _costmap);
452  pose.pose.orientation = getWorldOrientation(path[i].theta);
453  if (fabs(pose.pose.position.x - last_pose.pose.position.x) < 1e-4 &&
454  fabs(pose.pose.position.y - last_pose.pose.position.y) < 1e-4 &&
455  fabs(tf2::getYaw(pose.pose.orientation) - tf2::getYaw(last_pose.pose.orientation)) < 1e-4)
456  {
457  RCLCPP_DEBUG(
458  _logger,
459  "Removed a path from the path due to replication. "
460  "Make sure your minimum control set does not contain duplicate values!");
461  continue;
462  }
463  last_pose = pose;
464  plan.poses.push_back(pose);
465  }
466 
467  // Publish raw path for debug
468  if (_raw_plan_publisher->get_subscription_count() > 0) {
469  auto msg = std::make_unique<nav_msgs::msg::Path>(plan);
470  _raw_plan_publisher->publish(std::move(msg));
471  }
472 
473  if (_debug_visualizations) {
474  auto now = _clock->now();
475  // Publish expansions for debug
476  auto msg = std::make_unique<geometry_msgs::msg::PoseArray>();
477  geometry_msgs::msg::Pose msg_pose;
478  msg->header.stamp = now;
479  msg->header.frame_id = _global_frame;
480  for (auto & e : *expansions) {
481  msg_pose.position.x = std::get<0>(e);
482  msg_pose.position.y = std::get<1>(e);
483  msg_pose.orientation = getWorldOrientation(std::get<2>(e));
484  msg->poses.push_back(msg_pose);
485  }
486  _expansions_publisher->publish(std::move(msg));
487 
488  if (_planned_footprints_publisher->get_subscription_count() > 0) {
489  // Clear all markers first
490  auto marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
491  visualization_msgs::msg::Marker clear_all_marker;
492  clear_all_marker.action = visualization_msgs::msg::Marker::DELETEALL;
493  marker_array->markers.push_back(clear_all_marker);
494  _planned_footprints_publisher->publish(std::move(marker_array));
495 
496  // Publish smoothed footprints for debug
497  marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
498  for (size_t i = 0; i < plan.poses.size(); i++) {
499  const std::vector<geometry_msgs::msg::Point> edge =
500  transformFootprintToEdges(plan.poses[i].pose, _costmap_ros->getRobotFootprint());
501  marker_array->markers.push_back(createMarker(edge, i, _global_frame, now));
502  }
503  _planned_footprints_publisher->publish(std::move(marker_array));
504  }
505  }
506 
507  // Find how much time we have left to do smoothing
508  steady_clock::time_point b = steady_clock::now();
509  duration<double> time_span = duration_cast<duration<double>>(b - a);
510  double time_remaining = _max_planning_time - static_cast<double>(time_span.count());
511 
512 #ifdef BENCHMARK_TESTING
513  std::cout << "It took " << time_span.count() * 1000 <<
514  " milliseconds with " << num_iterations << " iterations." << std::endl;
515 #endif
516 
517  // Smooth plan
518  if (_smoother && num_iterations > 1) {
519  _smoother->smooth(
520  plan,
521  _costmap,
522  time_remaining,
523  _costmap_ros->getUseRadius() ? std::vector<geometry_msgs::msg::Point>() :
524  _costmap_ros->getRobotFootprint());
525  }
526 
527 #ifdef BENCHMARK_TESTING
528  steady_clock::time_point c = steady_clock::now();
529  duration<double> time_span2 = duration_cast<duration<double>>(c - b);
530  std::cout << "It took " << time_span2.count() * 1000 <<
531  " milliseconds to smooth path." << std::endl;
532 #endif
533 
534  if (_debug_visualizations) {
535  if (_smoothed_footprints_publisher->get_subscription_count() > 0) {
536  // Clear all markers first
537  auto marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
538  visualization_msgs::msg::Marker clear_all_marker;
539  clear_all_marker.action = visualization_msgs::msg::Marker::DELETEALL;
540  marker_array->markers.push_back(clear_all_marker);
541  _smoothed_footprints_publisher->publish(std::move(marker_array));
542 
543  // Publish smoothed footprints for debug
544  marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
545  auto now = _clock->now();
546  for (size_t i = 0; i < plan.poses.size(); i++) {
547  const std::vector<geometry_msgs::msg::Point> edge =
548  transformFootprintToEdges(plan.poses[i].pose, _costmap_ros->getRobotFootprint());
549  marker_array->markers.push_back(createMarker(edge, i, _global_frame, now));
550  }
551  _smoothed_footprints_publisher->publish(std::move(marker_array));
552  }
553  }
554 
555  return plan;
556 }
557 
558 template<typename NodeT>
559 rcl_interfaces::msg::SetParametersResult
561  const std::vector<rclcpp::Parameter> & parameters)
562 {
563  rcl_interfaces::msg::SetParametersResult result;
564  result.successful = true;
565  for (const auto & parameter : parameters) {
566  const auto & param_type = parameter.get_type();
567  const auto & param_name = parameter.get_name();
568  if (param_name.find(_name + ".") != 0) {
569  continue;
570  }
571  if (param_type == ParameterType::PARAMETER_DOUBLE) {
572  if (parameter.as_double() < 0.0) {
573  RCLCPP_WARN(
574  _logger, "The value of parameter '%s' is incorrectly set to %f, "
575  "it should be >=0. Ignoring parameter update.",
576  param_name.c_str(), parameter.as_double());
577  result.successful = false;
578  }
579  } else if (param_type == ParameterType::PARAMETER_INTEGER) {
580  if (param_name == _name + ".coarse_search_resolution" && _metadata.number_of_headings %
581  parameter.as_int() != 0)
582  {
583  RCLCPP_WARN(
584  _logger,
585  "coarse iteration should be an increment of the number"
586  " of angular bins configured. Ignoring parameter update!"
587  );
588  result.successful = false;
589  } else if (parameter.as_int() <= 0 && (param_name != _name + ".max_iterations" && // NOLINT
590  param_name != _name + ".max_on_approach_iterations"))
591  {
592  RCLCPP_WARN(
593  _logger, "The value of parameter '%s' is incorrectly set to %ld, "
594  "it should be >0. Ignoring parameter update.",
595  param_name.c_str(), parameter.as_int());
596  result.successful = false;
597  }
598  } else if (param_type == ParameterType::PARAMETER_STRING) {
599  if (param_name == _name + ".lattice_filepath") {
600  LatticeMetadata metadata =
601  LatticeMotionTable::getLatticeMetadata(parameter.as_string());
602  if (metadata.number_of_headings % _coarse_search_resolution != 0) {
603  RCLCPP_WARN(
604  _logger,
605  "coarse iteration should be an increment of the number "
606  "of angular bins configured. Ignoring parameter update!"
607  );
608  result.successful = false;
609  }
610  } else if (param_name == _name + ".goal_heading_mode") {
611  GoalHeadingMode goal_heading_mode = fromStringToGH(parameter.as_string());
612  if (goal_heading_mode == GoalHeadingMode::UNKNOWN) {
613  RCLCPP_WARN(
614  _logger,
615  "Unable to get GoalHeader type. Given '%s' Valid options are "
616  "DEFAULT, BIDIRECTIONAL, ALL_DIRECTION. Ignoring parameter update!",
617  parameter.as_string().c_str());
618  result.successful = false;
619  }
620  }
621  }
622  }
623  return result;
624 }
625 
626 template<typename NodeT>
627 void
629  const std::vector<rclcpp::Parameter> & parameters)
630 {
631  std::lock_guard<std::mutex> lock_reinit(_mutex);
632 
633  bool reinit_a_star = false;
634  bool reinit_lookup_table = false;
635  bool reinit_smoother = false;
636 
637  for (auto parameter : parameters) {
638  const auto & param_type = parameter.get_type();
639  const auto & param_name = parameter.get_name();
640  if (param_name.find(_name + ".") != 0) {
641  continue;
642  }
643  if (param_type == ParameterType::PARAMETER_DOUBLE) {
644  if (param_name == _name + ".max_planning_time") {
645  reinit_a_star = true;
646  _max_planning_time = parameter.as_double();
647  } else if (param_name == _name + ".tolerance") {
648  _tolerance = static_cast<float>(parameter.as_double());
649  } else if (param_name == _name + ".lookup_table_size") {
650  reinit_a_star = true;
651  reinit_lookup_table = true;
652  _lookup_table_size = parameter.as_double();
653  } else if (param_name == _name + ".reverse_penalty") {
654  reinit_a_star = true;
655  _search_info.reverse_penalty = static_cast<float>(parameter.as_double());
656  } else if (param_name == _name + ".change_penalty") {
657  reinit_a_star = true;
658  _search_info.change_penalty = static_cast<float>(parameter.as_double());
659  } else if (param_name == _name + ".non_straight_penalty") {
660  reinit_a_star = true;
661  _search_info.non_straight_penalty = static_cast<float>(parameter.as_double());
662  } else if (param_name == _name + ".cost_penalty") {
663  reinit_a_star = true;
664  _search_info.cost_penalty = static_cast<float>(parameter.as_double());
665  } else if (param_name == _name + ".rotation_penalty") {
666  reinit_a_star = true;
667  _search_info.rotation_penalty = static_cast<float>(parameter.as_double());
668  } else if (param_name == _name + ".analytic_expansion_ratio") {
669  reinit_a_star = true;
670  _search_info.analytic_expansion_ratio = static_cast<float>(parameter.as_double());
671  } else if (param_name == _name + ".analytic_expansion_max_length") {
672  reinit_a_star = true;
673  _analytic_expansion_max_length_m = parameter.as_double();
674  } else if (param_name == _name + ".analytic_expansion_max_cost") {
675  reinit_a_star = true;
676  _search_info.analytic_expansion_max_cost = static_cast<float>(parameter.as_double());
677  }
678  } else if (param_type == ParameterType::PARAMETER_BOOL) {
679  if (param_name == _name + ".allow_unknown") {
680  reinit_a_star = true;
681  _allow_unknown = parameter.as_bool();
682  } else if (param_name == _name + ".cache_obstacle_heuristic") {
683  reinit_a_star = true;
684  _search_info.cache_obstacle_heuristic = parameter.as_bool();
685  } else if (param_name == _name + ".allow_reverse_expansion") {
686  reinit_a_star = true;
687  reinit_lookup_table = true;
688  _search_info.allow_reverse_expansion = parameter.as_bool();
689  } else if (param_name == _name + ".smooth_path") {
690  if (parameter.as_bool()) {
691  reinit_smoother = true;
692  } else {
693  _smoother.reset();
694  }
695  } else if (param_name == _name + ".analytic_expansion_max_cost_override") {
696  _search_info.analytic_expansion_max_cost_override = parameter.as_bool();
697  reinit_a_star = true;
698  }
699  } else if (param_type == ParameterType::PARAMETER_INTEGER) {
700  if (param_name == _name + ".max_iterations") {
701  reinit_a_star = true;
702  _max_iterations = parameter.as_int();
703  if (_max_iterations <= 0) {
704  RCLCPP_INFO(
705  _logger, "maximum iteration selected as <= 0, "
706  "disabling maximum iterations.");
707  _max_iterations = std::numeric_limits<int>::max();
708  }
709  } else if (param_name == _name + ".max_on_approach_iterations") {
710  reinit_a_star = true;
711  _max_on_approach_iterations = parameter.as_int();
712  if (_max_on_approach_iterations <= 0) {
713  RCLCPP_INFO(
714  _logger, "On approach iteration selected as <= 0, "
715  "disabling tolerance and on approach iterations.");
716  _max_on_approach_iterations = std::numeric_limits<int>::max();
717  }
718  } else if (param_name == _name + ".terminal_checking_interval") {
719  reinit_a_star = true;
720  _terminal_checking_interval = parameter.as_int();
721  } else if (param_name == _name + ".coarse_search_resolution") {
722  _coarse_search_resolution = parameter.as_int();
723  }
724  } else if (param_type == ParameterType::PARAMETER_STRING) {
725  if (param_name == _name + ".lattice_filepath") {
726  reinit_a_star = true;
727  reinit_lookup_table = true;
728  if (_smoother) {
729  reinit_smoother = true;
730  }
731  _search_info.lattice_filepath = parameter.as_string();
732  _metadata = LatticeMotionTable::getLatticeMetadata(_search_info.lattice_filepath);
733  _search_info.minimum_turning_radius =
734  _metadata.min_turning_radius / (_metadata.grid_resolution);
735  } else if (param_name == _name + ".goal_heading_mode") {
736  std::string goal_heading_type = parameter.as_string();
737  RCLCPP_INFO(
738  _logger,
739  "GoalHeadingMode type set to '%s'.",
740  goal_heading_type.c_str());
741  _goal_heading_mode = fromStringToGH(goal_heading_type);
742  }
743  }
744  }
745 
746  // Re-init if needed with mutex lock (to avoid re-init while creating a plan)
747  if (reinit_a_star || reinit_smoother) {
748  // Convert using the fixed resolution of the lattice primitives.
749  _search_info.analytic_expansion_max_length =
750  _analytic_expansion_max_length_m / _metadata.grid_resolution;
751  _search_info.minimum_turning_radius =
752  _metadata.min_turning_radius / (_metadata.grid_resolution);
753  float lookup_table_dim =
754  static_cast<float>(_lookup_table_size) /
755  static_cast<float>(_metadata.grid_resolution);
756 
757  // Make sure its a whole number
758  lookup_table_dim = static_cast<float>(static_cast<int>(lookup_table_dim));
759 
760  // Make sure its an odd number
761  if (static_cast<int>(lookup_table_dim) % 2 == 0) {
762  RCLCPP_INFO(
763  _logger,
764  "Even sized heuristic lookup table size set %f, increasing size by 1 to make odd",
765  lookup_table_dim);
766  lookup_table_dim += 1.0;
767  }
768 
769  // Re-Initialize smoother
770  if (reinit_smoother) {
771  auto node = _node.lock();
772  SmootherParams params;
773  params.get(node, _name);
774  if (_metadata.motion_model == "omni") {
775  params.holonomic_ = true;
776  }
777  _smoother = std::make_unique<Smoother>(params);
778  _smoother->initialize(_metadata.min_turning_radius);
779  }
780 
781  // Re-Initialize A* template
782  if (reinit_a_star) {
783  if (_metadata.motion_model == "omni" && _search_info.allow_reverse_expansion) {
784  RCLCPP_WARN(
785  _logger,
786  "allow_reverse_expansion is not applicable for omnidirectional robots. Disabling.");
787  _search_info.allow_reverse_expansion = false;
788  }
789  if (reinit_lookup_table) {
790  _a_star = std::make_unique<AStarAlgorithm<NodeT>>(_motion_model, _search_info);
791  } else {
792  _a_star->setSearchInfo(_search_info);
793  }
794  _a_star->initialize(
795  _allow_unknown,
796  _max_iterations,
797  _max_on_approach_iterations,
798  _terminal_checking_interval,
799  _max_planning_time,
800  lookup_table_dim,
801  _metadata.number_of_headings);
802  }
803  }
804 }
805 
806 } // namespace nav2_smac_planner
807 
808 #endif // NAV2_SMAC_PLANNER__SMAC_PLANNER_LATTICE_IMPL_HPP_
A costmap grid collision checker.
A templated state lattice planner that allows custom node types.
void cleanup() override
Cleanup 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
Configuring plugin.
void activate() override
Activate lifecycle node.
rcl_interfaces::msg::SetParametersResult validateParameterUpdatesCallback(const std::vector< rclcpp::Parameter > &parameters)
Validate incoming parameter updates before applying them. This callback is triggered when one or more...
nav_msgs::msg::Path createPlan(const geometry_msgs::msg::PoseStamped &start, const geometry_msgs::msg::PoseStamped &goal, const std::vector< geometry_msgs::msg::PoseStamped > &viapoints, std::function< bool()> cancel_checker) override
Creating a plan from start and goal poses.
void updateParametersCallback(const std::vector< rclcpp::Parameter > &parameters)
Apply parameter updates after validation This callback is executed when parameters have been successf...
void deactivate() override
Deactivate lifecycle node.
A struct of all lattice metadata.
Definition: types.hpp:155
static LatticeMetadata getLatticeMetadata(const std::string &lattice_filepath)
Get file metadata needed.
Parameters for the smoother cost function.
void get(nav2::LifecycleNode::SharedPtr node, const std::string &name)
Get params from ROS parameter.
Definition: types.hpp:77