Skip to content

Commit b91eda5

Browse files
authored
Check if the tolerance circle is feasible when validating goals for path planning. (#5593)
* Implemented goal tolerance validity check Signed-off-by: Abhishekh Reddy <helloarm@pm.me> * Fixed description for getCoords function Signed-off-by: Abhishekh Reddy <helloarm@pm.me> * Added a test with lower tolerance for zone validity checking Signed-off-by: Abhishekh Reddy <helloarm@pm.me> * Updated tolerance check function implementation Signed-off-by: Abhishekh Reddy <helloarm@pm.me> * Updated isZoneValid function Signed-off-by: Abhishekh Reddy <helloarm@pm.me> * Updated isZoneValid function Signed-off-by: Abhishekh Reddy <helloarm@pm.me> --------- Signed-off-by: Abhishekh Reddy <helloarm@pm.me>
1 parent 48e7e06 commit b91eda5

File tree

3 files changed

+93
-3
lines changed

3 files changed

+93
-3
lines changed

nav2_smac_planner/include/nav2_smac_planner/goal_manager.hpp

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#ifndef NAV2_SMAC_PLANNER__GOAL_MANAGER_HPP_
1818
#define NAV2_SMAC_PLANNER__GOAL_MANAGER_HPP_
1919

20+
#include <algorithm>
2021
#include <unordered_set>
2122
#include <vector>
2223
#include <functional>
@@ -112,6 +113,74 @@ class GoalManager
112113
}
113114
}
114115

116+
/**
117+
* @brief Checks if zone within the radius of a node is feasible. Returns true if
118+
* there's at least one non-lethal cell within the node radius.
119+
* @param node Input node.
120+
* @param radius Search radius.
121+
* @param collision_checker Collision checker to validate nearby nodes.
122+
* @param traverse_unknown Flag whether traversal through unknown space is allowed.
123+
* @return true
124+
* @return false
125+
*/
126+
bool isZoneValid(
127+
const NodePtr node, const float & radius, GridCollisionChecker * collision_checker,
128+
const bool & traverse_unknown) const
129+
{
130+
if (radius < 1) {
131+
return false;
132+
}
133+
134+
const auto size_x = collision_checker->getCostmap()->getSizeInCellsX();
135+
const auto size_y = collision_checker->getCostmap()->getSizeInCellsY();
136+
137+
auto getIndexFromPoint = [&size_x] (const Coordinates & point) {
138+
unsigned int index = 0;
139+
140+
const auto mx = static_cast<unsigned int>(point.x);
141+
const auto my = static_cast<unsigned int>(point.y);
142+
143+
if constexpr (!std::is_same_v<NodeT, Node2D>) {
144+
const auto angle = static_cast<unsigned int>(point.theta);
145+
index = NodeT::getIndex(mx, my, angle);
146+
} else {
147+
index = NodeT::getIndex(mx, my, size_x);
148+
}
149+
150+
return index;
151+
};
152+
153+
const Coordinates & center_point = node->pose;
154+
const float min_x = std::max(0.0f, std::floor(center_point.x - radius));
155+
const float min_y = std::max(0.0f, std::floor(center_point.y - radius));
156+
const float max_x =
157+
std::min(static_cast<float>(size_x - 1), std::ceil(center_point.x + radius));
158+
const float max_y =
159+
std::min(static_cast<float>(size_y - 1), std::ceil(center_point.y + radius));
160+
const float radius_sq = radius * radius;
161+
162+
Coordinates m;
163+
for (m.x = min_x; m.x <= max_x; m.x += 1.0f) {
164+
for (m.y = min_y; m.y <= max_y; m.y += 1.0f) {
165+
const float dx = m.x - center_point.x;
166+
const float dy = m.y - center_point.y;
167+
168+
if (dx * dx + dy * dy > radius_sq) {
169+
continue;
170+
}
171+
172+
NodeT current_node(getIndexFromPoint(m));
173+
current_node.setPose(m);
174+
175+
if (current_node.isNodeValid(traverse_unknown, collision_checker)) {
176+
return true;
177+
}
178+
}
179+
}
180+
181+
return false;
182+
}
183+
115184
/**
116185
* @brief Filters and marks invalid goals based on collision checking and tolerance thresholds.
117186
*
@@ -133,7 +202,7 @@ class GoalManager
133202
}
134203
for (unsigned int i = 0; i < _goals_state.size(); i++) {
135204
if (_goals_state[i].goal->isNodeValid(traverse_unknown, collision_checker) ||
136-
tolerance > 0.001)
205+
isZoneValid(_goals_state[i].goal, tolerance, collision_checker, traverse_unknown))
137206
{
138207
_goals_state[i].is_valid = true;
139208
_goals_set.insert(_goals_state[i].goal);

nav2_smac_planner/include/nav2_smac_planner/node_2d.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ class Node2D
225225
}
226226

227227
/**
228-
* @brief Get index
228+
* @brief Get coordinates using index
229229
* @param Index Index of point
230230
* @return coordinates of point
231231
*/

nav2_smac_planner/test/test_goal_manager.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ TEST(GoalManagerTest, test_goal_manager)
116116
goal_manager.addGoal(pose_b);
117117
goal_manager.addGoal(pose_c);
118118

119-
int low_tolerance = 0.0f;
119+
int low_tolerance = 10.0f;
120120
goal_manager.removeInvalidGoals(low_tolerance, checker.get(), allow_unknow);
121121

122122
EXPECT_EQ(goal_manager.getGoalsSet().size(), 2);
@@ -131,6 +131,27 @@ TEST(GoalManagerTest, test_goal_manager)
131131
}
132132
}
133133

134+
// Test with zero tolerance, expect invalid goal to be still filtered out
135+
goal_manager.clear();
136+
goal_manager.addGoal(pose_a);
137+
goal_manager.addGoal(pose_b);
138+
goal_manager.addGoal(pose_c);
139+
140+
int zero_tolerance = 0.0f;
141+
goal_manager.removeInvalidGoals(zero_tolerance, checker.get(), allow_unknow);
142+
143+
EXPECT_EQ(goal_manager.getGoalsSet().size(), 2);
144+
EXPECT_EQ(goal_manager.getGoalsCoordinates().size(), 2);
145+
146+
// expect last goal to be invalid
147+
for (const auto & goal_state : goal_manager.getGoalsState()) {
148+
if (goal_state.goal == pose_c) {
149+
EXPECT_FALSE(goal_state.is_valid);
150+
} else {
151+
EXPECT_TRUE(goal_state.is_valid);
152+
}
153+
}
154+
134155
// Prepare goals for expansion
135156
goal_manager.clear();
136157
unsigned int test_goal_size = 16;

0 commit comments

Comments
 (0)