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 <limits>
20 #include <memory>
21 #include <string>
22 #include <tuple>
23 #include <utility>
24 #include <vector>
25 
26 #include "nav2_ros_common/node_utils.hpp"
27 #include "nav2_smac_planner/smac_planner_lattice.hpp"
28 #include "nav2_ros_common/tf2_factories.hpp"
29 
30 // #define BENCHMARK_TESTING
31 
32 namespace nav2_smac_planner
33 {
34 
35 using namespace std::chrono; // NOLINT
36 using rcl_interfaces::msg::ParameterType;
37 
38 template<typename NodeT>
40 : _a_star(nullptr),
41  _collision_checker(nullptr, 1, nullptr),
42  _smoother(nullptr),
43  _costmap(nullptr)
44 {
45 }
46 
47 template<typename NodeT>
49 {
50  RCLCPP_INFO(
51  _logger, "Destroying plugin %s of type SmacPlannerLattice",
52  _name.c_str());
53 }
54 
55 template<typename NodeT>
57  const nav2::LifecycleNode::WeakPtr & parent,
58  std::string name, nav2::TransformBuffer::SharedPtr/*tf*/,
59  std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
60 {
61  _node = parent;
62  auto node = parent.lock();
63  _logger = node->get_logger();
64  _clock = node->get_clock();
65  _costmap = costmap_ros->getCostmap();
66  _costmap_ros = costmap_ros;
67  _name = name;
68  _global_frame = costmap_ros->getGlobalFrameID();
69 
70  RCLCPP_INFO(_logger, "Configuring %s of type SmacPlannerLattice", name.c_str());
71 
72  // General planner params
73  double analytic_expansion_max_length_m;
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.analytic_expansion_max_length =
112  analytic_expansion_max_length_m / _costmap->getResolution();
113  _search_info.use_quadratic_cost_penalty =
114  node->declare_or_get_parameter(name + ".use_quadratic_cost_penalty", false);
115  _search_info.downsample_obstacle_heuristic =
116  node->declare_or_get_parameter(name + ".downsample_obstacle_heuristic", true);
117 
118  _max_planning_time = node->declare_or_get_parameter(name + ".max_planning_time", 5.0);
119  _lookup_table_size = node->declare_or_get_parameter(name + ".lookup_table_size", 20.0);
120  _search_info.allow_reverse_expansion =
121  node->declare_or_get_parameter(name + ".allow_reverse_expansion", false);
122  _debug_visualizations = node->declare_or_get_parameter(name + ".debug_visualizations", false);
123 
124  std::string goal_heading_type =
125  node->declare_or_get_parameter(name + ".goal_heading_mode", std::string("DEFAULT"));
126  _goal_heading_mode = fromStringToGH(goal_heading_type);
127 
128  _coarse_search_resolution =
129  node->declare_or_get_parameter(name + ".coarse_search_resolution", 1);
130 
131  if (_goal_heading_mode == GoalHeadingMode::UNKNOWN) {
132  std::string error_msg = "Unable to get GoalHeader type. Given '" + goal_heading_type + "' "
133  "Valid options are DEFAULT, BIDIRECTIONAL, ALL_DIRECTION. ";
134  throw nav2_core::PlannerException(error_msg);
135  }
136 
137  _metadata = LatticeMotionTable::getLatticeMetadata(_search_info.lattice_filepath);
138  _search_info.minimum_turning_radius =
139  _metadata.min_turning_radius / (_costmap->getResolution());
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>(_costmap->getResolution());
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  // Set collision checker and costmap information
330  _collision_checker.setFootprint(
331  _costmap_ros->getRobotFootprint(),
332  _costmap_ros->getUseRadius(),
333  findCircumscribedCost(_costmap_ros));
334  _a_star->setCollisionChecker(&_collision_checker);
335 
336  // Set starting point, in A* bin search coordinates
337  float mx_start, my_start, mx_goal, my_goal;
338  if (!_costmap->worldToMapContinuous(
339  start.pose.position.x,
340  start.pose.position.y,
341  mx_start,
342  my_start))
343  {
345  "Start Coordinates of(" + std::to_string(start.pose.position.x) + ", " +
346  std::to_string(start.pose.position.y) + ") was outside bounds");
347  }
348  unsigned int start_bin =
349  _a_star->getContext()->motion_table.getClosestAngularBin(tf2::getYaw(start.pose.orientation));
350  _a_star->setStart(mx_start, my_start, start_bin);
351 
352  // Set goal point, in A* bin search coordinates
353  if (!_costmap->worldToMapContinuous(
354  goal.pose.position.x,
355  goal.pose.position.y,
356  mx_goal,
357  my_goal))
358  {
360  "Goal Coordinates of(" + std::to_string(goal.pose.position.x) + ", " +
361  std::to_string(goal.pose.position.y) + ") was outside bounds");
362  }
363  unsigned int goal_bin =
364  _a_star->getContext()->motion_table.getClosestAngularBin(tf2::getYaw(goal.pose.orientation));
365  _a_star->setGoal(
366  mx_goal, my_goal, goal_bin,
367  _goal_heading_mode, _coarse_search_resolution);
368 
369  // Setup message
370  nav_msgs::msg::Path plan;
371  plan.header.stamp = _clock->now();
372  plan.header.frame_id = _global_frame;
373  geometry_msgs::msg::PoseStamped pose;
374  pose.header = plan.header;
375  pose.pose.position.z = 0.0;
376  pose.pose.orientation.x = 0.0;
377  pose.pose.orientation.y = 0.0;
378  pose.pose.orientation.z = 0.0;
379  pose.pose.orientation.w = 1.0;
380 
381  // Corner case of start and goal being on the same cell
382  if (std::floor(mx_start) == std::floor(mx_goal) &&
383  std::floor(my_start) == std::floor(my_goal) &&
384  start_bin == goal_bin)
385  {
386  pose.pose = start.pose;
387  pose.pose.orientation = goal.pose.orientation;
388  plan.poses.push_back(pose);
389 
390  // Publish raw path for debug
391  if (_raw_plan_publisher->get_subscription_count() > 0) {
392  auto msg = std::make_unique<nav_msgs::msg::Path>(plan);
393  _raw_plan_publisher->publish(std::move(msg));
394  }
395 
396  return plan;
397  }
398 
399  // Compute plan
400  typename NodeT::CoordinateVector path;
401  int num_iterations = 0;
402  std::string error;
403  std::unique_ptr<std::vector<std::tuple<float, float, float>>> expansions = nullptr;
404  if (_debug_visualizations) {
405  expansions = std::make_unique<std::vector<std::tuple<float, float, float>>>();
406  }
407 
408  // Note: All exceptions thrown are handled by the planner server and returned to the action
409  if (!_a_star->createPath(
410  path, num_iterations,
411  _tolerance / static_cast<float>(_costmap->getResolution()), cancel_checker, expansions.get()))
412  {
413  if (_debug_visualizations) {
414  auto now = _clock->now();
415  auto msg = std::make_unique<geometry_msgs::msg::PoseArray>();
416  geometry_msgs::msg::Pose msg_pose;
417  msg->header.stamp = now;
418  msg->header.frame_id = _global_frame;
419  for (auto & e : *expansions) {
420  msg_pose.position.x = std::get<0>(e);
421  msg_pose.position.y = std::get<1>(e);
422  msg_pose.orientation = getWorldOrientation(std::get<2>(e));
423  msg->poses.push_back(msg_pose);
424  }
425  _expansions_publisher->publish(std::move(msg));
426  }
427 
428  // Note: If the start is blocked only one iteration will occur before failure
429  if (num_iterations == 1) {
430  throw nav2_core::StartOccupied("Start occupied");
431  }
432 
433  if (num_iterations < _a_star->getMaxIterations()) {
434  throw nav2_core::NoValidPathCouldBeFound("no valid path found");
435  } else {
436  throw nav2_core::PlannerTimedOut("exceeded maximum iterations");
437  }
438  }
439 
440  // Convert to world coordinates
441  plan.poses.reserve(path.size());
442  geometry_msgs::msg::PoseStamped last_pose = pose;
443  for (int i = path.size() - 1; i >= 0; --i) {
444  pose.pose = getWorldCoords(path[i].x, path[i].y, _costmap);
445  pose.pose.orientation = getWorldOrientation(path[i].theta);
446  if (fabs(pose.pose.position.x - last_pose.pose.position.x) < 1e-4 &&
447  fabs(pose.pose.position.y - last_pose.pose.position.y) < 1e-4 &&
448  fabs(tf2::getYaw(pose.pose.orientation) - tf2::getYaw(last_pose.pose.orientation)) < 1e-4)
449  {
450  RCLCPP_DEBUG(
451  _logger,
452  "Removed a path from the path due to replication. "
453  "Make sure your minimum control set does not contain duplicate values!");
454  continue;
455  }
456  last_pose = pose;
457  plan.poses.push_back(pose);
458  }
459 
460  // Publish raw path for debug
461  if (_raw_plan_publisher->get_subscription_count() > 0) {
462  auto msg = std::make_unique<nav_msgs::msg::Path>(plan);
463  _raw_plan_publisher->publish(std::move(msg));
464  }
465 
466  if (_debug_visualizations) {
467  auto now = _clock->now();
468  // Publish expansions for debug
469  auto msg = std::make_unique<geometry_msgs::msg::PoseArray>();
470  geometry_msgs::msg::Pose msg_pose;
471  msg->header.stamp = now;
472  msg->header.frame_id = _global_frame;
473  for (auto & e : *expansions) {
474  msg_pose.position.x = std::get<0>(e);
475  msg_pose.position.y = std::get<1>(e);
476  msg_pose.orientation = getWorldOrientation(std::get<2>(e));
477  msg->poses.push_back(msg_pose);
478  }
479  _expansions_publisher->publish(std::move(msg));
480 
481  if (_planned_footprints_publisher->get_subscription_count() > 0) {
482  // Clear all markers first
483  auto marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
484  visualization_msgs::msg::Marker clear_all_marker;
485  clear_all_marker.action = visualization_msgs::msg::Marker::DELETEALL;
486  marker_array->markers.push_back(clear_all_marker);
487  _planned_footprints_publisher->publish(std::move(marker_array));
488 
489  // Publish smoothed footprints for debug
490  marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
491  for (size_t i = 0; i < plan.poses.size(); i++) {
492  const std::vector<geometry_msgs::msg::Point> edge =
493  transformFootprintToEdges(plan.poses[i].pose, _costmap_ros->getRobotFootprint());
494  marker_array->markers.push_back(createMarker(edge, i, _global_frame, now));
495  }
496  _planned_footprints_publisher->publish(std::move(marker_array));
497  }
498  }
499 
500  // Find how much time we have left to do smoothing
501  steady_clock::time_point b = steady_clock::now();
502  duration<double> time_span = duration_cast<duration<double>>(b - a);
503  double time_remaining = _max_planning_time - static_cast<double>(time_span.count());
504 
505 #ifdef BENCHMARK_TESTING
506  std::cout << "It took " << time_span.count() * 1000 <<
507  " milliseconds with " << num_iterations << " iterations." << std::endl;
508 #endif
509 
510  // Smooth plan
511  if (_smoother && num_iterations > 1) {
512  _smoother->smooth(
513  plan,
514  _costmap,
515  time_remaining,
516  _costmap_ros->getUseRadius() ? std::vector<geometry_msgs::msg::Point>() :
517  _costmap_ros->getRobotFootprint());
518  }
519 
520 #ifdef BENCHMARK_TESTING
521  steady_clock::time_point c = steady_clock::now();
522  duration<double> time_span2 = duration_cast<duration<double>>(c - b);
523  std::cout << "It took " << time_span2.count() * 1000 <<
524  " milliseconds to smooth path." << std::endl;
525 #endif
526 
527  if (_debug_visualizations) {
528  if (_smoothed_footprints_publisher->get_subscription_count() > 0) {
529  // Clear all markers first
530  auto marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
531  visualization_msgs::msg::Marker clear_all_marker;
532  clear_all_marker.action = visualization_msgs::msg::Marker::DELETEALL;
533  marker_array->markers.push_back(clear_all_marker);
534  _smoothed_footprints_publisher->publish(std::move(marker_array));
535 
536  // Publish smoothed footprints for debug
537  marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
538  auto now = _clock->now();
539  for (size_t i = 0; i < plan.poses.size(); i++) {
540  const std::vector<geometry_msgs::msg::Point> edge =
541  transformFootprintToEdges(plan.poses[i].pose, _costmap_ros->getRobotFootprint());
542  marker_array->markers.push_back(createMarker(edge, i, _global_frame, now));
543  }
544  _smoothed_footprints_publisher->publish(std::move(marker_array));
545  }
546  }
547 
548  return plan;
549 }
550 
551 template<typename NodeT>
552 rcl_interfaces::msg::SetParametersResult
554  const std::vector<rclcpp::Parameter> & parameters)
555 {
556  rcl_interfaces::msg::SetParametersResult result;
557  result.successful = true;
558  for (const auto & parameter : parameters) {
559  const auto & param_type = parameter.get_type();
560  const auto & param_name = parameter.get_name();
561  if (param_name.find(_name + ".") != 0) {
562  continue;
563  }
564  if (param_type == ParameterType::PARAMETER_DOUBLE) {
565  if (parameter.as_double() < 0.0) {
566  RCLCPP_WARN(
567  _logger, "The value of parameter '%s' is incorrectly set to %f, "
568  "it should be >=0. Ignoring parameter update.",
569  param_name.c_str(), parameter.as_double());
570  result.successful = false;
571  }
572  } else if (param_type == ParameterType::PARAMETER_INTEGER) {
573  if (param_name == _name + ".coarse_search_resolution" && _metadata.number_of_headings %
574  parameter.as_int() != 0)
575  {
576  RCLCPP_WARN(
577  _logger,
578  "coarse iteration should be an increment of the number"
579  " of angular bins configured. Ignoring parameter update!"
580  );
581  result.successful = false;
582  } else if (parameter.as_int() <= 0 && (param_name != _name + ".max_iterations" && // NOLINT
583  param_name != _name + ".max_on_approach_iterations"))
584  {
585  RCLCPP_WARN(
586  _logger, "The value of parameter '%s' is incorrectly set to %ld, "
587  "it should be >0. Ignoring parameter update.",
588  param_name.c_str(), parameter.as_int());
589  result.successful = false;
590  }
591  } else if (param_type == ParameterType::PARAMETER_STRING) {
592  if (param_name == _name + ".lattice_filepath") {
593  LatticeMetadata metadata =
594  LatticeMotionTable::getLatticeMetadata(parameter.as_string());
595  if (metadata.number_of_headings % _coarse_search_resolution != 0) {
596  RCLCPP_WARN(
597  _logger,
598  "coarse iteration should be an increment of the number "
599  "of angular bins configured. Ignoring parameter update!"
600  );
601  result.successful = false;
602  }
603  } else if (param_name == _name + ".goal_heading_mode") {
604  GoalHeadingMode goal_heading_mode = fromStringToGH(parameter.as_string());
605  if (goal_heading_mode == GoalHeadingMode::UNKNOWN) {
606  RCLCPP_WARN(
607  _logger,
608  "Unable to get GoalHeader type. Given '%s' Valid options are "
609  "DEFAULT, BIDIRECTIONAL, ALL_DIRECTION. Ignoring parameter update!",
610  parameter.as_string().c_str());
611  result.successful = false;
612  }
613  }
614  }
615  }
616  return result;
617 }
618 
619 template<typename NodeT>
620 void
622  const std::vector<rclcpp::Parameter> & parameters)
623 {
624  std::lock_guard<std::mutex> lock_reinit(_mutex);
625 
626  bool reinit_a_star = false;
627  bool reinit_lookup_table = false;
628  bool reinit_smoother = false;
629 
630  for (auto parameter : parameters) {
631  const auto & param_type = parameter.get_type();
632  const auto & param_name = parameter.get_name();
633  if (param_name.find(_name + ".") != 0) {
634  continue;
635  }
636  if (param_type == ParameterType::PARAMETER_DOUBLE) {
637  if (param_name == _name + ".max_planning_time") {
638  reinit_a_star = true;
639  _max_planning_time = parameter.as_double();
640  } else if (param_name == _name + ".tolerance") {
641  _tolerance = static_cast<float>(parameter.as_double());
642  } else if (param_name == _name + ".lookup_table_size") {
643  reinit_a_star = true;
644  reinit_lookup_table = true;
645  _lookup_table_size = parameter.as_double();
646  } else if (param_name == _name + ".reverse_penalty") {
647  reinit_a_star = true;
648  _search_info.reverse_penalty = static_cast<float>(parameter.as_double());
649  } else if (param_name == _name + ".change_penalty") {
650  reinit_a_star = true;
651  _search_info.change_penalty = static_cast<float>(parameter.as_double());
652  } else if (param_name == _name + ".non_straight_penalty") {
653  reinit_a_star = true;
654  _search_info.non_straight_penalty = static_cast<float>(parameter.as_double());
655  } else if (param_name == _name + ".cost_penalty") {
656  reinit_a_star = true;
657  _search_info.cost_penalty = static_cast<float>(parameter.as_double());
658  } else if (param_name == _name + ".rotation_penalty") {
659  reinit_a_star = true;
660  _search_info.rotation_penalty = static_cast<float>(parameter.as_double());
661  } else if (param_name == _name + ".analytic_expansion_ratio") {
662  reinit_a_star = true;
663  _search_info.analytic_expansion_ratio = static_cast<float>(parameter.as_double());
664  } else if (param_name == _name + ".analytic_expansion_max_length") {
665  reinit_a_star = true;
666  _search_info.analytic_expansion_max_length =
667  static_cast<float>(parameter.as_double()) / _costmap->getResolution();
668  } else if (param_name == _name + ".analytic_expansion_max_cost") {
669  reinit_a_star = true;
670  _search_info.analytic_expansion_max_cost = static_cast<float>(parameter.as_double());
671  }
672  } else if (param_type == ParameterType::PARAMETER_BOOL) {
673  if (param_name == _name + ".allow_unknown") {
674  reinit_a_star = true;
675  _allow_unknown = parameter.as_bool();
676  } else if (param_name == _name + ".cache_obstacle_heuristic") {
677  reinit_a_star = true;
678  _search_info.cache_obstacle_heuristic = parameter.as_bool();
679  } else if (param_name == _name + ".allow_reverse_expansion") {
680  reinit_a_star = true;
681  reinit_lookup_table = true;
682  _search_info.allow_reverse_expansion = parameter.as_bool();
683  } else if (param_name == _name + ".smooth_path") {
684  if (parameter.as_bool()) {
685  reinit_smoother = true;
686  } else {
687  _smoother.reset();
688  }
689  } else if (param_name == _name + ".analytic_expansion_max_cost_override") {
690  _search_info.analytic_expansion_max_cost_override = parameter.as_bool();
691  reinit_a_star = true;
692  }
693  } else if (param_type == ParameterType::PARAMETER_INTEGER) {
694  if (param_name == _name + ".max_iterations") {
695  reinit_a_star = true;
696  _max_iterations = parameter.as_int();
697  if (_max_iterations <= 0) {
698  RCLCPP_INFO(
699  _logger, "maximum iteration selected as <= 0, "
700  "disabling maximum iterations.");
701  _max_iterations = std::numeric_limits<int>::max();
702  }
703  } else if (param_name == _name + ".max_on_approach_iterations") {
704  reinit_a_star = true;
705  _max_on_approach_iterations = parameter.as_int();
706  if (_max_on_approach_iterations <= 0) {
707  RCLCPP_INFO(
708  _logger, "On approach iteration selected as <= 0, "
709  "disabling tolerance and on approach iterations.");
710  _max_on_approach_iterations = std::numeric_limits<int>::max();
711  }
712  } else if (param_name == _name + ".terminal_checking_interval") {
713  reinit_a_star = true;
714  _terminal_checking_interval = parameter.as_int();
715  } else if (param_name == _name + ".coarse_search_resolution") {
716  _coarse_search_resolution = parameter.as_int();
717  }
718  } else if (param_type == ParameterType::PARAMETER_STRING) {
719  if (param_name == _name + ".lattice_filepath") {
720  reinit_a_star = true;
721  reinit_lookup_table = true;
722  if (_smoother) {
723  reinit_smoother = true;
724  }
725  _search_info.lattice_filepath = parameter.as_string();
726  _metadata = LatticeMotionTable::getLatticeMetadata(_search_info.lattice_filepath);
727  _search_info.minimum_turning_radius =
728  _metadata.min_turning_radius / (_costmap->getResolution());
729  } else if (param_name == _name + ".goal_heading_mode") {
730  std::string goal_heading_type = parameter.as_string();
731  RCLCPP_INFO(
732  _logger,
733  "GoalHeadingMode type set to '%s'.",
734  goal_heading_type.c_str());
735  _goal_heading_mode = fromStringToGH(goal_heading_type);
736  }
737  }
738  }
739 
740  // Re-init if needed with mutex lock (to avoid re-init while creating a plan)
741  if (reinit_a_star || reinit_smoother) {
742  // convert to grid coordinates
743  _search_info.minimum_turning_radius =
744  _metadata.min_turning_radius / (_costmap->getResolution());
745  float lookup_table_dim =
746  static_cast<float>(_lookup_table_size) /
747  static_cast<float>(_costmap->getResolution());
748 
749  // Make sure its a whole number
750  lookup_table_dim = static_cast<float>(static_cast<int>(lookup_table_dim));
751 
752  // Make sure its an odd number
753  if (static_cast<int>(lookup_table_dim) % 2 == 0) {
754  RCLCPP_INFO(
755  _logger,
756  "Even sized heuristic lookup table size set %f, increasing size by 1 to make odd",
757  lookup_table_dim);
758  lookup_table_dim += 1.0;
759  }
760 
761  // Re-Initialize smoother
762  if (reinit_smoother) {
763  auto node = _node.lock();
764  SmootherParams params;
765  params.get(node, _name);
766  if (_metadata.motion_model == "omni") {
767  params.holonomic_ = true;
768  }
769  _smoother = std::make_unique<Smoother>(params);
770  _smoother->initialize(_metadata.min_turning_radius);
771  }
772 
773  // Re-Initialize A* template
774  if (reinit_a_star) {
775  if (_metadata.motion_model == "omni" && _search_info.allow_reverse_expansion) {
776  RCLCPP_WARN(
777  _logger,
778  "allow_reverse_expansion is not applicable for omnidirectional robots. Disabling.");
779  _search_info.allow_reverse_expansion = false;
780  }
781  if (reinit_lookup_table) {
782  _a_star = std::make_unique<AStarAlgorithm<NodeT>>(_motion_model, _search_info);
783  } else {
784  _a_star->setSearchInfo(_search_info);
785  }
786  _a_star->initialize(
787  _allow_unknown,
788  _max_iterations,
789  _max_on_approach_iterations,
790  _terminal_checking_interval,
791  _max_planning_time,
792  lookup_table_dim,
793  _metadata.number_of_headings);
794  }
795  }
796 }
797 
798 } // namespace nav2_smac_planner
799 
800 #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