4141from control_msgs .msg import JointTolerance
4242from controller_manager_msgs .srv import SwitchController
4343from rclpy .node import Node
44+ from sensor_msgs .msg import JointState
4445from trajectory_msgs .msg import JointTrajectory , JointTrajectoryPoint
4546
4647sys .path .append (os .path .dirname (__file__ ))
@@ -116,11 +117,7 @@ def setUp(self):
116117 time .sleep (1 )
117118 self .assertTrue (self ._io_status_controller_interface .resend_robot_program ().success )
118119
119- #
120- # Test functions
121- #
122-
123- def test_start_passthrough_controller (self ):
120+ def _activate_passthrough_controller (self ):
124121 self .assertTrue (
125122 self ._controller_manager_interface .switch_controller (
126123 strictness = SwitchController .Request .BEST_EFFORT ,
@@ -129,14 +126,59 @@ def test_start_passthrough_controller(self):
129126 ).ok
130127 )
131128
132- def test_passthrough_trajectory (self , tf_prefix ):
133- self .assertTrue (
134- self ._controller_manager_interface .switch_controller (
135- strictness = SwitchController .Request .BEST_EFFORT ,
136- activate_controllers = ["passthrough_trajectory_controller" ],
137- deactivate_controllers = ["joint_trajectory_controller" ],
138- ).ok
129+ def _get_current_joint_positions (self , tf_prefix , timeout = 5.0 ):
130+ last_joint_state = None
131+
132+ def js_cb (msg ):
133+ nonlocal last_joint_state
134+ last_joint_state = msg
135+
136+ joint_state_sub = self .node .create_subscription (JointState , "/joint_states" , js_cb , 1 )
137+ end_time = time .time () + timeout
138+ while last_joint_state is None and time .time () < end_time :
139+ rclpy .spin_once (self .node , timeout_sec = 0.1 )
140+ self .node .destroy_subscription (joint_state_sub )
141+ self .assertIsNotNone (last_joint_state )
142+
143+ joint_names = [tf_prefix + joint for joint in ROBOT_JOINTS ]
144+ return [
145+ last_joint_state .position [last_joint_state .name .index (name )] for name in joint_names
146+ ]
147+
148+ def _send_single_point_trajectory_at_current_pose (self , tf_prefix ):
149+ joint_names = [tf_prefix + joint for joint in ROBOT_JOINTS ]
150+ positions = self ._get_current_joint_positions (tf_prefix )
151+ trajectory = JointTrajectory (
152+ joint_names = joint_names ,
153+ points = [
154+ JointTrajectoryPoint (
155+ positions = positions ,
156+ time_from_start = Duration (sec = 0 , nanosec = 0 ),
157+ )
158+ ],
139159 )
160+ goal_tolerance = [JointTolerance (position = 0.01 , name = name ) for name in joint_names ]
161+ goal_time_tolerance = Duration (sec = 1 , nanosec = 0 )
162+ goal_handle = self ._passthrough_forward_joint_trajectory .send_goal (
163+ trajectory = trajectory ,
164+ goal_time_tolerance = goal_time_tolerance ,
165+ goal_tolerance = goal_tolerance ,
166+ )
167+ self .assertTrue (goal_handle .accepted )
168+ result = self ._passthrough_forward_joint_trajectory .get_result (
169+ goal_handle , TIMEOUT_EXECUTE_TRAJECTORY
170+ )
171+ self .assertEqual (result .error_code , FollowJointTrajectory .Result .SUCCESSFUL )
172+
173+ #
174+ # Test functions
175+ #
176+
177+ def test_start_passthrough_controller (self ):
178+ self ._activate_passthrough_controller ()
179+
180+ def test_passthrough_trajectory (self , tf_prefix ):
181+ self ._activate_passthrough_controller ()
140182
141183 goal_tolerance = [
142184 JointTolerance (position = 0.01 , name = tf_prefix + joint ) for joint in ROBOT_JOINTS
@@ -161,15 +203,20 @@ def test_passthrough_trajectory(self, tf_prefix):
161203 )
162204 self .assertEqual (result .error_code , FollowJointTrajectory .Result .SUCCESSFUL )
163205
206+ def test_single_point_trajectory_at_current_pose (self , tf_prefix ):
207+ """A trajectory with only one point at time 0.0 at the current pose should succeed."""
208+ self ._activate_passthrough_controller ()
209+ self ._send_single_point_trajectory_at_current_pose (tf_prefix )
210+
211+ def test_consecutive_single_point_trajectories (self , tf_prefix ):
212+ """Two consecutive single-point trajectories at time 0.0 should both succeed."""
213+ self ._activate_passthrough_controller ()
214+ self ._send_single_point_trajectory_at_current_pose (tf_prefix )
215+ self ._send_single_point_trajectory_at_current_pose (tf_prefix )
216+
164217 def test_quintic_trajectory (self , tf_prefix ):
165218 # Full quintic trajectory
166- self .assertTrue (
167- self ._controller_manager_interface .switch_controller (
168- strictness = SwitchController .Request .BEST_EFFORT ,
169- activate_controllers = ["passthrough_trajectory_controller" ],
170- deactivate_controllers = ["joint_trajectory_controller" ],
171- ).ok
172- )
219+ self ._activate_passthrough_controller ()
173220 trajectory = JointTrajectory (
174221 points = [
175222 JointTrajectoryPoint (
@@ -201,13 +248,7 @@ def test_quintic_trajectory(self, tf_prefix):
201248
202249 def test_impossible_goal_tolerance_fails (self , tf_prefix ):
203250 # Test impossible goal tolerance, should fail.
204- self .assertTrue (
205- self ._controller_manager_interface .switch_controller (
206- strictness = SwitchController .Request .BEST_EFFORT ,
207- activate_controllers = ["passthrough_trajectory_controller" ],
208- deactivate_controllers = ["joint_trajectory_controller" ],
209- ).ok
210- )
251+ self ._activate_passthrough_controller ()
211252 trajectory = JointTrajectory (
212253 points = [
213254 JointTrajectoryPoint (positions = pos , time_from_start = times )
@@ -235,13 +276,7 @@ def test_impossible_goal_tolerance_fails(self, tf_prefix):
235276
236277 def test_impossible_goal_time_tolerance_fails (self , tf_prefix ):
237278 # Test impossible goal time
238- self .assertTrue (
239- self ._controller_manager_interface .switch_controller (
240- strictness = SwitchController .Request .BEST_EFFORT ,
241- activate_controllers = ["passthrough_trajectory_controller" ],
242- deactivate_controllers = ["joint_trajectory_controller" ],
243- ).ok
244- )
279+ self ._activate_passthrough_controller ()
245280
246281 goal_tolerance = [
247282 JointTolerance (position = 0.01 , name = tf_prefix + joint ) for joint in ROBOT_JOINTS
0 commit comments