Skip to content

Commit 2a1060f

Browse files
committed
PR fixes
Signed-off-by: Tony Najjar <[email protected]>
1 parent ee9297b commit 2a1060f

File tree

3 files changed

+76
-86
lines changed

3 files changed

+76
-86
lines changed

nav2_mppi_controller/include/nav2_mppi_controller/tools/path_handler.hpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,9 @@ class PathHandler
129129
void prunePlan(nav_msgs::msg::Path & plan, const PathIterator end);
130130

131131
/**
132-
* @brief Check if the robot pose is within the set inversion tolerances
132+
* @brief Check if the robot pose is within the set inversion or rotation tolerances
133133
* @param robot_pose Robot's current pose to check
134-
* @return bool If the robot pose is within the set inversion tolerances
134+
* @return bool If the robot pose is within the tolerances for the next path constraint
135135
*/
136136
bool isWithinInversionTolerances(const geometry_msgs::msg::PoseStamped & robot_pose);
137137

@@ -156,11 +156,8 @@ class PathHandler
156156
double prune_distance_{0};
157157
double transform_tolerance_{0};
158158
float inversion_xy_tolerance_{0.2};
159-
float inversion_yaw_tolerance{0.4};
160-
float rotation_xy_tolerance_{0.2};
161-
float rotation_yaw_tolerance_{0.4};
162-
float rotation_translation_threshold_{0.02};
163-
float rotation_rotation_threshold_{0.785};
159+
float inversion_yaw_tolerance_{0.4};
160+
float minimum_rotation_angle_{0.785};
164161
bool enforce_path_inversion_{false};
165162
bool enforce_path_rotation_{false};
166163
unsigned int inversion_locale_{0u};

nav2_mppi_controller/include/nav2_mppi_controller/tools/utils.hpp

Lines changed: 53 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -513,59 +513,50 @@ inline void savitskyGolayFilter(
513513
/**
514514
* @brief Find the first pose at which there is an in-place rotation in the path
515515
* @param path Path to check
516-
* @param translation_threshold Maximum translation per pose transition
517-
* @param rotation_threshold Minimum total rotation to consider significant
516+
* @param rotation_threshold Minimum rotation angle to consider this an in-place rotation
518517
* @return Index after the rotation sequence if found, path size if no rotation detected
519518
*/
520519
inline unsigned int findFirstPathRotation(
521520
const nav_msgs::msg::Path & path,
522-
float translation_threshold,
523521
float rotation_threshold)
524522
{
525523
if (path.poses.size() < 2) {
526524
return path.poses.size();
527525
}
528526

529-
// Iterate through path to find first rotation sequence
530-
for (unsigned int start_idx = 0; start_idx < path.poses.size() - 1; ++start_idx) {
531-
// Check initial translation
532-
float ab_x = path.poses[start_idx + 1].pose.position.x -
533-
path.poses[start_idx].pose.position.x;
534-
float ab_y = path.poses[start_idx + 1].pose.position.y -
535-
path.poses[start_idx].pose.position.y;
536-
float initial_translation = sqrtf(ab_x * ab_x + ab_y * ab_y);
537-
538-
if (initial_translation >= translation_threshold) {
539-
continue; // Not a rotation sequence, try next pose
540-
}
541-
542-
// Start of potential rotation sequence
543-
float start_yaw = tf2::getYaw(path.poses[start_idx].pose.orientation);
544-
unsigned int j = start_idx + 1;
545-
unsigned int last_valid_j = j; // Track the last pose in the rotation sequence
546-
547-
// Keep accumulating poses while translation remains small
548-
while (j < path.poses.size() - 1) {
549-
float next_x = path.poses[j + 1].pose.position.x - path.poses[j].pose.position.x;
550-
float next_y = path.poses[j + 1].pose.position.y - path.poses[j].pose.position.y;
551-
float next_translation = sqrtf(next_x * next_x + next_y * next_y);
527+
// Iterate through path to find first in-place rotation
528+
for (unsigned int idx = 0; idx < path.poses.size() - 1; ++idx) {
529+
float dx = path.poses[idx + 1].pose.position.x - path.poses[idx].pose.position.x;
530+
float dy = path.poses[idx + 1].pose.position.y - path.poses[idx].pose.position.y;
531+
float translation = sqrtf(dx * dx + dy * dy);
532+
533+
// Check if poses are at roughly the same location
534+
if (translation < 1e-3) {
535+
float yaw1 = tf2::getYaw(path.poses[idx].pose.orientation);
536+
float yaw2 = tf2::getYaw(path.poses[idx + 1].pose.orientation);
537+
float rotation = fabs(angles::shortest_angular_distance(yaw1, yaw2));
538+
539+
// Check if this meets the minimum rotation threshold
540+
if (rotation > rotation_threshold) {
541+
// Found start of in-place rotation, now find where it ends
542+
unsigned int end_idx = idx + 1;
543+
544+
// Continue while we have minimal translation (still rotating in place)
545+
while (end_idx < path.poses.size() - 1) {
546+
float next_dx = path.poses[end_idx + 1].pose.position.x -
547+
path.poses[end_idx].pose.position.x;
548+
float next_dy = path.poses[end_idx + 1].pose.position.y -
549+
path.poses[end_idx].pose.position.y;
550+
float next_translation = sqrtf(next_dx * next_dx + next_dy * next_dy);
551+
552+
if (next_translation >= 1e-3) {
553+
break; // End of in-place rotation sequence
554+
}
555+
end_idx++;
556+
}
552557

553-
// If individual translation is above threshold, end of rotation sequence
554-
if (next_translation >= translation_threshold) {
555-
break;
558+
return end_idx;
556559
}
557-
558-
j++;
559-
last_valid_j = j; // Update the last valid pose in the sequence
560-
}
561-
562-
// Check if we have accumulated enough rotation
563-
float end_yaw = tf2::getYaw(path.poses[last_valid_j].pose.orientation);
564-
float total_rotation = fabs(angles::shortest_angular_distance(start_yaw, end_yaw));
565-
566-
if (total_rotation > rotation_threshold) {
567-
// Return the index after the last pose in the rotation sequence
568-
return last_valid_j + 1;
569560
}
570561
}
571562

@@ -608,22 +599,35 @@ inline unsigned int findFirstPathInversion(nav_msgs::msg::Path & path)
608599
}
609600

610601
/**
611-
* @brief Find and remove poses after the first inversion in the path
612-
* @param path to check for inversion
613-
* @return The location of the inversion, return 0 if none exist
602+
* @brief Find and remove poses after the first inversion or in-place rotation in the path
603+
* @param path Path to check for inversion or rotation
604+
* @param rotation_threshold Minimum rotation angle to consider an in-place rotation (0 to disable rotation check)
605+
* @return The location of the inversion/rotation, return 0 if none exist
614606
*/
615-
inline unsigned int removePosesAfterFirstInversion(nav_msgs::msg::Path & path)
607+
inline unsigned int removePosesAfterFirstInversion(
608+
nav_msgs::msg::Path & path,
609+
float rotation_threshold = 0.0f)
616610
{
617611
nav_msgs::msg::Path cropped_path = path;
612+
613+
// Check for in-place rotation first (if enabled)
614+
unsigned int first_constraint = path.poses.size();
615+
if (rotation_threshold > 0.0f) {
616+
first_constraint = findFirstPathRotation(cropped_path, rotation_threshold);
617+
}
618+
619+
// Check for inversion and take whichever comes first
618620
const unsigned int first_after_inversion = findFirstPathInversion(cropped_path);
619-
if (first_after_inversion == path.poses.size()) {
620-
return 0u;
621+
first_constraint = std::min(first_constraint, first_after_inversion);
622+
623+
if (first_constraint == path.poses.size()) {
624+
return 0u; // No constraint found
621625
}
622626

623627
cropped_path.poses.erase(
624-
cropped_path.poses.begin() + first_after_inversion, cropped_path.poses.end());
628+
cropped_path.poses.begin() + first_constraint, cropped_path.poses.end());
625629
path = cropped_path;
626-
return first_after_inversion;
630+
return first_constraint;
627631
}
628632

629633
/**

nav2_mppi_controller/src/path_handler.cpp

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,16 @@ void PathHandler::initialize(
3939
getParam(prune_distance_, "prune_distance", 1.5);
4040
getParam(transform_tolerance_, "transform_tolerance", 0.1);
4141
getParam(enforce_path_inversion_, "enforce_path_inversion", false);
42-
if (enforce_path_inversion_) {
42+
getParam(enforce_path_rotation_, "enforce_path_rotation", false);
43+
44+
if (enforce_path_inversion_ || enforce_path_rotation_) {
4345
getParam(inversion_xy_tolerance_, "inversion_xy_tolerance", 0.2);
44-
getParam(inversion_yaw_tolerance, "inversion_yaw_tolerance", 0.4);
46+
getParam(inversion_yaw_tolerance_, "inversion_yaw_tolerance", 0.4);
4547
inversion_locale_ = 0u;
4648
}
47-
getParam(enforce_path_rotation_, "enforce_path_rotation", false);
49+
4850
if (enforce_path_rotation_) {
49-
getParam(rotation_xy_tolerance_, "rotation_xy_tolerance", 0.2);
50-
getParam(rotation_yaw_tolerance_, "rotation_yaw_tolerance", 0.4);
51-
getParam(rotation_translation_threshold_, "rotation_translation_threshold", 0.02);
52-
getParam(rotation_rotation_threshold_, "rotation_rotation_threshold", 0.785);
53-
rotation_locale_ = 0u;
51+
getParam(minimum_rotation_angle_, "minimum_rotation_angle", 0.785);
5452
}
5553
}
5654

@@ -139,11 +137,16 @@ nav_msgs::msg::Path PathHandler::transformPath(
139137

140138
prunePlan(global_plan_up_to_inversion_, lower_bound);
141139

142-
if (enforce_path_inversion_ && inversion_locale_ != 0u) {
140+
if ((enforce_path_inversion_ || enforce_path_rotation_) && inversion_locale_ != 0u) {
143141
if (isWithinInversionTolerances(global_pose)) {
142+
// Robot has reached the inversion/rotation point, unlock the rest of the path
144143
prunePlan(global_plan_, global_plan_.poses.begin() + inversion_locale_);
145144
global_plan_up_to_inversion_ = global_plan_;
146-
inversion_locale_ = utils::removePosesAfterFirstInversion(global_plan_up_to_inversion_);
145+
146+
// Recompute locale on the updated path
147+
float rotation_check = enforce_path_rotation_ ? minimum_rotation_angle_ : 0.0f;
148+
inversion_locale_ = utils::removePosesAfterFirstInversion(
149+
global_plan_up_to_inversion_, rotation_check);
147150
}
148151
}
149152

@@ -173,26 +176,12 @@ double PathHandler::getMaxCostmapDist()
173176
void PathHandler::setPath(const nav_msgs::msg::Path & plan)
174177
{
175178
global_plan_ = plan;
179+
global_plan_up_to_inversion_ = plan;
176180

177-
// Start with the full plan and progressively apply restrictions
178-
nav_msgs::msg::Path most_restrictive_plan = global_plan_;
179-
180-
// Check for rotation restrictions first
181-
if (enforce_path_rotation_) {
182-
rotation_locale_ = utils::removePosesAfterFirstRotation(
183-
most_restrictive_plan,
184-
rotation_translation_threshold_,
185-
rotation_rotation_threshold_);
186-
}
187-
188-
// Check for inversion restrictions (may further restrict the path)
189-
if (enforce_path_inversion_) {
190-
inversion_locale_ = utils::removePosesAfterFirstInversion(most_restrictive_plan);
191-
}
192-
193-
// Set the working paths to the most restrictive version
194-
global_plan_up_to_rotation_ = most_restrictive_plan;
195-
global_plan_up_to_inversion_ = most_restrictive_plan;
181+
// Find and restrict to the first rotation or inversion constraint
182+
float rotation_check = enforce_path_rotation_ ? minimum_rotation_angle_ : 0.0f;
183+
inversion_locale_ = utils::removePosesAfterFirstInversion(
184+
global_plan_up_to_inversion_, rotation_check);
196185
}
197186

198187
nav_msgs::msg::Path & PathHandler::getPath() {return global_plan_;}
@@ -232,7 +221,7 @@ bool PathHandler::isWithinInversionTolerances(const geometry_msgs::msg::PoseStam
232221
tf2::getYaw(robot_pose.pose.orientation),
233222
tf2::getYaw(last_pose.pose.orientation));
234223

235-
return distance <= inversion_xy_tolerance_ && fabs(angle_distance) <= inversion_yaw_tolerance;
224+
return distance <= inversion_xy_tolerance_ && fabs(angle_distance) <= inversion_yaw_tolerance_;
236225
}
237226

238227
bool PathHandler::isWithinRotationTolerances(const geometry_msgs::msg::PoseStamped & robot_pose)

0 commit comments

Comments
 (0)