acceptance_test_joint_limits.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # Copyright (c) 2018 Pilz GmbH & Co. KG
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 
16 from __future__ import print_function
17 
18 import unittest
19 from math import radians
20 
21 import rospy
22 from urdf_parser_py.urdf import URDF
23 from actionlib import SimpleActionClient
24 from control_msgs.msg import FollowJointTrajectoryAction, FollowJointTrajectoryGoal, FollowJointTrajectoryResult
25 from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint
26 from sensor_msgs.msg import JointState
27 
28 _CONTROLLER_ACTION_NAME = '/prbt/manipulator_joint_trajectory_controller/follow_joint_trajectory'
29 _ACTION_SERVER_TIMEOUT_SEC = 5
30 _JOINT_STATES_TOPIC_NAME = '/joint_states'
31 _WAIT_FOR_MESSAGE_TIMEOUT_SEC = 1
32 
33 # Use axis ranges from data sheet: https://www.pilz.com/download/open/PRBT_6_Operat_Manual_1004685-EN-02.pdf (page 19)
34 _JOINT_LIMITS_DEGREE = {
35  'prbt_joint_1': 170,
36  'prbt_joint_2': 145,
37  'prbt_joint_3': 135,
38  'prbt_joint_4': 170,
39  'prbt_joint_5': 170,
40  'prbt_joint_6': 179
41 }
42 _JOINT_POSITIONS_TOLERANCE = 0.001
43 _VELOCITY_SCALE = 0.2
44 _JOINT_LIMIT_OVERSTEP = 0.1
45 
46 
47 class AcceptanceTestJointLimits(unittest.TestCase):
48  """ Test that each joint can be moved exactly to its limits and not further.
49  """
50 
51  def setUp(self):
52  """ Initialize a client for sending trajectories to the controller.
53  """
54  self.client = SimpleActionClient(_CONTROLLER_ACTION_NAME, FollowJointTrajectoryAction)
55  if not self.client.wait_for_server(timeout=rospy.Duration(_ACTION_SERVER_TIMEOUT_SEC)):
56  self.fail('Timed out waiting for action server ' + _CONTROLLER_ACTION_NAME)
57 
58  self.joint_names = sorted(_JOINT_LIMITS_DEGREE.keys())
59  self.home_positions = [0] * len(self.joint_names)
60 
61  # read joint limits from urdf
62  self.joint_velocity_limits = [0] * len(self.joint_names)
63  robot = URDF.from_parameter_server("robot_description")
64 
65  for joint in robot.joints:
66  if joint.name in self.joint_names:
67  index = self.joint_names.index(joint.name)
68  self.joint_velocity_limits[index] = joint.limit.velocity
69 
70  self.assertTrue(all(self.joint_velocity_limits))
71 
72  def _ask_for_permission(self, test_name):
73  s = raw_input('Perform ' + test_name + ' [(y)es, (n)o]?: ')
74  if(s == "n"):
75  print('\n\nSkip ' + test_name + '\n___TEST-END___\n')
76  return 0
77  print('\n\nStart ' + test_name + '\n')
78  return 1
79 
81  """ Return the current joint state positions in the order given by self.joint_names.
82  """
83  msg = JointState()
84  positions = []
85  try:
86  msg = rospy.wait_for_message(_JOINT_STATES_TOPIC_NAME, JointState, timeout =_WAIT_FOR_MESSAGE_TIMEOUT_SEC)
87  except rospy.ROSException:
88  self.fail('Could not retrieve message from topic ' + _JOINT_STATES_TOPIC_NAME)
89 
90  for name in self.joint_names:
91  try:
92  index = msg.name.index(name)
93  positions.append(msg.position[index])
94  except ValueError:
95  self.fail('Could not retrieve joint state position for ' + name)
96 
97  return positions
98 
100  """ Check if current joint positions are within the limits.
101  """
102  positions = self._current_joint_state_positions()
103 
104  for i in range(len(self.joint_names)):
105  position = positions[i]
106  name = self.joint_names[i]
107  limit = radians(_JOINT_LIMITS_DEGREE[name])+_JOINT_POSITIONS_TOLERANCE
108 
109  self.assertGreater(position, -limit, 'Joint ' + name + ' violates lower limit. Position: ' + str(position))
110  self.assertLess(position, limit, 'Joint ' + name + ' violates upper limit. Position: ' + str(position))
111 
112  def _drive_home(self):
113  """ Move the robot to the home position.
114  """
115  positions = self._current_joint_state_positions()
116  diffs = []
117  for i in range(len(positions)):
118  diffs.append(abs(positions[i]-self.home_positions[i]))
119 
120  if any(diff > _JOINT_POSITIONS_TOLERANCE for diff in diffs):
122 
123  def _execute_trajectory(self, positions):
124  """ Execute a single point trajectory given through joint positions (in the order given by self.joint_names). Return true on success and
125  false otherwise.
126  """
127  self.assertEqual(len(positions), len(self.joint_names))
128 
129  # determine duration from distance, max velocity and velocity scaling
130  current_positions = self._current_joint_state_positions()
131  durations = []
132 
133  for i in range(len(current_positions)):
134  distance = abs(positions[i] - current_positions[i])
135  durations.append(distance/self.joint_velocity_limits[i])
136 
137  duration = max(durations)/_VELOCITY_SCALE
138 
139  # construct goal
140  traj_point = JointTrajectoryPoint()
141  traj_point.positions = positions
142  traj_point.time_from_start = rospy.Duration(duration)
143 
144  traj = JointTrajectory()
145  traj.joint_names = self.joint_names
146  traj.points.append(traj_point)
147 
148  goal = FollowJointTrajectoryGoal()
149  goal.trajectory = traj
150 
151  # send to trajectory controller
152  self.client.send_goal(goal)
153  self.client.wait_for_result()
154 
155  # evaluate result
156  result = self.client.get_result()
157  success = (result.error_code == FollowJointTrajectoryResult.SUCCESSFUL)
158  if not success:
159  rospy.loginfo('Failure executing: ' + str(goal))
160  return success
161 
162  def _joint_limit_reaching_test(self, joint_name):
163  """ Test if the robot can be commanded to move exactly to the limits
164 
165  Test Sequence:
166  1. Command a movement to the home position.
167  2. Command a movement to the lower limit.
168  3. Command a movement to the upper limit.
169  4. Command a movement to the home position.
170 
171  Expected Results:
172  1. Trajectory is executed successfully.
173  2. Trajectory is executed successfully.
174  3. Trajectory is executed successfully.
175  4. Trajectory is executed successfully.
176  """
177  self._drive_home()
178 
179  index = self.joint_names.index(joint_name)
180  limit = _JOINT_LIMITS_DEGREE[joint_name]
181 
182  lower_positions = [0] * len(self.joint_names)
183  lower_positions[index] = -radians(limit)
184 
185  self.assertTrue(self._execute_trajectory(lower_positions))
186 
187  upper_positions = [0] * len(self.joint_names)
188  upper_positions[index] = radians(limit)
189 
190  self.assertTrue(self._execute_trajectory(upper_positions))
191 
192  self._drive_home()
193 
194  def _joint_limit_overstepping_test(self, joint_name):
195  """ Test if the robot does not overstep the limits
196 
197  Test Sequence:
198  1. Command a movement to the home position.
199  2. Command a movement overstepping the lower limit.
200  3. Command a movement overstepping the upper limit.
201  4. Command a movement to the home position.
202 
203  Expected Results:
204  1. Trajectory is executed successfully.
205  2. Trajectory execution is aborted and the robot does not overstep the limits.
206  3. Trajectory execution is aborted and the robot does not overstep the limits.
207  4. Trajectory is executed successfully.
208  """
209  self._drive_home()
210 
211  index = self.joint_names.index(joint_name)
212  limit = _JOINT_LIMITS_DEGREE[joint_name]
213 
214  lower_positions = [0] * len(self.joint_names)
215  lower_positions[index] = -(radians(limit) + _JOINT_LIMIT_OVERSTEP)
216 
217  self.assertFalse(self._execute_trajectory(lower_positions))
218  self._check_joint_limits()
219 
220  upper_positions = [0] * len(self.joint_names)
221  upper_positions[index] = radians(limit) + _JOINT_LIMIT_OVERSTEP
222 
223  self.assertFalse(self._execute_trajectory(upper_positions))
224  self._check_joint_limits()
225 
226  self._drive_home()
227 
229  """ Perform all reaching tests. Before each test ask the user if he wants to skip it.
230  """
231  for name in self.joint_names:
232  if self._ask_for_permission('joint_limit_reaching_test for ' + name):
233  self._joint_limit_reaching_test(name)
234 
236  """ Perform all overstepping tests. Before each test ask the user if he wants to skip it.
237  """
238  for name in self.joint_names:
239  if self._ask_for_permission('joint_limit_overstepping_test for ' + name):
241 
242 
243 if __name__ == "__main__":
244  # init ros node
245  rospy.init_node('acceptance_test_joint_limits_node')
246  unittest.main()


prbt_support
Author(s):
autogenerated on Tue Feb 2 2021 03:50:33