Nav2 Navigation Stack - jazzy  jazzy
ROS 2 Navigation Stack
smac_planner_lattice.cpp
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 #include <string>
16 #include <memory>
17 #include <vector>
18 #include <algorithm>
19 #include <limits>
20 
21 #include "Eigen/Core"
22 #include "nav2_smac_planner/smac_planner_lattice.hpp"
23 
24 // #define BENCHMARK_TESTING
25 
26 namespace nav2_smac_planner
27 {
28 
29 using namespace std::chrono; // NOLINT
30 using rcl_interfaces::msg::ParameterType;
31 
33 : _a_star(nullptr),
34  _collision_checker(nullptr, 1, nullptr),
35  _smoother(nullptr),
36  _costmap(nullptr)
37 {
38 }
39 
41 {
42  RCLCPP_INFO(
43  _logger, "Destroying plugin %s of type SmacPlannerLattice",
44  _name.c_str());
45 }
46 
48  const rclcpp_lifecycle::LifecycleNode::WeakPtr & parent,
49  std::string name, std::shared_ptr<tf2_ros::Buffer>/*tf*/,
50  std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
51 {
52  _node = parent;
53  auto node = parent.lock();
54  _logger = node->get_logger();
55  _clock = node->get_clock();
56  _costmap = costmap_ros->getCostmap();
57  _costmap_ros = costmap_ros;
58  _name = name;
59  _global_frame = costmap_ros->getGlobalFrameID();
60 
61  RCLCPP_INFO(_logger, "Configuring %s of type SmacPlannerLattice", name.c_str());
62 
63  // General planner params
64  double analytic_expansion_max_length_m;
65  bool smooth_path;
66 
67  nav2_util::declare_parameter_if_not_declared(
68  node, name + ".tolerance", rclcpp::ParameterValue(0.25));
69  _tolerance = static_cast<float>(node->get_parameter(name + ".tolerance").as_double());
70  nav2_util::declare_parameter_if_not_declared(
71  node, name + ".allow_unknown", rclcpp::ParameterValue(true));
72  node->get_parameter(name + ".allow_unknown", _allow_unknown);
73  nav2_util::declare_parameter_if_not_declared(
74  node, name + ".max_iterations", rclcpp::ParameterValue(1000000));
75  node->get_parameter(name + ".max_iterations", _max_iterations);
76  nav2_util::declare_parameter_if_not_declared(
77  node, name + ".max_on_approach_iterations", rclcpp::ParameterValue(1000));
78  node->get_parameter(name + ".max_on_approach_iterations", _max_on_approach_iterations);
79  nav2_util::declare_parameter_if_not_declared(
80  node, name + ".terminal_checking_interval", rclcpp::ParameterValue(5000));
81  node->get_parameter(name + ".terminal_checking_interval", _terminal_checking_interval);
82  nav2_util::declare_parameter_if_not_declared(
83  node, name + ".smooth_path", rclcpp::ParameterValue(true));
84  node->get_parameter(name + ".smooth_path", smooth_path);
85 
86  // Default to a well rounded model: 16 bin, 0.4m turning radius, ackermann model
87  nav2_util::declare_parameter_if_not_declared(
88  node, name + ".lattice_filepath", rclcpp::ParameterValue(
89  ament_index_cpp::get_package_share_directory("nav2_smac_planner") +
90  "/sample_primitives/5cm_resolution/0.5m_turning_radius/ackermann/output.json"));
91  node->get_parameter(name + ".lattice_filepath", _search_info.lattice_filepath);
92  nav2_util::declare_parameter_if_not_declared(
93  node, name + ".cache_obstacle_heuristic", rclcpp::ParameterValue(false));
94  node->get_parameter(name + ".cache_obstacle_heuristic", _search_info.cache_obstacle_heuristic);
95  nav2_util::declare_parameter_if_not_declared(
96  node, name + ".reverse_penalty", rclcpp::ParameterValue(2.0));
97  node->get_parameter(name + ".reverse_penalty", _search_info.reverse_penalty);
98  nav2_util::declare_parameter_if_not_declared(
99  node, name + ".change_penalty", rclcpp::ParameterValue(0.05));
100  node->get_parameter(name + ".change_penalty", _search_info.change_penalty);
101  nav2_util::declare_parameter_if_not_declared(
102  node, name + ".non_straight_penalty", rclcpp::ParameterValue(1.05));
103  node->get_parameter(name + ".non_straight_penalty", _search_info.non_straight_penalty);
104  nav2_util::declare_parameter_if_not_declared(
105  node, name + ".cost_penalty", rclcpp::ParameterValue(2.0));
106  node->get_parameter(name + ".cost_penalty", _search_info.cost_penalty);
107  nav2_util::declare_parameter_if_not_declared(
108  node, name + ".retrospective_penalty", rclcpp::ParameterValue(0.015));
109  node->get_parameter(name + ".retrospective_penalty", _search_info.retrospective_penalty);
110  nav2_util::declare_parameter_if_not_declared(
111  node, name + ".rotation_penalty", rclcpp::ParameterValue(5.0));
112  node->get_parameter(name + ".rotation_penalty", _search_info.rotation_penalty);
113  nav2_util::declare_parameter_if_not_declared(
114  node, name + ".analytic_expansion_ratio", rclcpp::ParameterValue(3.5));
115  node->get_parameter(name + ".analytic_expansion_ratio", _search_info.analytic_expansion_ratio);
116  nav2_util::declare_parameter_if_not_declared(
117  node, name + ".analytic_expansion_max_cost", rclcpp::ParameterValue(200.0));
118  node->get_parameter(
119  name + ".analytic_expansion_max_cost", _search_info.analytic_expansion_max_cost);
120  nav2_util::declare_parameter_if_not_declared(
121  node, name + ".analytic_expansion_max_cost_override", rclcpp::ParameterValue(false));
122  node->get_parameter(
123  name + ".analytic_expansion_max_cost_override",
124  _search_info.analytic_expansion_max_cost_override);
125  nav2_util::declare_parameter_if_not_declared(
126  node, name + ".analytic_expansion_max_length", rclcpp::ParameterValue(3.0));
127  node->get_parameter(name + ".analytic_expansion_max_length", analytic_expansion_max_length_m);
128  _search_info.analytic_expansion_max_length =
129  analytic_expansion_max_length_m / _costmap->getResolution();
130 
131  nav2_util::declare_parameter_if_not_declared(
132  node, name + ".max_planning_time", rclcpp::ParameterValue(5.0));
133  node->get_parameter(name + ".max_planning_time", _max_planning_time);
134  nav2_util::declare_parameter_if_not_declared(
135  node, name + ".lookup_table_size", rclcpp::ParameterValue(20.0));
136  node->get_parameter(name + ".lookup_table_size", _lookup_table_size);
137  nav2_util::declare_parameter_if_not_declared(
138  node, name + ".allow_reverse_expansion", rclcpp::ParameterValue(false));
139  node->get_parameter(name + ".allow_reverse_expansion", _search_info.allow_reverse_expansion);
140  nav2_util::declare_parameter_if_not_declared(
141  node, name + ".debug_visualizations", rclcpp::ParameterValue(false));
142  node->get_parameter(name + ".debug_visualizations", _debug_visualizations);
143 
144  _metadata = LatticeMotionTable::getLatticeMetadata(_search_info.lattice_filepath);
145  _search_info.minimum_turning_radius =
146  _metadata.min_turning_radius / (_costmap->getResolution());
147  _motion_model = MotionModel::STATE_LATTICE;
148 
149  if (_metadata.motion_model == "omni" && _search_info.allow_reverse_expansion) {
150  RCLCPP_WARN(
151  _logger,
152  "allow_reverse_expansion is not applicable for omnidirectional robots. Disabling.");
153  _search_info.allow_reverse_expansion = false;
154  }
155 
156  if (_max_on_approach_iterations <= 0) {
157  RCLCPP_INFO(
158  _logger, "On approach iteration selected as <= 0, "
159  "disabling tolerance and on approach iterations.");
160  _max_on_approach_iterations = std::numeric_limits<int>::max();
161  }
162 
163  if (_max_iterations <= 0) {
164  RCLCPP_INFO(
165  _logger, "maximum iteration selected as <= 0, "
166  "disabling maximum iterations.");
167  _max_iterations = std::numeric_limits<int>::max();
168  }
169 
170  float lookup_table_dim =
171  static_cast<float>(_lookup_table_size) /
172  static_cast<float>(_costmap->getResolution());
173 
174  // Make sure its a whole number
175  lookup_table_dim = static_cast<float>(static_cast<int>(lookup_table_dim));
176 
177  // Make sure its an odd number
178  if (static_cast<int>(lookup_table_dim) % 2 == 0) {
179  RCLCPP_INFO(
180  _logger,
181  "Even sized heuristic lookup table size set %f, increasing size by 1 to make odd",
182  lookup_table_dim);
183  lookup_table_dim += 1.0;
184  }
185 
186  // Initialize collision checker using 72 evenly sized bins instead of the lattice
187  // heading angles. This is done so that we have precomputed angles every 5 degrees.
188  // If we used the sparse lattice headings (usually 16), then when we attempt to collision
189  // check for intermediary points of the primitives, we're forced to round to one of the 16
190  // increments causing "wobbly" checks that could cause larger robots to virtually show collisions
191  // in valid configurations. This approximation helps to bound orientation error for all checks
192  // in exchange for slight inaccuracies in the collision headings in terminal search states.
193  _collision_checker = GridCollisionChecker(_costmap_ros, 72u, node);
194  _collision_checker.setFootprint(
195  costmap_ros->getRobotFootprint(),
196  costmap_ros->getUseRadius(),
197  findCircumscribedCost(costmap_ros));
198 
199  // Initialize A* template
200  _a_star = std::make_unique<AStarAlgorithm<NodeLattice>>(_motion_model, _search_info);
201  _a_star->initialize(
202  _allow_unknown,
203  _max_iterations,
204  _max_on_approach_iterations,
205  _terminal_checking_interval,
206  _max_planning_time,
207  lookup_table_dim,
208  _metadata.number_of_headings);
209 
210  // Initialize path smoother
211  SmootherParams params;
212  params.get(node, name);
213  if (_metadata.motion_model == "omni") {
214  params.holonomic_ = true;
215  }
216  if (smooth_path) {
217  _smoother = std::make_unique<Smoother>(params);
218  _smoother->initialize(_metadata.min_turning_radius);
219  }
220 
221  _raw_plan_publisher = node->create_publisher<nav_msgs::msg::Path>("unsmoothed_plan", 1);
222 
223  if (_debug_visualizations) {
224  _expansions_publisher = node->create_publisher<geometry_msgs::msg::PoseArray>("expansions", 1);
225  _planned_footprints_publisher = node->create_publisher<visualization_msgs::msg::MarkerArray>(
226  "planned_footprints", 1);
227  _smoothed_footprints_publisher =
228  node->create_publisher<visualization_msgs::msg::MarkerArray>(
229  "smoothed_footprints", 1);
230  }
231 
232  RCLCPP_INFO(
233  _logger, "Configured plugin %s of type SmacPlannerLattice with "
234  "maximum iterations %i, max on approach iterations %i, "
235  "and %s. Tolerance %.2f. Using motion model: %s. State lattice file: %s.",
236  _name.c_str(), _max_iterations, _max_on_approach_iterations,
237  _allow_unknown ? "allowing unknown traversal" : "not allowing unknown traversal",
238  _tolerance, toString(_motion_model).c_str(), _search_info.lattice_filepath.c_str());
239 }
240 
242 {
243  RCLCPP_INFO(
244  _logger, "Activating plugin %s of type SmacPlannerLattice",
245  _name.c_str());
246  _raw_plan_publisher->on_activate();
247  if (_debug_visualizations) {
248  _expansions_publisher->on_activate();
249  _planned_footprints_publisher->on_activate();
250  _smoothed_footprints_publisher->on_activate();
251  }
252  auto node = _node.lock();
253  // Add callback for dynamic parameters
254  _dyn_params_handler = node->add_on_set_parameters_callback(
255  std::bind(&SmacPlannerLattice::dynamicParametersCallback, this, std::placeholders::_1));
256 }
257 
259 {
260  RCLCPP_INFO(
261  _logger, "Deactivating plugin %s of type SmacPlannerLattice",
262  _name.c_str());
263  _raw_plan_publisher->on_deactivate();
264  if (_debug_visualizations) {
265  _expansions_publisher->on_deactivate();
266  _planned_footprints_publisher->on_deactivate();
267  _smoothed_footprints_publisher->on_deactivate();
268  }
269  // shutdown dyn_param_handler
270  auto node = _node.lock();
271  if (_dyn_params_handler && node) {
272  node->remove_on_set_parameters_callback(_dyn_params_handler.get());
273  }
274  _dyn_params_handler.reset();
275 }
276 
278 {
279  RCLCPP_INFO(
280  _logger, "Cleaning up plugin %s of type SmacPlannerLattice",
281  _name.c_str());
283  _a_star.reset();
284  _smoother.reset();
285  _raw_plan_publisher.reset();
286  if (_debug_visualizations) {
287  _expansions_publisher.reset();
288  _planned_footprints_publisher.reset();
289  _smoothed_footprints_publisher.reset();
290  }
291 }
292 
293 nav_msgs::msg::Path SmacPlannerLattice::createPlan(
294  const geometry_msgs::msg::PoseStamped & start,
295  const geometry_msgs::msg::PoseStamped & goal,
296  std::function<bool()> cancel_checker)
297 {
298  std::lock_guard<std::mutex> lock_reinit(_mutex);
299  steady_clock::time_point a = steady_clock::now();
300 
301  std::unique_lock<nav2_costmap_2d::Costmap2D::mutex_t> lock(*(_costmap->getMutex()));
302 
303  // Set collision checker and costmap information
304  _collision_checker.setFootprint(
305  _costmap_ros->getRobotFootprint(),
306  _costmap_ros->getUseRadius(),
307  findCircumscribedCost(_costmap_ros));
308  _a_star->setCollisionChecker(&_collision_checker);
309 
310  // Set starting point, in A* bin search coordinates
311  float mx_start, my_start, mx_goal, my_goal;
312  if (!_costmap->worldToMapContinuous(
313  start.pose.position.x,
314  start.pose.position.y,
315  mx_start,
316  my_start))
317  {
319  "Start Coordinates of(" + std::to_string(start.pose.position.x) + ", " +
320  std::to_string(start.pose.position.y) + ") was outside bounds");
321  }
322  _a_star->setStart(
323  mx_start, my_start,
324  NodeLattice::motion_table.getClosestAngularBin(tf2::getYaw(start.pose.orientation)));
325 
326  // Set goal point, in A* bin search coordinates
327  if (!_costmap->worldToMapContinuous(
328  goal.pose.position.x,
329  goal.pose.position.y,
330  mx_goal,
331  my_goal))
332  {
334  "Goal Coordinates of(" + std::to_string(goal.pose.position.x) + ", " +
335  std::to_string(goal.pose.position.y) + ") was outside bounds");
336  }
337  _a_star->setGoal(
338  mx_goal, my_goal,
339  NodeLattice::motion_table.getClosestAngularBin(tf2::getYaw(goal.pose.orientation)));
340 
341  // Setup message
342  nav_msgs::msg::Path plan;
343  plan.header.stamp = _clock->now();
344  plan.header.frame_id = _global_frame;
345  geometry_msgs::msg::PoseStamped pose;
346  pose.header = plan.header;
347  pose.pose.position.z = 0.0;
348  pose.pose.orientation.x = 0.0;
349  pose.pose.orientation.y = 0.0;
350  pose.pose.orientation.z = 0.0;
351  pose.pose.orientation.w = 1.0;
352 
353  // Corner case of start and goal being on the same cell
354  if (std::floor(mx_start) == std::floor(mx_goal) &&
355  std::floor(my_start) == std::floor(my_goal))
356  {
357  pose.pose = start.pose;
358  pose.pose.orientation = goal.pose.orientation;
359  plan.poses.push_back(pose);
360 
361  // Publish raw path for debug
362  if (_raw_plan_publisher->get_subscription_count() > 0) {
363  _raw_plan_publisher->publish(plan);
364  }
365 
366  return plan;
367  }
368 
369  // Compute plan
370  NodeLattice::CoordinateVector path;
371  int num_iterations = 0;
372  std::string error;
373  std::unique_ptr<std::vector<std::tuple<float, float, float>>> expansions = nullptr;
374  if (_debug_visualizations) {
375  expansions = std::make_unique<std::vector<std::tuple<float, float, float>>>();
376  }
377 
378  // Note: All exceptions thrown are handled by the planner server and returned to the action
379  if (!_a_star->createPath(
380  path, num_iterations,
381  _tolerance / static_cast<float>(_costmap->getResolution()), cancel_checker, expansions.get()))
382  {
383  if (_debug_visualizations) {
384  auto now = _clock->now();
385  geometry_msgs::msg::PoseArray msg;
386  geometry_msgs::msg::Pose msg_pose;
387  msg.header.stamp = now;
388  msg.header.frame_id = _global_frame;
389  for (auto & e : *expansions) {
390  msg_pose.position.x = std::get<0>(e);
391  msg_pose.position.y = std::get<1>(e);
392  msg_pose.orientation = getWorldOrientation(std::get<2>(e));
393  msg.poses.push_back(msg_pose);
394  }
395  _expansions_publisher->publish(msg);
396  }
397 
398  // Note: If the start is blocked only one iteration will occur before failure
399  if (num_iterations == 1) {
400  throw nav2_core::StartOccupied("Start occupied");
401  }
402 
403  if (num_iterations < _a_star->getMaxIterations()) {
404  throw nav2_core::NoValidPathCouldBeFound("no valid path found");
405  } else {
406  throw nav2_core::PlannerTimedOut("exceeded maximum iterations");
407  }
408  }
409 
410  // Convert to world coordinates
411  plan.poses.reserve(path.size());
412  geometry_msgs::msg::PoseStamped last_pose = pose;
413  for (int i = path.size() - 1; i >= 0; --i) {
414  pose.pose = getWorldCoords(path[i].x, path[i].y, _costmap);
415  pose.pose.orientation = getWorldOrientation(path[i].theta);
416  if (fabs(pose.pose.position.x - last_pose.pose.position.x) < 1e-4 &&
417  fabs(pose.pose.position.y - last_pose.pose.position.y) < 1e-4 &&
418  fabs(tf2::getYaw(pose.pose.orientation) - tf2::getYaw(last_pose.pose.orientation)) < 1e-4)
419  {
420  RCLCPP_DEBUG(
421  _logger,
422  "Removed a path from the path due to replication. "
423  "Make sure your minimum control set does not contain duplicate values!");
424  continue;
425  }
426  last_pose = pose;
427  plan.poses.push_back(pose);
428  }
429 
430  // Publish raw path for debug
431  if (_raw_plan_publisher->get_subscription_count() > 0) {
432  _raw_plan_publisher->publish(plan);
433  }
434 
435  if (_debug_visualizations) {
436  auto now = _clock->now();
437  // Publish expansions for debug
438  geometry_msgs::msg::PoseArray msg;
439  geometry_msgs::msg::Pose msg_pose;
440  msg.header.stamp = now;
441  msg.header.frame_id = _global_frame;
442  for (auto & e : *expansions) {
443  msg_pose.position.x = std::get<0>(e);
444  msg_pose.position.y = std::get<1>(e);
445  msg_pose.orientation = getWorldOrientation(std::get<2>(e));
446  msg.poses.push_back(msg_pose);
447  }
448  _expansions_publisher->publish(msg);
449 
450  if (_planned_footprints_publisher->get_subscription_count() > 0) {
451  // Clear all markers first
452  auto marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
453  visualization_msgs::msg::Marker clear_all_marker;
454  clear_all_marker.action = visualization_msgs::msg::Marker::DELETEALL;
455  marker_array->markers.push_back(clear_all_marker);
456  _planned_footprints_publisher->publish(std::move(marker_array));
457 
458  // Publish smoothed footprints for debug
459  marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
460  for (size_t i = 0; i < plan.poses.size(); i++) {
461  const std::vector<geometry_msgs::msg::Point> edge =
462  transformFootprintToEdges(plan.poses[i].pose, _costmap_ros->getRobotFootprint());
463  marker_array->markers.push_back(createMarker(edge, i, _global_frame, now));
464  }
465  _planned_footprints_publisher->publish(std::move(marker_array));
466  }
467  }
468 
469  // Find how much time we have left to do smoothing
470  steady_clock::time_point b = steady_clock::now();
471  duration<double> time_span = duration_cast<duration<double>>(b - a);
472  double time_remaining = _max_planning_time - static_cast<double>(time_span.count());
473 
474 #ifdef BENCHMARK_TESTING
475  std::cout << "It took " << time_span.count() * 1000 <<
476  " milliseconds with " << num_iterations << " iterations." << std::endl;
477 #endif
478 
479  // Smooth plan
480  if (_smoother && num_iterations > 1) {
481  _smoother->smooth(plan, _costmap, time_remaining);
482  }
483 
484 #ifdef BENCHMARK_TESTING
485  steady_clock::time_point c = steady_clock::now();
486  duration<double> time_span2 = duration_cast<duration<double>>(c - b);
487  std::cout << "It took " << time_span2.count() * 1000 <<
488  " milliseconds to smooth path." << std::endl;
489 #endif
490 
491  if (_debug_visualizations) {
492  if (_smoothed_footprints_publisher->get_subscription_count() > 0) {
493  // Clear all markers first
494  auto marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
495  visualization_msgs::msg::Marker clear_all_marker;
496  clear_all_marker.action = visualization_msgs::msg::Marker::DELETEALL;
497  marker_array->markers.push_back(clear_all_marker);
498  _smoothed_footprints_publisher->publish(std::move(marker_array));
499 
500  // Publish smoothed footprints for debug
501  marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
502  auto now = _clock->now();
503  for (size_t i = 0; i < plan.poses.size(); i++) {
504  const std::vector<geometry_msgs::msg::Point> edge =
505  transformFootprintToEdges(plan.poses[i].pose, _costmap_ros->getRobotFootprint());
506  marker_array->markers.push_back(createMarker(edge, i, _global_frame, now));
507  }
508  _smoothed_footprints_publisher->publish(std::move(marker_array));
509  }
510  }
511 
512  return plan;
513 }
514 
515 rcl_interfaces::msg::SetParametersResult
516 SmacPlannerLattice::dynamicParametersCallback(std::vector<rclcpp::Parameter> parameters)
517 {
518  rcl_interfaces::msg::SetParametersResult result;
519  std::lock_guard<std::mutex> lock_reinit(_mutex);
520 
521  bool reinit_a_star = false;
522  bool reinit_smoother = false;
523 
524  for (auto parameter : parameters) {
525  const auto & type = parameter.get_type();
526  const auto & name = parameter.get_name();
527 
528  if (type == ParameterType::PARAMETER_DOUBLE) {
529  if (name == _name + ".max_planning_time") {
530  reinit_a_star = true;
531  _max_planning_time = parameter.as_double();
532  } else if (name == _name + ".tolerance") {
533  _tolerance = static_cast<float>(parameter.as_double());
534  } else if (name == _name + ".lookup_table_size") {
535  reinit_a_star = true;
536  _lookup_table_size = parameter.as_double();
537  } else if (name == _name + ".reverse_penalty") {
538  reinit_a_star = true;
539  _search_info.reverse_penalty = static_cast<float>(parameter.as_double());
540  } else if (name == _name + ".change_penalty") {
541  reinit_a_star = true;
542  _search_info.change_penalty = static_cast<float>(parameter.as_double());
543  } else if (name == _name + ".non_straight_penalty") {
544  reinit_a_star = true;
545  _search_info.non_straight_penalty = static_cast<float>(parameter.as_double());
546  } else if (name == _name + ".cost_penalty") {
547  reinit_a_star = true;
548  _search_info.cost_penalty = static_cast<float>(parameter.as_double());
549  } else if (name == _name + ".rotation_penalty") {
550  reinit_a_star = true;
551  _search_info.rotation_penalty = static_cast<float>(parameter.as_double());
552  } else if (name == _name + ".analytic_expansion_ratio") {
553  reinit_a_star = true;
554  _search_info.analytic_expansion_ratio = static_cast<float>(parameter.as_double());
555  } else if (name == _name + ".analytic_expansion_max_length") {
556  reinit_a_star = true;
557  _search_info.analytic_expansion_max_length =
558  static_cast<float>(parameter.as_double()) / _costmap->getResolution();
559  } else if (name == _name + ".analytic_expansion_max_cost") {
560  reinit_a_star = true;
561  _search_info.analytic_expansion_max_cost = static_cast<float>(parameter.as_double());
562  }
563  } else if (type == ParameterType::PARAMETER_BOOL) {
564  if (name == _name + ".allow_unknown") {
565  reinit_a_star = true;
566  _allow_unknown = parameter.as_bool();
567  } else if (name == _name + ".cache_obstacle_heuristic") {
568  reinit_a_star = true;
569  _search_info.cache_obstacle_heuristic = parameter.as_bool();
570  } else if (name == _name + ".allow_reverse_expansion") {
571  reinit_a_star = true;
572  _search_info.allow_reverse_expansion = parameter.as_bool();
573  } else if (name == _name + ".smooth_path") {
574  if (parameter.as_bool()) {
575  reinit_smoother = true;
576  } else {
577  _smoother.reset();
578  }
579  } else if (name == _name + ".analytic_expansion_max_cost_override") {
580  _search_info.analytic_expansion_max_cost_override = parameter.as_bool();
581  reinit_a_star = true;
582  }
583  } else if (type == ParameterType::PARAMETER_INTEGER) {
584  if (name == _name + ".max_iterations") {
585  reinit_a_star = true;
586  _max_iterations = parameter.as_int();
587  if (_max_iterations <= 0) {
588  RCLCPP_INFO(
589  _logger, "maximum iteration selected as <= 0, "
590  "disabling maximum iterations.");
591  _max_iterations = std::numeric_limits<int>::max();
592  }
593  } else if (name == _name + ".max_on_approach_iterations") {
594  reinit_a_star = true;
595  _max_on_approach_iterations = parameter.as_int();
596  if (_max_on_approach_iterations <= 0) {
597  RCLCPP_INFO(
598  _logger, "On approach iteration selected as <= 0, "
599  "disabling tolerance and on approach iterations.");
600  _max_on_approach_iterations = std::numeric_limits<int>::max();
601  }
602  } else if (name == _name + ".terminal_checking_interval") {
603  reinit_a_star = true;
604  _terminal_checking_interval = parameter.as_int();
605  }
606  } else if (type == ParameterType::PARAMETER_STRING) {
607  if (name == _name + ".lattice_filepath") {
608  reinit_a_star = true;
609  if (_smoother) {
610  reinit_smoother = true;
611  }
612  _search_info.lattice_filepath = parameter.as_string();
613  _metadata = LatticeMotionTable::getLatticeMetadata(_search_info.lattice_filepath);
614  _search_info.minimum_turning_radius =
615  _metadata.min_turning_radius / (_costmap->getResolution());
616  }
617  }
618  }
619 
620  // Re-init if needed with mutex lock (to avoid re-init while creating a plan)
621  if (reinit_a_star || reinit_smoother) {
622  // convert to grid coordinates
623  _search_info.minimum_turning_radius =
624  _metadata.min_turning_radius / (_costmap->getResolution());
625  float lookup_table_dim =
626  static_cast<float>(_lookup_table_size) /
627  static_cast<float>(_costmap->getResolution());
628 
629  // Make sure its a whole number
630  lookup_table_dim = static_cast<float>(static_cast<int>(lookup_table_dim));
631 
632  // Make sure its an odd number
633  if (static_cast<int>(lookup_table_dim) % 2 == 0) {
634  RCLCPP_INFO(
635  _logger,
636  "Even sized heuristic lookup table size set %f, increasing size by 1 to make odd",
637  lookup_table_dim);
638  lookup_table_dim += 1.0;
639  }
640 
641  // Re-Initialize smoother
642  if (reinit_smoother) {
643  auto node = _node.lock();
644  SmootherParams params;
645  params.get(node, _name);
646  if (_metadata.motion_model == "omni") {
647  params.holonomic_ = true;
648  }
649  _smoother = std::make_unique<Smoother>(params);
650  _smoother->initialize(_metadata.min_turning_radius);
651  }
652 
653  // Re-Initialize A* template
654  if (reinit_a_star) {
655  if (_metadata.motion_model == "omni" && _search_info.allow_reverse_expansion) {
656  RCLCPP_WARN(
657  _logger,
658  "allow_reverse_expansion is not applicable for omnidirectional robots. Disabling.");
659  _search_info.allow_reverse_expansion = false;
660  }
661  _a_star = std::make_unique<AStarAlgorithm<NodeLattice>>(_motion_model, _search_info);
662  _a_star->initialize(
663  _allow_unknown,
664  _max_iterations,
665  _max_on_approach_iterations,
666  _terminal_checking_interval,
667  _max_planning_time,
668  lookup_table_dim,
669  _metadata.number_of_headings);
670  }
671  }
672 
673  result.successful = true;
674  return result;
675 }
676 
677 } // namespace nav2_smac_planner
678 
679 #include "pluginlib/class_list_macros.hpp"
Abstract interface for global planners to adhere to with pluginlib.
double getResolution() const
Accessor for the resolution of the costmap.
Definition: costmap_2d.cpp:544
bool worldToMapContinuous(double wx, double wy, float &mx, float &my) const
Convert from world coordinates to map coordinates.
Definition: costmap_2d.cpp:300
A costmap grid collision checker.
void setFootprint(const nav2_costmap_2d::Footprint &footprint, const bool &radius, const double &possible_collision_cost)
A constructor for nav2_smac_planner::GridCollisionChecker for use when irregular bin intervals are ap...
static void destroyStaticAssets()
Destroy shared pointer assets at the end of the process that don't require normal destruction handlin...
void deactivate() override
Deactivate lifecycle node.
void configure(const rclcpp_lifecycle::LifecycleNode::WeakPtr &parent, std::string name, std::shared_ptr< tf2_ros::Buffer > tf, std::shared_ptr< nav2_costmap_2d::Costmap2DROS > costmap_ros) override
Configuring plugin.
nav_msgs::msg::Path createPlan(const geometry_msgs::msg::PoseStamped &start, const geometry_msgs::msg::PoseStamped &goal, std::function< bool()> cancel_checker) override
Creating a plan from start and goal poses.
rcl_interfaces::msg::SetParametersResult dynamicParametersCallback(std::vector< rclcpp::Parameter > parameters)
Callback executed when a paramter change is detected.
void cleanup() override
Cleanup lifecycle node.
void activate() override
Activate lifecycle node.
static LatticeMetadata getLatticeMetadata(const std::string &lattice_filepath)
Get file metadata needed.
Parameters for the smoother cost function.
void get(std::shared_ptr< rclcpp_lifecycle::LifecycleNode > node, const std::string &name)
Get params from ROS parameter.
Definition: types.hpp:75