16 #ifndef NAV2_SMAC_PLANNER__A_STAR_IMPL_HPP_
17 #define NAV2_SMAC_PLANNER__A_STAR_IMPL_HPP_
27 #include <type_traits>
31 #include "nav2_smac_planner/a_star.hpp"
33 namespace nav2_smac_planner
35 using namespace std::chrono;
37 template<
typename NodeT>
39 const MotionModel & motion_model,
41 : _traverse_unknown(true),
42 _is_initialized(false),
44 _terminal_checking_interval(5000),
45 _max_planning_time(0),
48 _search_info(search_info),
51 _motion_model(motion_model)
53 _graph.reserve(100000);
56 template<
typename NodeT>
61 template<
typename NodeT>
63 const bool & allow_unknown,
65 const int & max_on_approach_iterations,
66 const int & terminal_checking_interval,
67 const double & max_planning_time,
68 const float & lookup_table_size,
69 const unsigned int & dim_3_size)
71 _traverse_unknown = allow_unknown;
72 _max_iterations = max_iterations;
73 _max_on_approach_iterations = max_on_approach_iterations;
74 _terminal_checking_interval = terminal_checking_interval;
75 _max_planning_time = max_planning_time;
77 if constexpr (std::is_base_of_v<Node2D, NodeT>) {
79 _shared_ctx = std::make_shared<NodeContext>();
80 if (dim_3_size != 1) {
81 throw std::runtime_error(
"Node type Node2D cannot be given non-1 dim 3 quantization.");
85 if (!_is_initialized) {
86 _shared_ctx = std::make_shared<NodeContext>();
87 _shared_ctx->distance_heuristic->precomputeDistanceHeuristic(
88 lookup_table_size, _motion_model,
90 _search_info, _shared_ctx->motion_table);
94 _is_initialized =
true;
95 _dim3_size = dim_3_size;
96 _expander = std::make_unique<AnalyticExpansion<NodeT>>(
97 _motion_model, _search_info, _traverse_unknown, _dim3_size);
100 template<
typename NodeT>
103 _collision_checker = collision_checker;
106 unsigned int y_size = _costmap->getSizeInCellsY();
110 if (getSizeX() != x_size || getSizeY() != y_size) {
116 NodeT::initMotionModel(
117 _shared_ctx.get(), _motion_model, _x_size, _y_size, _dim3_size,
121 _goal_manager.setContext(_shared_ctx.get());
122 _expander->setContext(_shared_ctx.get());
123 _expander->setCollisionChecker(_collision_checker);
126 template<
typename NodeT>
128 const uint64_t & index)
130 auto iter = _graph.find(index);
131 if (iter != _graph.end()) {
132 return &(iter->second);
135 return &(_graph.emplace(index, NodeT(index, _shared_ctx.get())).first->second);
138 template<
typename NodeT>
142 const unsigned int & dim_3)
144 if constexpr (std::is_base_of_v<Node2D, NodeT>) {
147 throw std::runtime_error(
"Node type Node2D cannot be given non-zero starting dim 3.");
151 static_cast<unsigned int>(mx),
152 static_cast<unsigned int>(my),
158 static_cast<unsigned int>(mx),
159 static_cast<unsigned int>(my),
161 _start->setPose(Coordinates(mx, my, dim_3));
165 template<
typename NodeT>
167 const NodePtr & node,
168 std::vector<std::tuple<float, float, float>> * expansions_log)
170 if constexpr (std::is_base_of_v<Node2D, NodeT>) {
173 expansions_log->emplace_back(
174 _costmap->getOriginX() + ((coords.x + 0.5) * _costmap->getResolution()),
175 _costmap->getOriginY() + ((coords.y + 0.5) * _costmap->getResolution()),
179 typename NodeT::Coordinates coords = node->pose;
180 expansions_log->emplace_back(
181 _costmap->getOriginX() + ((coords.x + 0.5) * _costmap->getResolution()),
182 _costmap->getOriginY() + ((coords.y + 0.5) * _costmap->getResolution()),
183 _shared_ctx->motion_table.getAngleFromBin(coords.theta));
187 template<
typename NodeT>
191 const unsigned int & dim_3,
192 const GoalHeadingMode & goal_heading_mode,
193 const int & coarse_search_resolution)
195 if constexpr (std::is_base_of_v<Node2D, NodeT>) {
198 throw std::runtime_error(
"Node type Node2D cannot be given non-zero goal dim 3.");
200 _goal_manager.clear();
201 auto goal = addToGraph(
203 static_cast<unsigned int>(mx),
204 static_cast<unsigned int>(my),
208 _goal_manager.addGoal(goal);
210 _coarse_search_resolution = 1;
214 _coarse_search_resolution = 1;
216 _goal_manager.clear();
217 Coordinates ref_goal_coord(mx, my,
static_cast<float>(dim_3));
219 if (!_search_info.cache_obstacle_heuristic ||
220 _goal_manager.hasGoalChanged(ref_goal_coord))
223 throw std::runtime_error(
"Start must be set before goal.");
226 _shared_ctx->obstacle_heuristic->resetObstacleHeuristic(
227 _collision_checker->getCostmapROS(), _start->pose.x, _start->pose.y, mx, my,
228 _shared_ctx->motion_table.downsample_obstacle_heuristic);
231 _goal_manager.setRefGoalCoordinates(ref_goal_coord);
233 unsigned int num_bins = _shared_ctx->motion_table.num_angle_quantization;
235 switch (goal_heading_mode) {
236 case GoalHeadingMode::DEFAULT:
239 auto goal = addToGraph(
241 static_cast<unsigned int>(mx),
242 static_cast<unsigned int>(my),
244 goal->setPose(
typename NodeT::Coordinates(mx, my,
static_cast<float>(dim_3)));
245 _goal_manager.addGoal(goal);
249 case GoalHeadingMode::BIDIRECTIONAL:
253 auto goal = addToGraph(
255 static_cast<unsigned int>(mx),
256 static_cast<unsigned int>(my),
258 goal->setPose(
typename NodeT::Coordinates(mx, my,
static_cast<float>(dim_3)));
259 _goal_manager.addGoal(goal);
262 unsigned int opposite_heading = (dim_3 + (num_bins / 2)) % num_bins;
263 auto opposite_goal = addToGraph(
265 static_cast<unsigned int>(mx),
266 static_cast<unsigned int>(my),
268 opposite_goal->setPose(
269 typename NodeT::Coordinates(mx, my,
static_cast<float>(opposite_heading)));
270 _goal_manager.addGoal(opposite_goal);
274 case GoalHeadingMode::ALL_DIRECTION:
277 _coarse_search_resolution = coarse_search_resolution;
280 for (
unsigned int i = 0; i < num_bins; ++i) {
281 auto goal = addToGraph(
283 static_cast<unsigned int>(mx),
284 static_cast<unsigned int>(my),
286 goal->setPose(
typename NodeT::Coordinates(mx, my,
static_cast<float>(i)));
287 _goal_manager.addGoal(goal);
291 case GoalHeadingMode::UNKNOWN:
292 throw std::runtime_error(
"Goal heading is UNKNOWN.");
297 template<
typename NodeT>
301 if (_graph.empty()) {
302 throw std::runtime_error(
"Failed to compute path, no costmap given.");
306 if (!_start || _goal_manager.goalsIsEmpty()) {
307 throw std::runtime_error(
"Failed to compute path, no valid start or goal given.");
311 _goal_manager.removeInvalidGoals(getToleranceHeuristic(), _collision_checker, _traverse_unknown);
314 if (_goal_manager.getGoalsSet().empty()) {
322 template<
typename NodeT>
325 if (_best_heuristic_node.first < getToleranceHeuristic()) {
326 _graph.at(_best_heuristic_node.second).backtracePath(path);
333 template<
typename NodeT>
335 CoordinateVector & path,
int & iterations,
336 const float & tolerance,
337 std::function<
bool()> cancel_checker,
338 std::vector<std::tuple<float, float, float>> * expansions_log)
340 steady_clock::time_point start_time = steady_clock::now();
341 _tolerance = tolerance;
342 _best_heuristic_node = {std::numeric_limits<float>::max(), 0};
345 if (!areInputsValid()) {
349 NodeVector coarse_check_goals, fine_check_goals;
350 _goal_manager.prepareGoalsForAnalyticExpansion(
351 coarse_check_goals, fine_check_goals,
352 _coarse_search_resolution);
355 addNode(0.0, getStart());
356 getStart()->setAccumulatedCost(0.0);
359 NodePtr current_node =
nullptr;
360 NodePtr neighbor =
nullptr;
361 NodePtr expansion_result =
nullptr;
363 NodeVector neighbors;
364 int approach_iterations = 0;
365 NeighborIterator neighbor_iterator;
366 int analytic_iterations = 0;
367 int closest_distance = std::numeric_limits<int>::max();
370 const uint64_t max_index =
static_cast<uint64_t
>(getSizeX()) *
371 static_cast<uint64_t
>(getSizeY()) *
372 static_cast<uint64_t
>(getSizeDim3());
373 NodeGetter neighborGetter =
374 [&,
this](
const uint64_t & index, NodePtr & neighbor_rtn) ->
bool
376 if (index >= max_index) {
380 neighbor_rtn = addToGraph(index);
384 while (iterations < getMaxIterations() && !_queue.empty()) {
386 if (iterations % _terminal_checking_interval == 0) {
387 if (cancel_checker()) {
390 std::chrono::duration<double> planning_duration =
391 std::chrono::duration_cast<std::chrono::duration<double>>(steady_clock::now() - start_time);
392 if (
static_cast<double>(planning_duration.count()) >= _max_planning_time) {
394 return getClosestPathWithinTolerance(path);
399 current_node = getNextNode();
402 if (expansions_log) {
403 populateExpansionsLog(current_node, expansions_log);
409 if (onVisitationCheckNode(current_node)) {
416 current_node->visited();
419 expansion_result =
nullptr;
420 expansion_result = _expander->tryAnalyticExpansion(
421 current_node, coarse_check_goals, fine_check_goals,
422 _goal_manager.getGoalsCoordinates(), neighborGetter, analytic_iterations, closest_distance);
423 if (expansion_result !=
nullptr) {
424 current_node = expansion_result;
428 if (_goal_manager.isGoal(current_node)) {
429 return current_node->backtracePath(path);
430 }
else if (_best_heuristic_node.first < getToleranceHeuristic()) {
432 approach_iterations++;
433 if (approach_iterations >= getOnApproachMaxIterations()) {
434 return _graph.at(_best_heuristic_node.second).backtracePath(path);
440 current_node->getNeighbors(neighborGetter, _collision_checker, _traverse_unknown, neighbors);
442 for (neighbor_iterator = neighbors.begin();
443 neighbor_iterator != neighbors.end(); ++neighbor_iterator)
445 neighbor = *neighbor_iterator;
448 g_cost = current_node->getAccumulatedCost() + current_node->getTraversalCost(neighbor);
451 if (g_cost < neighbor->getAccumulatedCost()) {
452 neighbor->setAccumulatedCost(g_cost);
453 neighbor->parent = current_node;
456 addNode(g_cost + getHeuristicCost(neighbor), neighbor);
462 return getClosestPathWithinTolerance(path);
465 template<
typename NodeT>
471 template<
typename NodeT>
477 return node.graph_node_ptr;
480 template<
typename NodeT>
485 _queue.emplace(cost, queued_node);
488 template<
typename NodeT>
491 const Coordinates node_coords =
492 NodeT::getCoords(node->getIndex(), getSizeX(), getSizeDim3());
493 float heuristic = node->getHeuristicCost(node_coords, _goal_manager.getGoalsCoordinates());
494 if (heuristic < _best_heuristic_node.first) {
495 _best_heuristic_node = {heuristic, node->getIndex()};
501 template<
typename NodeT>
504 return current_node->wasVisited();
507 template<
typename NodeT>
511 std::swap(_queue, q);
514 template<
typename NodeT>
518 std::swap(_graph, g);
519 _graph.reserve(100000);
522 template<
typename NodeT>
524 const unsigned int & x,
const unsigned int & y,
525 const unsigned int & dim_3)
527 if constexpr (std::is_base_of_v<Node2D, NodeT>) {
530 return NodeT::getIndex(
531 x, y, dim_3, _shared_ctx->motion_table.size_x,
532 _shared_ctx->motion_table.num_angle_quantization);
536 template<
typename NodeT>
539 return _max_iterations;
542 template<
typename NodeT>
545 return _max_on_approach_iterations;
548 template<
typename NodeT>
554 template<
typename NodeT>
560 template<
typename NodeT>
566 template<
typename NodeT>
572 template<
typename NodeT>
575 return _coarse_search_resolution;
578 template<
typename NodeT>
581 return _goal_manager;
584 template<
typename NodeT>
587 return _shared_ctx.get();
unsigned int getSizeInCellsX() const
Accessor for the x size of the costmap in cells.
~AStarAlgorithm()
A destructor for nav2_smac_planner::AStarAlgorithm.
unsigned int & getSizeDim3()
Get number of angle quantization bins (SE2) or Z coordinate (XYZ)
unsigned int getCoarseSearchResolution()
Get the resolution of the coarse search.
bool createPath(CoordinateVector &path, int &num_iterations, const float &tolerance, std::function< bool()> cancel_checker, std::vector< std::tuple< float, float, float >> *expansions_log=nullptr)
Creating path from given costmap, start, and goal.
int & getOnApproachMaxIterations()
Get maximum number of on-approach iterations after within threshold.
bool onVisitationCheckNode(const NodePtr &node)
Check if node has been visited.
void setCollisionChecker(GridCollisionChecker *collision_checker)
Sets the collision checker to use.
void initialize(const bool &allow_unknown, int &max_iterations, const int &max_on_approach_iterations, const int &terminal_checking_interval, const double &max_planning_time, const float &lookup_table_size, const unsigned int &dim_3_size)
Initialization of the planner with defaults.
NodeContext * getContext()
Get pointer to shared node context.
NodePtr getNextNode()
Get pointer to next goal in open set.
bool areInputsValid()
Check if inputs to planner are valid.
void clearQueue()
Clear heuristic queue of nodes to search.
AStarAlgorithm(const MotionModel &motion_model, const SearchInfo &search_info)
A constructor for nav2_smac_planner::AStarAlgorithm.
void populateExpansionsLog(const NodePtr &node, std::vector< std::tuple< float, float, float >> *expansions_log)
Populate a debug log of expansions for Hybrid-A* for visualization.
bool getClosestPathWithinTolerance(CoordinateVector &path)
Get the closest path within tolerance if available.
int & getMaxIterations()
Get maximum number of iterations to plan.
uint64_t getIndex(const unsigned int &x, const unsigned int &y, const unsigned int &dim3)
Get index at coordinates.
void clearGraph()
Clear graph of nodes searched.
unsigned int & getSizeY()
Get size of graph in Y.
void setStart(const float &mx, const float &my, const unsigned int &dim_3)
Set the starting pose for planning, as a node index.
void addNode(const float &cost, NodePtr &node)
Add a node to the open set.
GoalManagerT getGoalManager()
Get the goals manager class.
unsigned int & getSizeX()
Get size of graph in X.
float getHeuristicCost(const NodePtr &node)
Get cost of heuristic of node.
float & getToleranceHeuristic()
Get tolerance, in node nodes.
NodePtr & getStart()
Get pointer reference to starting node.
NodePtr addToGraph(const uint64_t &index)
Adds node to graph.
void setGoal(const float &mx, const float &my, const unsigned int &dim_3, const GoalHeadingMode &goal_heading_mode=GoalHeadingMode::DEFAULT, const int &coarse_search_resolution=1)
Set the goal for planning, as a node index.
Responsible for managing multiple variables storing information on the goal.
A costmap grid collision checker.
uint64_t getIndex()
Gets cell index.
NodeBasic implementation for priority queue insertion.
void populateSearchNode(NodeT *&node)
Take a NodeBasic and populate it with any necessary state cached in the queue for NodeT.
void processSearchNode()
Take a NodeBasic and populate it with any necessary state cached in the queue for NodeTs.
Search properties and penalties.