19 #include "nav2_smac_planner/analytic_expansion.hpp"
21 namespace nav2_smac_planner
24 template<
typename NodeT>
26 const MotionModel & motion_model,
28 const bool & traverse_unknown,
29 const unsigned int & dim_3_size)
30 : _motion_model(motion_model),
31 _search_info(search_info),
32 _traverse_unknown(traverse_unknown),
33 _dim_3_size(dim_3_size),
34 _collision_checker(nullptr)
38 template<
typename NodeT>
42 _collision_checker = collision_checker;
45 template<
typename NodeT>
47 const NodePtr & current_node,
48 const NodeVector & coarse_check_goals,
49 const NodeVector & fine_check_goals,
50 const CoordinateVector & goals_coords,
51 const NodeGetter & getter,
int & analytic_iterations,
52 int & closest_distance)
55 if (_motion_model == MotionModel::DUBIN || _motion_model == MotionModel::REEDS_SHEPP ||
56 _motion_model == MotionModel::STATE_LATTICE)
59 const Coordinates node_coords =
61 current_node->getIndex(), _collision_checker->getCostmap()->getSizeInCellsX(), _dim_3_size);
64 NodePtr current_best_goal =
nullptr;
65 NodePtr current_best_node =
nullptr;
66 float current_best_score = std::numeric_limits<float>::max();
68 closest_distance = std::min(
70 static_cast<int>(NodeT::getHeuristicCost(node_coords, goals_coords)));
74 int desired_iterations = std::max(
75 static_cast<int>(closest_distance / _search_info.analytic_expansion_ratio),
76 static_cast<int>(std::ceil(_search_info.analytic_expansion_ratio)));
80 std::min(analytic_iterations, desired_iterations);
84 if (analytic_iterations <= 0) {
86 analytic_iterations = desired_iterations;
87 bool found_valid_expansion =
false;
90 for (
auto & current_goal_node : coarse_check_goals) {
93 current_node, current_goal_node, getter,
94 current_node->motion_table.state_space);
95 if (!analytic_nodes.nodes.empty()) {
96 found_valid_expansion =
true;
97 NodePtr node = current_node;
98 float score = refineAnalyticPath(
99 node, current_goal_node, getter, analytic_nodes);
101 if (score < current_best_score) {
102 current_best_analytic_nodes = analytic_nodes;
103 current_best_goal = current_goal_node;
104 current_best_score = score;
105 current_best_node = node;
111 if (found_valid_expansion) {
112 for (
auto & current_goal_node : fine_check_goals) {
115 current_node, current_goal_node, getter,
116 current_node->motion_table.state_space);
117 if (!analytic_nodes.nodes.empty()) {
118 NodePtr node = current_node;
119 float score = refineAnalyticPath(
120 node, current_goal_node, getter, analytic_nodes);
122 if (score < current_best_score) {
123 current_best_analytic_nodes = analytic_nodes;
124 current_best_goal = current_goal_node;
125 current_best_score = score;
126 current_best_node = node;
133 if (!current_best_analytic_nodes.nodes.empty()) {
134 return setAnalyticPath(
135 current_best_node, current_best_goal,
136 current_best_analytic_nodes);
138 analytic_iterations--;
142 return NodePtr(
nullptr);
145 template<
typename NodeT>
147 const ompl::base::ReedsSheppStateSpace::ReedsSheppPath & path)
149 const double * lengths = path.length_;
152 for (
int i = 0; i < 5; ++i) {
153 if (lengths[i] == 0.0) {
157 int currentDirection = (lengths[i] > 0.0) ? 1 : -1;
158 if (last_dir != 0 && currentDirection != last_dir) {
161 last_dir = currentDirection;
167 template<
typename NodeT>
169 const NodePtr & node,
170 const NodePtr & goal,
171 const NodeGetter & node_getter,
172 const ompl::base::StateSpacePtr & state_space)
174 static ompl::base::ScopedState<> from(state_space), to(state_space), s(state_space);
175 from[0] = node->pose.x;
176 from[1] = node->pose.y;
177 from[2] = node->motion_table.getAngleFromBin(node->pose.theta);
178 to[0] = goal->pose.x;
179 to[1] = goal->pose.y;
180 to[2] = node->motion_table.getAngleFromBin(goal->pose.theta);
182 float d = state_space->distance(from(), to());
184 auto rs_state_space =
dynamic_cast<ompl::base::ReedsSheppStateSpace *
>(state_space.get());
185 int direction_changes = 0;
186 if (rs_state_space) {
187 direction_changes = countDirectionChanges(rs_state_space->reedsShepp(from.get(), to.get()));
191 static const float sqrt_2 = sqrtf(2.0f);
197 if (d > _search_info.analytic_expansion_max_length || d < sqrt_2) {
201 unsigned int num_intervals =
static_cast<unsigned int>(std::floor(d / sqrt_2));
206 possible_nodes.nodes.reserve(num_intervals);
207 std::vector<double> reals;
213 NodePtr next(
nullptr);
215 Coordinates proposed_coordinates;
216 bool failure =
false;
217 std::vector<float> node_costs;
218 node_costs.reserve(num_intervals);
221 for (
float i = 1; i <= num_intervals; i++) {
222 state_space->interpolate(from(), to(), i / num_intervals, s());
225 theta = (reals[2] < 0.0) ? (reals[2] + 2.0 * M_PI) : reals[2];
226 theta = (theta > 2.0 * M_PI) ? (theta - 2.0 * M_PI) : theta;
227 angle = node->motion_table.getAngle(theta);
230 index = NodeT::getIndex(
231 static_cast<unsigned int>(reals[0]),
232 static_cast<unsigned int>(reals[1]),
233 static_cast<unsigned int>(angle));
235 if (node_getter(index, next)) {
236 Coordinates initial_node_coords = next->pose;
237 proposed_coordinates = {
static_cast<float>(reals[0]),
static_cast<float>(reals[1]), angle};
238 next->setPose(proposed_coordinates);
239 if (next->isNodeValid(_traverse_unknown, _collision_checker) && next != prev) {
241 possible_nodes.add(next, initial_node_coords, proposed_coordinates);
242 node_costs.emplace_back(next->getCost());
246 next->setPose(initial_node_coords);
259 const float max_cost = _search_info.analytic_expansion_max_cost;
260 auto max_cost_it = std::max_element(node_costs.begin(), node_costs.end());
261 if (max_cost_it != node_costs.end() && *max_cost_it > max_cost) {
267 bool cost_exit_high_cost_region =
false;
268 for (
auto iter = node_costs.rbegin(); iter != node_costs.rend(); ++iter) {
269 const float & curr_cost = *iter;
270 if (curr_cost <= max_cost) {
271 cost_exit_high_cost_region =
true;
272 }
else if (curr_cost > max_cost && cost_exit_high_cost_region) {
281 if (d < 2.0f * M_PI * goal->motion_table.min_turning_radius &&
282 _search_info.analytic_expansion_max_cost_override)
291 for (
const auto & node_pose : possible_nodes.nodes) {
292 const auto & n = node_pose.node;
293 n->setPose(node_pose.initial_coords);
300 possible_nodes.setDirectionChanges(direction_changes);
301 return possible_nodes;
304 template<
typename NodeT>
307 const NodePtr & goal_node,
308 const NodeGetter & getter,
311 NodePtr test_node = node;
313 for (
int i = 0; i < 8; i++) {
316 if (test_node->parent && test_node->parent->parent &&
317 test_node->parent->parent->parent &&
318 test_node->parent->parent->parent->parent &&
319 test_node->parent->parent->parent->parent->parent)
321 test_node = test_node->parent->parent->parent->parent->parent;
323 refined_analytic_nodes =
325 test_node, goal_node, getter,
326 test_node->motion_table.state_space);
327 if (refined_analytic_nodes.nodes.empty()) {
330 if (refined_analytic_nodes.direction_changes > analytic_nodes.direction_changes) {
334 analytic_nodes = refined_analytic_nodes;
346 if (expansion.nodes.size() < 2) {
347 return std::numeric_limits<float>::max();
351 float normalized_cost = 0.0;
353 const float distance = hypotf(
354 expansion.nodes[1].proposed_coords.x - expansion.nodes[0].proposed_coords.x,
355 expansion.nodes[1].proposed_coords.y - expansion.nodes[0].proposed_coords.y);
356 const float & weight = expansion.nodes[0].node->motion_table.cost_penalty;
357 for (
auto iter = expansion.nodes.begin(); iter != expansion.nodes.end(); ++iter) {
358 normalized_cost = iter->node->getCost() / 252.0f;
360 score += distance * (1.0 + weight * normalized_cost);
365 float original_score = scoringFn(analytic_nodes);
366 float best_score = original_score;
367 float score = std::numeric_limits<float>::max();
368 float min_turn_rad = node->motion_table.min_turning_radius;
369 const float max_min_turn_rad = 4.0 * min_turn_rad;
370 while (min_turn_rad < max_min_turn_rad) {
372 ompl::base::StateSpacePtr state_space;
373 if (node->motion_table.motion_model == MotionModel::DUBIN) {
374 state_space = std::make_shared<ompl::base::DubinsStateSpace>(min_turn_rad);
376 state_space = std::make_shared<ompl::base::ReedsSheppStateSpace>(min_turn_rad);
378 refined_analytic_nodes = getAnalyticPath(node, goal_node, getter, state_space);
379 score = scoringFn(refined_analytic_nodes);
382 if (score <= best_score &&
383 refined_analytic_nodes.direction_changes <= analytic_nodes.direction_changes)
385 analytic_nodes = refined_analytic_nodes;
392 if (score <= original_score &&
393 refined_analytic_nodes.direction_changes < analytic_nodes.direction_changes)
395 analytic_nodes = refined_analytic_nodes;
403 template<
typename NodeT>
405 const NodePtr & node,
406 const NodePtr & goal_node,
409 _detached_nodes.clear();
412 for (
const auto & node_pose : expanded_nodes.nodes) {
413 auto n = node_pose.node;
415 if (n->getIndex() != goal_node->getIndex()) {
416 if (n->wasVisited()) {
417 _detached_nodes.push_back(std::make_unique<NodeT>(-1));
418 n = _detached_nodes.back().get();
421 n->pose = node_pose.proposed_coords;
426 if (goal_node != prev) {
427 goal_node->parent = prev;
428 cleanNode(goal_node);
429 goal_node->visited();
437 node->setMotionPrimitive(
nullptr);
440 template<
typename NodeT>
451 const ompl::base::StateSpacePtr &)
453 return AnalyticExpansionNodes();
461 AnalyticExpansionNodes &)
463 return std::numeric_limits<float>::max();
470 const AnalyticExpansionNodes &)
472 return NodePtr(
nullptr);
480 const CoordinateVector &,
481 const NodeGetter &,
int &,
484 return NodePtr(
nullptr);
487 template class AnalyticExpansion<Node2D>;
488 template class AnalyticExpansion<NodeHybrid>;
489 template class AnalyticExpansion<NodeLattice>;
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.
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.