Skip to content

Commit 4d72845

Browse files
committed
PR fixes
Signed-off-by: Tony Najjar <[email protected]>
1 parent 47327a5 commit 4d72845

File tree

3 files changed

+101
-20
lines changed

3 files changed

+101
-20
lines changed

nav2_mppi_controller/include/nav2_mppi_controller/tools/path_handler.hpp

Lines changed: 5 additions & 3 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

@@ -148,8 +148,10 @@ class PathHandler
148148
double prune_distance_{0};
149149
double transform_tolerance_{0};
150150
float inversion_xy_tolerance_{0.2};
151-
float inversion_yaw_tolerance{0.4};
151+
float inversion_yaw_tolerance_{0.4};
152+
float minimum_rotation_angle_{0.785};
152153
bool enforce_path_inversion_{false};
154+
bool enforce_path_rotation_{false};
153155
unsigned int inversion_locale_{0u};
154156
};
155157
} // namespace mppi

nav2_mppi_controller/include/nav2_mppi_controller/tools/utils.hpp

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,59 @@ inline void savitskyGolayFilter(
510510
control_sequence.wz(offset)};
511511
}
512512

513+
/**
514+
* @brief Find the first pose at which there is an in-place rotation in the path
515+
* @param path Path to check
516+
* @param rotation_threshold Minimum rotation angle to consider this an in-place rotation
517+
* @return Index after the rotation sequence if found, path size if no rotation detected
518+
*/
519+
inline unsigned int findFirstPathRotation(
520+
const nav_msgs::msg::Path & path,
521+
float rotation_threshold)
522+
{
523+
if (path.poses.size() < 2) {
524+
return path.poses.size();
525+
}
526+
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+
}
557+
558+
return end_idx;
559+
}
560+
}
561+
}
562+
563+
return path.poses.size(); // No significant rotation detected
564+
}
565+
513566
/**
514567
* @brief Find the iterator of the first pose at which there is an inversion on the path,
515568
* @param path to check for inversion
@@ -545,22 +598,35 @@ inline unsigned int findFirstPathInversion(nav_msgs::msg::Path & path)
545598
}
546599

547600
/**
548-
* @brief Find and remove poses after the first inversion in the path
549-
* @param path to check for inversion
550-
* @return The location of the inversion, return 0 if none exist
601+
* @brief Find and remove poses after the first inversion or in-place rotation in the path
602+
* @param path Path to check for inversion or rotation
603+
* @param rotation_threshold Minimum rotation angle to consider an in-place rotation (0 to disable rotation check)
604+
* @return The location of the inversion/rotation, return 0 if none exist
551605
*/
552-
inline unsigned int removePosesAfterFirstInversion(nav_msgs::msg::Path & path)
606+
inline unsigned int removePosesAfterFirstInversion(
607+
nav_msgs::msg::Path & path,
608+
float rotation_threshold = 0.0f)
553609
{
554610
nav_msgs::msg::Path cropped_path = path;
611+
612+
// Check for in-place rotation first (if enabled)
613+
unsigned int first_constraint = path.poses.size();
614+
if (rotation_threshold > 0.0f) {
615+
first_constraint = findFirstPathRotation(cropped_path, rotation_threshold);
616+
}
617+
618+
// Check for inversion and take whichever comes first
555619
const unsigned int first_after_inversion = findFirstPathInversion(cropped_path);
556-
if (first_after_inversion == path.poses.size()) {
557-
return 0u;
620+
first_constraint = std::min(first_constraint, first_after_inversion);
621+
622+
if (first_constraint == path.poses.size()) {
623+
return 0u; // No constraint found
558624
}
559625

560626
cropped_path.poses.erase(
561-
cropped_path.poses.begin() + first_after_inversion, cropped_path.poses.end());
627+
cropped_path.poses.begin() + first_constraint, cropped_path.poses.end());
562628
path = cropped_path;
563-
return first_after_inversion;
629+
return first_constraint;
564630
}
565631

566632
/**

nav2_mppi_controller/src/path_handler.cpp

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,17 @@ 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
}
49+
50+
if (enforce_path_rotation_) {
51+
getParam(minimum_rotation_angle_, "minimum_rotation_angle", 0.785);
52+
}
4753
}
4854

4955
std::pair<nav_msgs::msg::Path, PathIterator>
@@ -131,11 +137,16 @@ nav_msgs::msg::Path PathHandler::transformPath(
131137

132138
prunePlan(global_plan_up_to_inversion_, lower_bound);
133139

134-
if (enforce_path_inversion_ && inversion_locale_ != 0u) {
140+
if ((enforce_path_inversion_ || enforce_path_rotation_) && inversion_locale_ != 0u) {
135141
if (isWithinInversionTolerances(global_pose)) {
142+
// Robot has reached the inversion/rotation point, unlock the rest of the path
136143
prunePlan(global_plan_, global_plan_.poses.begin() + inversion_locale_);
137144
global_plan_up_to_inversion_ = global_plan_;
138-
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);
139150
}
140151
}
141152

@@ -156,10 +167,12 @@ double PathHandler::getMaxCostmapDist()
156167
void PathHandler::setPath(const nav_msgs::msg::Path & plan)
157168
{
158169
global_plan_ = plan;
159-
global_plan_up_to_inversion_ = global_plan_;
160-
if (enforce_path_inversion_) {
161-
inversion_locale_ = utils::removePosesAfterFirstInversion(global_plan_up_to_inversion_);
162-
}
170+
global_plan_up_to_inversion_ = plan;
171+
172+
// Find and restrict to the first rotation or inversion constraint
173+
float rotation_check = enforce_path_rotation_ ? minimum_rotation_angle_ : 0.0f;
174+
inversion_locale_ = utils::removePosesAfterFirstInversion(
175+
global_plan_up_to_inversion_, rotation_check);
163176
}
164177

165178
nav_msgs::msg::Path & PathHandler::getPath() {return global_plan_;}
@@ -199,7 +212,7 @@ bool PathHandler::isWithinInversionTolerances(const geometry_msgs::msg::PoseStam
199212
tf2::getYaw(robot_pose.pose.orientation),
200213
tf2::getYaw(last_pose.pose.orientation));
201214

202-
return distance <= inversion_xy_tolerance_ && fabs(angle_distance) <= inversion_yaw_tolerance;
215+
return distance <= inversion_xy_tolerance_ && fabs(angle_distance) <= inversion_yaw_tolerance_;
203216
}
204217

205218
} // namespace mppi

0 commit comments

Comments
 (0)