@@ -510,8 +510,71 @@ 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 translation_threshold Maximum translation per pose transition
517+ * @param rotation_threshold Minimum total rotation to consider significant
518+ * @return Index after the rotation sequence if found, path size if no rotation detected
519+ */
520+ inline unsigned int findFirstPathRotation (
521+ const nav_msgs::msg::Path & path,
522+ float translation_threshold,
523+ float rotation_threshold)
524+ {
525+ if (path.poses .size () < 2 ) {
526+ return path.poses .size ();
527+ }
528+
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);
552+
553+ // If individual translation is above threshold, end of rotation sequence
554+ if (next_translation >= translation_threshold) {
555+ break ;
556+ }
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 ;
569+ }
570+ }
571+
572+ return path.poses .size (); // No significant rotation detected
573+ }
574+
513575/* *
514576 * @brief Find the iterator of the first pose at which there is an inversion on the path,
577+ * or where the robot rotates in place
515578 * @param path to check for inversion
516579 * @return the first point after the inversion found in the path
517580 */
@@ -563,6 +626,38 @@ inline unsigned int removePosesAfterFirstInversion(nav_msgs::msg::Path & path)
563626 return first_after_inversion;
564627}
565628
629+ /* *
630+ * @brief Find and remove poses after the first in-place rotation in the path
631+ * @param path to check for rotation
632+ * @param translation_threshold Maximum translation per pose transition
633+ * @param rotation_threshold Minimum total rotation to consider significant
634+ * @return The location of the rotation, return 0 if none exist
635+ */
636+ inline unsigned int removePosesAfterFirstRotation (
637+ nav_msgs::msg::Path & path,
638+ float translation_threshold,
639+ float rotation_threshold)
640+ {
641+ if (path.poses .size () < 2 ) {
642+ return 0u ;
643+ }
644+
645+ nav_msgs::msg::Path cropped_path = path;
646+ // Find first in-place rotation
647+ const unsigned int first_after_rotation = findFirstPathRotation (
648+ cropped_path, translation_threshold, rotation_threshold);
649+
650+ if (first_after_rotation == path.poses .size ()) {
651+ return 0u ; // No rotation found
652+ }
653+
654+ // Remove poses after the rotation
655+ cropped_path.poses .erase (
656+ cropped_path.poses .begin () + first_after_rotation, cropped_path.poses .end ());
657+ path = cropped_path;
658+ return first_after_rotation;
659+ }
660+
566661/* *
567662 * @brief Compare to trajectory points to find closest path point along integrated distances
568663 * @param vec Vect to check
0 commit comments