@@ -669,6 +669,59 @@ TEST_F(JointSaturationLimiterTest, when_jerk_exceeded_with_pos_vel_and_acc_expec
669669 }
670670}
671671
672+ TEST_F (JointSaturationLimiterTest, when_acc_limited_vel_overshoots_expect_vel_reclamped)
673+ {
674+ SetupNode (" joint_saturation_limiter" );
675+ Load ();
676+
677+ if (joint_limiter_)
678+ {
679+ Init ();
680+ Configure ();
681+
682+ // dt such that max_acc * dt > max_velocity: 5.0 * 0.5 = 2.5 > 2.0
683+ // Acceleration limiting recomputes velocity which then overshoots max_vel.
684+ // The second-pass velocity clamp should catch it.
685+ rclcpp::Duration period (0 , 500000000 ); // 0.5 second
686+
687+ // forward direction
688+ {
689+ current_joint_states_.positions [0 ] = 0.0 ;
690+ current_joint_states_.velocities [0 ] = 0.0 ;
691+ desired_joint_states_.positions [0 ] = 1.0 ;
692+ desired_joint_states_.velocities [0 ] = 2.0 ;
693+ desired_joint_states_.accelerations [0 ] = 10.0 ;
694+
695+ ASSERT_TRUE (joint_limiter_->enforce (current_joint_states_, desired_joint_states_, period));
696+
697+ CHECK_STATE_SINGLE_JOINT (
698+ desired_joint_states_, 0 ,
699+ 1.0 , // pos = 0 + max_vel * dt
700+ 2.0 , // vel re-clamped to max_vel
701+ 4.0 // acc = (2.0 - 0) / dt
702+ );
703+ }
704+
705+ // reverse direction
706+ {
707+ current_joint_states_.positions [0 ] = 0.0 ;
708+ current_joint_states_.velocities [0 ] = 0.0 ;
709+ desired_joint_states_.positions [0 ] = -1.0 ;
710+ desired_joint_states_.velocities [0 ] = -2.0 ;
711+ desired_joint_states_.accelerations [0 ] = -10.0 ;
712+
713+ ASSERT_TRUE (joint_limiter_->enforce (current_joint_states_, desired_joint_states_, period));
714+
715+ CHECK_STATE_SINGLE_JOINT (
716+ desired_joint_states_, 0 ,
717+ -1.0 , // pos = 0 - max_vel * dt
718+ -2.0 , // vel re-clamped to -max_vel
719+ -4.0 // acc = (-2.0 - 0) / dt
720+ );
721+ }
722+ }
723+ }
724+
672725int main (int argc, char ** argv)
673726{
674727 ::testing::InitGoogleMock (&argc, argv);
0 commit comments