15 #ifndef NAV2_SMAC_PLANNER__ANALYTIC_EXPANSION_IMPL_HPP_
16 #define NAV2_SMAC_PLANNER__ANALYTIC_EXPANSION_IMPL_HPP_
18 #include <ompl/config.h>
23 #include <type_traits>
26 #include "nav2_smac_planner/analytic_expansion.hpp"
28 namespace nav2_smac_planner
31 template<
typename NodeT>
33 const MotionModel & motion_model,
35 const bool & traverse_unknown,
36 const unsigned int & dim_3_size)
37 : _motion_model(motion_model),
38 _search_info(search_info),
39 _traverse_unknown(traverse_unknown),
40 _dim_3_size(dim_3_size),
41 _collision_checker(nullptr)
45 template<
typename NodeT>
49 _collision_checker = collision_checker;
52 template<
typename NodeT>
58 template<
typename NodeT>
60 const NodePtr & current_node,
61 const NodeVector & coarse_check_goals,
62 const NodeVector & fine_check_goals,
63 const CoordinateVector & goals_coords,
64 const NodeGetter & getter,
int & analytic_iterations,
65 int & closest_distance)
68 if constexpr (std::is_base_of_v<Node2D, NodeT>) {
69 return NodePtr(
nullptr);
72 if (_motion_model == MotionModel::DUBIN || _motion_model == MotionModel::REEDS_SHEPP ||
73 _motion_model == MotionModel::STATE_LATTICE)
76 const Coordinates node_coords =
78 current_node->getIndex(), _collision_checker->getCostmap()->getSizeInCellsX(), _dim_3_size);
81 NodePtr current_best_goal =
nullptr;
82 NodePtr current_best_node =
nullptr;
83 float current_best_score = std::numeric_limits<float>::max();
85 closest_distance = std::min(
87 static_cast<int>(current_node->getHeuristicCost(node_coords, goals_coords)));
91 int desired_iterations = std::max(
92 static_cast<int>(closest_distance / _search_info.analytic_expansion_ratio),
93 static_cast<int>(std::ceil(_search_info.analytic_expansion_ratio)));
97 std::min(analytic_iterations, desired_iterations);
101 if (analytic_iterations <= 0) {
103 analytic_iterations = desired_iterations;
104 bool found_valid_expansion =
false;
107 for (
auto & current_goal_node : coarse_check_goals) {
110 current_node, current_goal_node, getter,
111 _ctx->motion_table.state_space);
112 if (!analytic_nodes.nodes.empty()) {
113 found_valid_expansion =
true;
114 NodePtr node = current_node;
115 float score = refineAnalyticPath(
116 node, current_goal_node, getter, analytic_nodes);
118 if (score < current_best_score) {
119 current_best_analytic_nodes = analytic_nodes;
120 current_best_goal = current_goal_node;
121 current_best_score = score;
122 current_best_node = node;
128 if (found_valid_expansion) {
129 for (
auto & current_goal_node : fine_check_goals) {
132 current_node, current_goal_node, getter,
133 _ctx->motion_table.state_space);
134 if (!analytic_nodes.nodes.empty()) {
135 NodePtr node = current_node;
136 float score = refineAnalyticPath(
137 node, current_goal_node, getter, analytic_nodes);
139 if (score < current_best_score) {
140 current_best_analytic_nodes = analytic_nodes;
141 current_best_goal = current_goal_node;
142 current_best_score = score;
143 current_best_node = node;
150 if (!current_best_analytic_nodes.nodes.empty()) {
151 return setAnalyticPath(
152 current_best_node, current_best_goal,
153 current_best_analytic_nodes);
155 analytic_iterations--;
159 return NodePtr(
nullptr);
163 template<
typename NodeT>
165 #
if OMPL_VERSION_VALUE >= 2000000
166 const ompl::base::ReedsSheppStateSpace::PathType & path)
168 const ompl::base::ReedsSheppStateSpace::ReedsSheppPath & path)
171 const double * lengths = path.length_;
174 for (
int i = 0; i < 5; ++i) {
175 if (lengths[i] == 0.0) {
179 int currentDirection = (lengths[i] > 0.0) ? 1 : -1;
180 if (last_dir != 0 && currentDirection != last_dir) {
183 last_dir = currentDirection;
189 template<
typename NodeT>
191 const NodePtr & node,
192 const NodePtr & goal,
193 const NodeGetter & node_getter,
194 const ompl::base::StateSpacePtr & state_space)
197 if constexpr (std::is_base_of_v<Node2D, NodeT>) {
200 ompl::base::ScopedState<> from(state_space), to(state_space), s(state_space);
201 from[0] = node->pose.x;
202 from[1] = node->pose.y;
203 from[2] = _ctx->motion_table.getAngleFromBin(node->pose.theta);
204 to[0] = goal->pose.x;
205 to[1] = goal->pose.y;
206 to[2] = _ctx->motion_table.getAngleFromBin(goal->pose.theta);
208 float d = state_space->distance(from(), to());
210 auto rs_state_space =
dynamic_cast<ompl::base::ReedsSheppStateSpace *
>(state_space.get());
211 int direction_changes = 0;
212 if (rs_state_space) {
213 #if OMPL_VERSION_VALUE >= 2000000
214 direction_changes = countDirectionChanges(rs_state_space->getPath(from.get(), to.get()));
216 direction_changes = countDirectionChanges(rs_state_space->reedsShepp(from.get(), to.get()));
221 static const float sqrt_2 = sqrtf(2.0f);
227 if (d > _search_info.analytic_expansion_max_length || d < sqrt_2) {
231 unsigned int num_intervals =
static_cast<unsigned int>(std::floor(d / sqrt_2));
236 possible_nodes.nodes.reserve(num_intervals);
237 std::vector<double> reals;
243 NodePtr next(
nullptr);
245 Coordinates proposed_coordinates;
246 bool failure =
false;
247 std::vector<float> node_costs;
248 node_costs.reserve(num_intervals);
251 for (
float i = 1; i <= num_intervals; i++) {
252 state_space->interpolate(from(), to(), i / num_intervals, s());
255 theta = (reals[2] < 0.0) ? (reals[2] + 2.0 * M_PI) : reals[2];
256 theta = (theta > 2.0 * M_PI) ? (theta - 2.0 * M_PI) : theta;
257 angle = _ctx->motion_table.getAngle(theta);
260 index = NodeT::getIndex(
261 static_cast<unsigned int>(reals[0]),
262 static_cast<unsigned int>(reals[1]),
263 static_cast<unsigned int>(angle),
264 _ctx->motion_table.size_x,
265 _ctx->motion_table.num_angle_quantization);
267 if (node_getter(index, next)) {
268 Coordinates initial_node_coords = next->pose;
269 proposed_coordinates = {
static_cast<float>(reals[0]),
static_cast<float>(reals[1]), angle};
270 next->setPose(proposed_coordinates);
271 if (next->isNodeValid(_traverse_unknown, _collision_checker) && next != prev) {
273 possible_nodes.add(next, initial_node_coords, proposed_coordinates);
274 node_costs.emplace_back(next->getCost());
278 next->setPose(initial_node_coords);
291 const float max_cost = _search_info.analytic_expansion_max_cost;
292 auto max_cost_it = std::max_element(node_costs.begin(), node_costs.end());
293 if (max_cost_it != node_costs.end() && *max_cost_it > max_cost) {
299 bool cost_exit_high_cost_region =
false;
300 for (
auto iter = node_costs.rbegin(); iter != node_costs.rend(); ++iter) {
301 const float & curr_cost = *iter;
302 if (curr_cost <= max_cost) {
303 cost_exit_high_cost_region =
true;
304 }
else if (curr_cost > max_cost && cost_exit_high_cost_region) {
313 if (d < 2.0f * M_PI * _ctx->motion_table.min_turning_radius &&
314 _search_info.analytic_expansion_max_cost_override)
323 for (
const auto & node_pose : possible_nodes.nodes) {
324 const auto & n = node_pose.node;
325 n->setPose(node_pose.initial_coords);
332 possible_nodes.setDirectionChanges(direction_changes);
333 return possible_nodes;
337 template<
typename NodeT>
340 const NodePtr & goal_node,
341 const NodeGetter & getter,
345 if constexpr (std::is_base_of_v<Node2D, NodeT>) {
346 return std::numeric_limits<float>::max();
348 NodePtr test_node = node;
350 for (
int i = 0; i < 8; i++) {
353 if (test_node->parent && test_node->parent->parent &&
354 test_node->parent->parent->parent &&
355 test_node->parent->parent->parent->parent &&
356 test_node->parent->parent->parent->parent->parent)
358 test_node =
static_cast<NodePtr
>(test_node->parent->parent->parent->parent->parent);
360 refined_analytic_nodes =
362 test_node, goal_node, getter,
363 _ctx->motion_table.state_space);
364 if (refined_analytic_nodes.nodes.empty()) {
367 if (refined_analytic_nodes.direction_changes > analytic_nodes.direction_changes) {
371 analytic_nodes = refined_analytic_nodes;
383 if (expansion.nodes.size() < 2) {
384 return std::numeric_limits<float>::max();
388 float normalized_cost = 0.0;
390 const float distance = hypotf(
391 expansion.nodes[1].proposed_coords.x - expansion.nodes[0].proposed_coords.x,
392 expansion.nodes[1].proposed_coords.y - expansion.nodes[0].proposed_coords.y);
393 const float & weight = _ctx->motion_table.cost_penalty;
394 for (
auto iter = expansion.nodes.begin(); iter != expansion.nodes.end(); ++iter) {
395 normalized_cost = iter->node->getCost() / 252.0f;
397 score += distance * (1.0 + weight * normalized_cost);
402 float original_score = scoringFn(analytic_nodes);
403 float best_score = original_score;
404 float score = std::numeric_limits<float>::max();
405 float min_turn_rad = _ctx->motion_table.min_turning_radius;
406 const float max_min_turn_rad = 4.0 * min_turn_rad;
409 if (_ctx->motion_table.motion_model == MotionModel::OMNI) {
413 while (min_turn_rad < max_min_turn_rad) {
415 ompl::base::StateSpacePtr state_space;
416 if (_ctx->motion_table.motion_model == MotionModel::DUBIN) {
417 state_space = std::make_shared<ompl::base::DubinsStateSpace>(min_turn_rad);
419 state_space = std::make_shared<ompl::base::ReedsSheppStateSpace>(min_turn_rad);
421 refined_analytic_nodes = getAnalyticPath(node, goal_node, getter, state_space);
422 score = scoringFn(refined_analytic_nodes);
425 if (score <= best_score &&
426 refined_analytic_nodes.direction_changes <= analytic_nodes.direction_changes)
428 analytic_nodes = refined_analytic_nodes;
435 if (score <= original_score &&
436 refined_analytic_nodes.direction_changes < analytic_nodes.direction_changes)
438 analytic_nodes = refined_analytic_nodes;
447 template<
typename NodeT>
449 const NodePtr & node,
450 const NodePtr & goal_node,
454 if constexpr (std::is_base_of_v<Node2D, NodeT>) {
455 return NodePtr(
nullptr);
457 _detached_nodes.clear();
460 for (
const auto & node_pose : expanded_nodes.nodes) {
461 auto n = node_pose.node;
463 if (n->getIndex() != goal_node->getIndex()) {
464 if (n->wasVisited()) {
465 _detached_nodes.push_back(std::make_unique<NodeT>(-1, _ctx));
466 n = _detached_nodes.back().get();
469 n->pose = node_pose.proposed_coords;
474 if (goal_node != prev) {
475 goal_node->parent = prev;
476 cleanNode(goal_node);
477 goal_node->visited();
483 template<
typename NodeT>
487 if constexpr (std::is_base_of_v<NodeLattice, NodeT>) {
488 node->setMotionPrimitive(
nullptr);
AnalyticExpansionNodes getAnalyticPath(const NodePtr &node, const NodePtr &goal, const NodeGetter &getter, const ompl::base::StateSpacePtr &state_space)
Perform an analytic path expansion to the goal.
NodePtr tryAnalyticExpansion(const NodePtr ¤t_node, const NodeVector &coarse_check_goals, const NodeVector &fine_check_goals, const CoordinateVector &goals_coords, const NodeGetter &getter, int &iterations, int &closest_distance)
Attempt an analytic path completion.
int countDirectionChanges(const ompl::base::ReedsSheppStateSpace::ReedsSheppPath &path)
Counts the number of direction changes in a Reeds-Shepp path.
void cleanNode(const NodePtr &nodes)
Takes an expanded nodes to clean up, if necessary, of any state information that may be polluting it ...
void setCollisionChecker(GridCollisionChecker *collision_checker)
Sets the collision checker and costmap to use in expansion validation.
AnalyticExpansion(const MotionModel &motion_model, const SearchInfo &search_info, const bool &traverse_unknown, const unsigned int &dim_3_size)
Constructor for analytic expansion object.
float refineAnalyticPath(NodePtr &node, const NodePtr &goal_node, const NodeGetter &getter, AnalyticExpansionNodes &analytic_nodes)
Refined analytic path from the current node to the goal.
void setContext(NodeContext *ctx)
Sets the shared context to use.
NodePtr setAnalyticPath(const NodePtr &node, const NodePtr &goal, const AnalyticExpansionNodes &expanded_nodes)
Takes final analytic expansion and appends to current expanded node.
A costmap grid collision checker.
Analytic expansion nodes and associated metadata.
Search properties and penalties.