25 #include "angles/angles.h"
27 #include "ompl/base/ScopedState.h"
28 #include "ompl/base/spaces/DubinsStateSpace.h"
29 #include "ompl/base/spaces/ReedsSheppStateSpace.h"
30 #include "ompl/base/spaces/SE2StateSpace.h"
32 #include "nav2_smac_planner/node_lattice.hpp"
34 using namespace std::chrono;
36 namespace nav2_smac_planner
45 void LatticeMotionTable::initMotionModel(
46 unsigned int & size_x_in,
50 change_penalty = search_info.change_penalty;
51 non_straight_penalty = search_info.non_straight_penalty;
52 cost_penalty = search_info.cost_penalty;
53 reverse_penalty = search_info.reverse_penalty;
54 travel_distance_reward = 1.0f - search_info.retrospective_penalty;
55 allow_reverse_expansion = search_info.allow_reverse_expansion;
56 rotation_penalty = search_info.rotation_penalty;
57 min_turning_radius = search_info.minimum_turning_radius;
58 downsample_obstacle_heuristic = search_info.downsample_obstacle_heuristic;
59 use_quadratic_cost_penalty = search_info.use_quadratic_cost_penalty;
61 if (current_lattice_filepath == search_info.lattice_filepath) {
64 current_lattice_filepath = search_info.lattice_filepath;
67 lattice_metadata = getLatticeMetadata(current_lattice_filepath);
68 std::ifstream latticeFile(current_lattice_filepath);
69 if (!latticeFile.is_open()) {
70 throw std::runtime_error(
"Could not open lattice file");
74 num_angle_quantization = lattice_metadata.number_of_headings;
77 if (lattice_metadata.motion_model ==
"omni") {
79 state_space = std::make_shared<ompl::base::SE2StateSpace>();
80 motion_model = MotionModel::OMNI;
81 }
else if (!allow_reverse_expansion) {
82 state_space = std::make_shared<ompl::base::DubinsStateSpace>(
83 lattice_metadata.min_turning_radius);
84 motion_model = MotionModel::DUBIN;
86 state_space = std::make_shared<ompl::base::ReedsSheppStateSpace>(
87 lattice_metadata.min_turning_radius);
88 motion_model = MotionModel::REEDS_SHEPP;
93 float prev_start_angle = 0.0;
94 std::vector<MotionPrimitive> primitives;
95 nlohmann::json json_primitives = json[
"primitives"];
96 for (
unsigned int i = 0; i < json_primitives.size(); ++i) {
98 fromJsonToMotionPrimitive(json_primitives[i], new_primitive);
100 if (prev_start_angle != new_primitive.start_angle) {
101 motion_primitives.push_back(primitives);
103 prev_start_angle = new_primitive.start_angle;
105 primitives.push_back(new_primitive);
107 motion_primitives.push_back(primitives);
110 trig_values.reserve(lattice_metadata.number_of_headings);
111 for (
unsigned int i = 0; i < lattice_metadata.heading_angles.size(); ++i) {
112 trig_values.emplace_back(
113 cos(lattice_metadata.heading_angles[i]),
114 sin(lattice_metadata.heading_angles[i]));
118 MotionPrimitivePtrs LatticeMotionTable::getMotionPrimitives(
120 unsigned int & direction_change_index)
122 MotionPrimitives & prims_at_heading = motion_primitives[node->pose.theta];
123 MotionPrimitivePtrs primitive_projection_list;
124 for (
unsigned int i = 0; i != prims_at_heading.size(); i++) {
125 primitive_projection_list.push_back(&prims_at_heading[i]);
129 direction_change_index =
static_cast<unsigned int>(primitive_projection_list.size());
131 if (allow_reverse_expansion) {
133 double reserve_heading = node->pose.theta - (num_angle_quantization / 2);
134 if (reserve_heading < 0) {
135 reserve_heading += num_angle_quantization;
137 if (reserve_heading > num_angle_quantization) {
138 reserve_heading -= num_angle_quantization;
141 MotionPrimitives & prims_at_reverse_heading = motion_primitives[reserve_heading];
142 for (
unsigned int i = 0; i != prims_at_reverse_heading.size(); i++) {
143 primitive_projection_list.push_back(&prims_at_reverse_heading[i]);
147 return primitive_projection_list;
150 LatticeMetadata LatticeMotionTable::getLatticeMetadata(
const std::string & lattice_filepath)
152 std::ifstream lattice_file(lattice_filepath);
153 if (!lattice_file.is_open()) {
154 throw std::runtime_error(
"Could not open lattice file!");
160 fromJsonToMetaData(j[
"lattice_metadata"], metadata);
164 unsigned int LatticeMotionTable::getClosestAngularBin(
const double & theta)
166 float min_dist = std::numeric_limits<float>::max();
167 unsigned int closest_idx = 0;
169 for (
unsigned int i = 0; i != lattice_metadata.heading_angles.size(); i++) {
170 dist = fabs(angles::shortest_angular_distance(theta, lattice_metadata.heading_angles[i]));
171 if (dist < min_dist) {
179 float & LatticeMotionTable::getAngleFromBin(
const unsigned int & bin_idx)
181 return lattice_metadata.heading_angles[bin_idx];
184 double LatticeMotionTable::getAngle(
const double & theta)
186 return getClosestAngularBin(theta);
191 pose(0.0f, 0.0f, 0.0f),
192 _cell_cost(std::numeric_limits<float>::quiet_NaN()),
193 _accumulated_cost(std::numeric_limits<float>::max()),
196 _motion_primitive(nullptr),
198 _is_node_valid(false),
211 _cell_cost = std::numeric_limits<float>::quiet_NaN();
212 _accumulated_cost = std::numeric_limits<float>::max();
213 _was_visited =
false;
217 _motion_primitive =
nullptr;
219 _is_node_valid =
false;
223 const bool & traverse_unknown,
229 if (!std::isnan(_cell_cost)) {
230 return _is_node_valid;
236 const double angle = std::fmod(
238 2.0 * M_PI) / bin_size;
240 this->pose.x, this->pose.y, angle , traverse_unknown))
242 _is_node_valid =
false;
243 _cell_cost = collision_checker->
getCost();
248 float max_cell_cost = collision_checker->
getCost();
251 if (motion_primitive) {
252 const float & grid_resolution = _ctx->motion_table.lattice_metadata.grid_resolution;
253 const float & resolution_diag_sq = 2.0 * grid_resolution * grid_resolution;
254 MotionPose last_pose(1e9, 1e9, 1e9, TurnDirection::UNKNOWN);
255 MotionPose pose_dist(0.0, 0.0, 0.0, TurnDirection::UNKNOWN);
259 initial_pose._x = this->pose.x - (motion_primitive->poses.back()._x / grid_resolution);
260 initial_pose._y = this->pose.y - (motion_primitive->poses.back()._y / grid_resolution);
261 initial_pose._theta = _ctx->motion_table.
getAngleFromBin(motion_primitive->start_angle);
263 for (
auto it = motion_primitive->poses.begin(); it != motion_primitive->poses.end(); ++it) {
265 pose_dist = *it - last_pose;
267 if (pose_dist._x * pose_dist._x + pose_dist._y * pose_dist._y > resolution_diag_sq) {
270 prim_pose._x = initial_pose._x + (it->_x / grid_resolution);
271 prim_pose._y = initial_pose._y + (it->_y / grid_resolution);
275 prim_pose._theta = std::fmod(it->_theta + M_PI, 2.0 * M_PI);
277 prim_pose._theta = std::fmod(it->_theta, 2.0 * M_PI);
282 prim_pose._theta / bin_size ,
285 _is_node_valid =
false;
286 _cell_cost = std::max(max_cell_cost, collision_checker->
getCost());
289 max_cell_cost = std::max(max_cell_cost, collision_checker->
getCost());
294 _cell_cost = max_cell_cost;
295 _is_node_valid =
true;
296 return _is_node_valid;
301 const float normalized_cost = child->
getCost() / 252.0;
302 if (std::isnan(normalized_cost)) {
303 throw std::runtime_error(
304 "Node attempted to get traversal "
305 "cost without a known collision cost!");
311 const float prim_length =
312 transition_prim->trajectory_length / _ctx->motion_table.lattice_metadata.grid_resolution;
313 if (prim ==
nullptr) {
318 if (transition_prim->trajectory_length < 1e-4) {
319 return _ctx->motion_table.rotation_penalty *
320 (1.0 + _ctx->motion_table.cost_penalty * normalized_cost);
323 float travel_cost = 0.0;
324 float travel_cost_raw = 0.0;
325 if (_ctx->motion_table.use_quadratic_cost_penalty) {
326 travel_cost_raw = prim_length *
327 (_ctx->motion_table.travel_distance_reward +
328 _ctx->motion_table.cost_penalty * normalized_cost * normalized_cost);
330 travel_cost_raw = prim_length *
331 (_ctx->motion_table.travel_distance_reward +
332 _ctx->motion_table.cost_penalty * normalized_cost);
335 if (transition_prim->arc_length < 0.001) {
337 travel_cost = travel_cost_raw;
339 if (prim->left_turn == transition_prim->left_turn) {
341 travel_cost = travel_cost_raw * _ctx->motion_table.non_straight_penalty;
344 travel_cost = travel_cost_raw *
345 (_ctx->motion_table.non_straight_penalty + _ctx->motion_table.change_penalty);
352 travel_cost *= _ctx->motion_table.reverse_penalty;
360 const CoordinateVector & goals_coords)
364 const float obstacle_heuristic = _ctx->obstacle_heuristic->getObstacleHeuristic(
365 node_coords, _ctx->motion_table.cost_penalty,
366 _ctx->motion_table.use_quadratic_cost_penalty,
367 _ctx->motion_table.downsample_obstacle_heuristic);
368 float distance_heuristic = std::numeric_limits<float>::max();
369 for (
unsigned int i = 0; i < goals_coords.size(); i++) {
370 distance_heuristic = std::min(
372 _ctx->distance_heuristic->getDistanceHeuristic(node_coords, goals_coords[i],
373 obstacle_heuristic, _ctx->motion_table));
375 return std::max(obstacle_heuristic, distance_heuristic);
380 const MotionModel & motion_model,
381 unsigned int & size_x,
386 if (motion_model != MotionModel::STATE_LATTICE) {
387 throw std::runtime_error(
388 "Invalid motion model for Lattice node. Please select"
389 " STATE_LATTICE and provide a valid lattice file.");
396 std::function<
bool(
const uint64_t &,
399 const bool & traverse_unknown,
400 NodeVector & neighbors)
405 Coordinates initial_node_coords, motion_projection;
406 unsigned int direction_change_index = 0;
409 direction_change_index);
410 const float & grid_resolution = _ctx->motion_table.lattice_metadata.grid_resolution;
412 for (
unsigned int i = 0; i != motion_primitives.size(); i++) {
413 const MotionPose & end_pose = motion_primitives[i]->poses.back();
414 motion_projection.x = this->pose.x + (end_pose._x / grid_resolution);
415 motion_projection.y = this->pose.y + (end_pose._y / grid_resolution);
416 motion_projection.theta = motion_primitives[i]->end_angle ;
423 if (i >= direction_change_index) {
425 float opposite_heading_theta =
426 motion_projection.theta - (_ctx->motion_table.num_angle_quantization / 2);
427 if (opposite_heading_theta < 0) {
428 opposite_heading_theta += _ctx->motion_table.num_angle_quantization;
430 if (opposite_heading_theta > _ctx->motion_table.num_angle_quantization) {
431 opposite_heading_theta -= _ctx->motion_table.num_angle_quantization;
433 motion_projection.theta = opposite_heading_theta;
437 static_cast<unsigned int>(motion_projection.x),
438 static_cast<unsigned int>(motion_projection.y),
439 static_cast<unsigned int>(motion_projection.theta),
440 _ctx->motion_table.size_x, _ctx->motion_table.num_angle_quantization);
442 if (NeighborGetter(index, neighbor) && !neighbor->
wasVisited()) {
445 initial_node_coords = neighbor->pose;
451 motion_projection.theta));
456 traverse_unknown, collision_checker, motion_primitives[i],
backwards))
461 neighbors.push_back(neighbor);
463 neighbor->
setPose(initial_node_coords);
477 while (current_node->parent) {
479 current_node = current_node->parent;
490 NodeLattice::CoordinateVector & path)
494 const float & grid_resolution = _ctx->motion_table.lattice_metadata.grid_resolution;
498 initial_pose.x = current_node->pose.x - (prim->poses.back()._x / grid_resolution);
499 initial_pose.y = current_node->pose.y - (prim->poses.back()._y / grid_resolution);
500 initial_pose.theta = _ctx->motion_table.
getAngleFromBin(prim->start_angle);
502 for (
auto it = prim->poses.crbegin(); it != prim->poses.crend(); ++it) {
504 prim_pose.x = initial_pose.x + (it->_x / grid_resolution);
505 prim_pose.y = initial_pose.y + (it->_y / grid_resolution);
509 prim_pose.theta = std::fmod(it->_theta + M_PI, 2.0 * M_PI);
511 prim_pose.theta = it->_theta;
513 path.push_back(prim_pose);
517 path.push_back(current_node->pose);
518 path.back().theta = _ctx->motion_table.
getAngleFromBin(path.back().theta);
A costmap grid collision checker.
bool inCollision(const float &x, const float &y, const float &theta, const bool &traverse_unknown)
Check if in collision with costmap and footprint at pose.
std::vector< float > & getPrecomputedAngles()
Get the angles of the precomputed footprint orientations.
float getCost()
Get cost at footprint pose in costmap.
NodeLattice implementation for graph, Hybrid-A*.
void getNeighbors(std::function< bool(const uint64_t &, nav2_smac_planner::NodeLattice *&)> &validity_checker, GridCollisionChecker *collision_checker, const bool &traverse_unknown, NodeVector &neighbors)
Retrieve all valid neighbors of a node.
void backwards(bool back=true)
Sets that this primitive is moving in reverse.
uint64_t getIndex()
Gets cell index.
static void initMotionModel(NodeContext *ctx, const MotionModel &motion_model, unsigned int &size_x, unsigned int &size_y, unsigned int &angle_quantization, SearchInfo &search_info)
Initialize motion models.
~NodeLattice()
A destructor for nav2_smac_planner::NodeLattice.
float getCost()
Gets the costmap cost at this node.
void reset()
Reset method for new search.
bool backtracePath(CoordinateVector &path)
Set the starting pose for planning, as a node index.
float getTraversalCost(const NodePtr &child)
Get traversal cost of parent node to child node.
bool isNodeValid(const bool &traverse_unknown, GridCollisionChecker *collision_checker, MotionPrimitive *primitive=nullptr, bool is_backwards=false)
Check if this node is valid.
float getHeuristicCost(const Coordinates &node_coords, const CoordinateVector &goals_coords)
Get cost of heuristic of node.
void addNodeToPath(NodePtr current_node, CoordinateVector &path)
add node to the path
bool isBackward()
Gets if this primitive is moving in reverse.
bool wasVisited()
Gets if cell has been visited in search.
MotionPrimitive *& getMotionPrimitive()
Gets the motion primitive used to achieve node in search.
void setMotionPrimitive(MotionPrimitive *prim)
Sets the motion primitive used to achieve node in search.
void setPose(const Coordinates &pose_in)
setting continuous coordinate search poses (in partial-cells)
Implementation of coordinate2d structure.
void initMotionModel(unsigned int &size_x_in, SearchInfo &search_info)
Initializing state lattice planner's motion model.
MotionPrimitivePtrs getMotionPrimitives(const NodeLattice *node, unsigned int &direction_change_index)
Get projections of motion models.
float & getAngleFromBin(const unsigned int &bin_idx)
Get the raw orientation from an angular bin.
A struct for poses in motion primitives.
A struct of all motion primitive data.
Search properties and penalties.