@@ -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 */
520519inline 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/* *
0 commit comments