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