Skip to content

Commit 1bb68c5

Browse files
Fix State-Lattice planner crashes due to FP precision loss
1 parent 822560c commit 1bb68c5

File tree

2 files changed

+70
-4
lines changed

2 files changed

+70
-4
lines changed

nav2_smac_planner/src/node_lattice.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@ bool NodeLattice::isNodeValid(
223223

224224
// If valid motion primitives are set, check intermediary poses > 1 cell apart
225225
if (motion_primitive) {
226-
const float pi_2 = 2.0 * M_PI;
227226
const float & grid_resolution = motion_table.lattice_metadata.grid_resolution;
228227
const float & resolution_diag_sq = 2.0 * grid_resolution * grid_resolution;
229228
MotionPose last_pose(1e9, 1e9, 1e9), pose_dist(0.0, 0.0, 0.0);
@@ -246,7 +245,7 @@ bool NodeLattice::isNodeValid(
246245
// If reversing, invert the angle because the robot is backing into the primitive
247246
// not driving forward with it
248247
if (is_backwards) {
249-
prim_pose._theta = std::fmod(it->_theta + M_PI, pi_2);
248+
prim_pose._theta = std::fmod(it->_theta + M_PI, 2.0 * M_PI);
250249
} else {
251250
prim_pose._theta = it->_theta;
252251
}
@@ -561,7 +560,7 @@ void NodeLattice::addNodeToPath(
561560
Coordinates initial_pose, prim_pose;
562561
MotionPrimitive * prim = nullptr;
563562
const float & grid_resolution = NodeLattice::motion_table.lattice_metadata.grid_resolution;
564-
const float pi_2 = 2.0 * M_PI;
563+
const float epsilon = std::numeric_limits<float>::epsilon();
565564
prim = current_node->getMotionPrimitive();
566565
// if motion primitive is valid, then was searched (rather than analytically expanded),
567566
// include dense path of subpoints making up the primitive at grid resolution
@@ -577,7 +576,7 @@ void NodeLattice::addNodeToPath(
577576
// If reversing, invert the angle because the robot is backing into the primitive
578577
// not driving forward with it
579578
if (current_node->isBackward()) {
580-
prim_pose.theta = std::fmod(it->_theta + M_PI, pi_2);
579+
prim_pose.theta = std::fmod(it->_theta + M_PI, 2.0 * M_PI);
581580
} else {
582581
prim_pose.theta = it->_theta;
583582
}

nav2_smac_planner/test/test_nodelattice.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,3 +288,70 @@ TEST(NodeLatticeTest, test_get_neighbors)
288288

289289
delete costmapA;
290290
}
291+
292+
TEST(NodeLatticeTest, test_node_lattice_custom_footprint)
293+
{
294+
std::string pkg_share_dir = ament_index_cpp::get_package_share_directory("nav2_smac_planner");
295+
std::string filePath =
296+
pkg_share_dir +
297+
"/sample_primitives/5cm_resolution/0.5m_turning_radius/ackermann" +
298+
"/output.json";
299+
300+
nav2_smac_planner::SearchInfo info;
301+
info.minimum_turning_radius = 0.5;
302+
info.non_straight_penalty = 1;
303+
info.change_penalty = 1;
304+
info.reverse_penalty = 1;
305+
info.cost_penalty = 1;
306+
info.retrospective_penalty = 0.1;
307+
info.analytic_expansion_ratio = 1;
308+
info.lattice_filepath = filePath;
309+
info.cache_obstacle_heuristic = true;
310+
info.allow_reverse_expansion = true;
311+
312+
unsigned int x = 100;
313+
unsigned int y = 100;
314+
unsigned int angle_quantization = 16;
315+
316+
nav2_smac_planner::NodeLattice::initMotionModel(
317+
nav2_smac_planner::MotionModel::STATE_LATTICE, x, y, angle_quantization, info);
318+
319+
nav2_smac_planner::NodeLattice node(49);
320+
321+
nav2_costmap_2d::Costmap2D * costmap = new nav2_costmap_2d::Costmap2D(
322+
40, 40, 0.05, 0.0, 0.0, 0);
323+
std::unique_ptr<nav2_smac_planner::GridCollisionChecker> checker =
324+
std::make_unique<nav2_smac_planner::GridCollisionChecker>(costmap, 72);
325+
326+
// Make some custom asymmetrical footprint
327+
nav2_costmap_2d::Footprint footprint;
328+
geometry_msgs::msg::Point p;
329+
p.x = -0.1;
330+
p.y = -0.15;
331+
footprint.push_back(p);
332+
p.x = 0.35;
333+
p.y = -0.15;
334+
footprint.push_back(p);
335+
p.x = 0.35;
336+
p.y = 0.22;
337+
footprint.push_back(p);
338+
p.x = -0.1;
339+
p.y = 0.22;
340+
footprint.push_back(p);
341+
checker->setFootprint(footprint, false, 0.0);
342+
343+
// Test that the node is valid though all motion primitives poses for custom footprint
344+
// Setting initial robot pose to (1.0, 1.0, 0.0)
345+
node.pose.x = 20;
346+
node.pose.y = 20;
347+
node.pose.theta = 0;
348+
nav2_smac_planner::MotionPrimitivePtrs motion_primitives =
349+
nav2_smac_planner::NodeLattice::motion_table.getMotionPrimitives(&node);
350+
EXPECT_GT(motion_primitives.size(), 0);
351+
for (unsigned int i = 0; i < motion_primitives.size(); i++) {
352+
EXPECT_EQ(node.isNodeValid(true, checker.get(), motion_primitives[i], false), true);
353+
EXPECT_EQ(node.isNodeValid(true, checker.get(), motion_primitives[i], true), true);
354+
}
355+
356+
delete costmap;
357+
}

0 commit comments

Comments
 (0)