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_NAMES = ['prbt_joint_1', 'prbt_joint_2', 'prbt_joint_3', 'prbt_joint_4', 'prbt_joint_5', 'prbt_joint_6']
35 _JOINT_LIMITS_DEGREE = {
36  'prbt_joint_1': 170,
37  'prbt_joint_2': 145,
38  'prbt_joint_3': 135,
39  'prbt_joint_4': 170,
40  'prbt_joint_5': 170,
41  'prbt_joint_6': 179
42 }
43 _JOINT_POSITIONS_TOLERANCE = 0.001
44 _VELOCITY_SCALE = 0.2
45 _JOINT_LIMIT_OVERSTEP = 0.1
46 _COLLISION_JOINT_NAMES = [
47  # Collision if mounted on a table
48  'prbt_joint_2', \
49  # Self-Collision if gripper is mounted
50  'prbt_joint_5' \
51  ]
52 
53 
54 class AcceptanceTestJointLimits(unittest.TestCase):
55  """ Test that each joint can be moved exactly to its limits and not further.
56  """
57 
58  def setUp(self):
59  """ Initialize a client for sending trajectories to the controller.
60  """
61  self.client = SimpleActionClient(_CONTROLLER_ACTION_NAME, FollowJointTrajectoryAction)
62  if not self.client.wait_for_server(timeout=rospy.Duration(_ACTION_SERVER_TIMEOUT_SEC)):
63  self.fail('Timed out waiting for action server ' + _CONTROLLER_ACTION_NAME)
64 
65  self.joint_names = _JOINT_NAMES
66  self.home_positions = [0] * len(self.joint_names)
67 
68  # read joint limits from urdf
69  self.joint_velocity_limits = [0] * len(self.joint_names)
70  robot = URDF.from_parameter_server("robot_description")
71 
72  for joint in robot.joints:
73  if joint.name in self.joint_names:
74  index = self.joint_names.index(joint.name)
75  self.joint_velocity_limits[index] = joint.limit.velocity
76 
77  self.assertTrue(all(self.joint_velocity_limits))
78 
79  def _ask_for_permission(self, test_name):
80  s = input('Perform ' + test_name + ' [(y)es, (n)o]?: ')
81  if(s == "n"):
82  print('\n\nSkip ' + test_name + '\n___TEST-END___\n')
83  return 0
84  print('\n\nStart ' + test_name + '\n')
85  return 1
86 
88  """ Return the current joint state positions in the order given by self.joint_names.
89  """
90  msg = JointState()
91  positions = []
92  try:
93  msg = rospy.wait_for_message(_JOINT_STATES_TOPIC_NAME, JointState, timeout=_WAIT_FOR_MESSAGE_TIMEOUT_SEC)
94  except rospy.ROSException:
95  self.fail('Could not retrieve message from topic ' + _JOINT_STATES_TOPIC_NAME)
96 
97  for name in self.joint_names:
98  try:
99  index = msg.name.index(name)
100  positions.append(msg.position[index])
101  except ValueError:
102  self.fail('Could not retrieve joint state position for ' + name)
103 
104  return positions
105 
107  """ Check if current joint positions are within the limits.
108  """
109  positions = self._current_joint_state_positions()
110 
111  for i in range(len(self.joint_names)):
112  position = positions[i]
113  name = self.joint_names[i]
114  limit = radians(_JOINT_LIMITS_DEGREE[name])+_JOINT_POSITIONS_TOLERANCE
115 
116  self.assertGreater(position, -limit, 'Joint ' + name + ' violates lower limit. Position: ' + str(position))
117  self.assertLess(position, limit, 'Joint ' + name + ' violates upper limit. Position: ' + str(position))
118 
119  def _drive_home(self):
120  """ Move the robot to the home position.
121  """
122  positions = self._current_joint_state_positions()
123  diffs = []
124  for i in range(len(positions)):
125  diffs.append(abs(positions[i]-self.home_positions[i]))
126 
127  if any(diff > _JOINT_POSITIONS_TOLERANCE for diff in diffs):
129 
130  def _execute_trajectory(self, positions):
131  """ Execute a single point trajectory given through joint positions (in the order given by self.joint_names).
132  Return true on success and false otherwise.
133  """
134  self.assertEqual(len(positions), len(self.joint_names))
135 
136  # determine duration from distance, max velocity and velocity scaling
137  current_positions = self._current_joint_state_positions()
138  durations = []
139 
140  for i in range(len(current_positions)):
141  distance = abs(positions[i] - current_positions[i])
142  durations.append(distance/self.joint_velocity_limits[i])
143 
144  # we cannot assume constant velocity, so multiply by a factor of 2 to compensate for acceleration/deceleration
145  duration = 2*max(durations)/_VELOCITY_SCALE
146 
147  # construct goal
148  traj_point = JointTrajectoryPoint()
149  traj_point.positions = positions
150  traj_point.velocities = [0.0]*len(positions)
151  traj_point.time_from_start = rospy.Duration(duration)
152 
153  traj = JointTrajectory()
154  traj.joint_names = self.joint_names
155  traj.points.append(traj_point)
156 
157  goal = FollowJointTrajectoryGoal()
158  goal.trajectory = traj
159 
160  # send to trajectory controller
161  self.client.send_goal(goal)
162  self.client.wait_for_result()
163 
164  # evaluate result
165  result = self.client.get_result()
166  success = (result.error_code == FollowJointTrajectoryResult.SUCCESSFUL)
167  if not success:
168  rospy.loginfo('Failure executing: ' + str(goal))
169  return success
170 
171  def _joint_limit_reaching_test(self, joint_name):
172  """ Test if the robot can be commanded to move exactly to the limits
173 
174  Test Sequence:
175  1. Command a movement to the home position.
176  2. Command a movement to the lower limit.
177  3. Command a movement to the upper limit.
178  4. Command a movement to the home position.
179 
180  Expected Results:
181  1. Trajectory is executed successfully.
182  2. Trajectory is executed successfully.
183  3. Trajectory is executed successfully.
184  4. Trajectory is executed successfully.
185  """
186  self._drive_home()
187 
188  index = self.joint_names.index(joint_name)
189  limit = _JOINT_LIMITS_DEGREE[joint_name]
190 
191  lower_positions = [0] * len(self.joint_names)
192  lower_positions[index] = -radians(limit)
193 
194  self.assertTrue(self._execute_trajectory(lower_positions))
195 
196  upper_positions = [0] * len(self.joint_names)
197  upper_positions[index] = radians(limit)
198 
199  self.assertTrue(self._execute_trajectory(upper_positions))
200 
201  self._drive_home()
202 
203  def _joint_limit_overstepping_test(self, joint_name):
204  """ Test if the robot does not overstep the limits
205 
206  Test Sequence:
207  1. Command a movement to the home position.
208  2. Command a movement overstepping the lower limit.
209  3. Command a movement overstepping the upper limit.
210  4. Command a movement to the home position.
211 
212  Expected Results:
213  1. Trajectory is executed successfully.
214  2. Trajectory execution is aborted and the robot does not overstep the limits.
215  3. Trajectory execution is aborted and the robot does not overstep the limits.
216  4. Trajectory is executed successfully.
217  """
218  self._drive_home()
219 
220  index = self.joint_names.index(joint_name)
221  limit = _JOINT_LIMITS_DEGREE[joint_name]
222 
223  lower_positions = [0] * len(self.joint_names)
224  lower_positions[index] = -(radians(limit) + _JOINT_LIMIT_OVERSTEP)
225 
226  self._execute_trajectory(lower_positions) # Controller does not abort trajectory
227  self._check_joint_limits()
228 
229  upper_positions = [0] * len(self.joint_names)
230  upper_positions[index] = radians(limit) + _JOINT_LIMIT_OVERSTEP
231 
232  self._execute_trajectory(upper_positions) # Controller does not abort trajectory
233  self._check_joint_limits()
234 
235  self._drive_home()
236 
238  """ Perform all reaching tests. Before each test ask the user if he wants to skip it.
239  """
240  for name in self.joint_names:
241  if name not in _COLLISION_JOINT_NAMES:
242  if self._ask_for_permission('joint_limit_reaching_test for ' + name):
243  self._joint_limit_reaching_test(name)
244 
246  """ Perform all overstepping tests. Before each test ask the user if he wants to skip it.
247  """
248  for name in self.joint_names:
249  if name not in _COLLISION_JOINT_NAMES:
250  if self._ask_for_permission('joint_limit_overstepping_test for ' + name):
252 
253 
254 if __name__ == "__main__":
255  # init ros node
256  rospy.init_node('acceptance_test_joint_limits_node')
257  unittest.main()
acceptance_test_joint_limits.AcceptanceTestJointLimits._joint_limit_reaching_test
def _joint_limit_reaching_test(self, joint_name)
Definition: acceptance_test_joint_limits.py:171
acceptance_test_joint_limits.AcceptanceTestJointLimits
Definition: acceptance_test_joint_limits.py:54
acceptance_test_joint_limits.AcceptanceTestJointLimits._joint_limit_overstepping_test
def _joint_limit_overstepping_test(self, joint_name)
Definition: acceptance_test_joint_limits.py:203
acceptance_test_joint_limits.AcceptanceTestJointLimits._execute_trajectory
def _execute_trajectory(self, positions)
Definition: acceptance_test_joint_limits.py:130
acceptance_test_joint_limits.AcceptanceTestJointLimits.setUp
def setUp(self)
Definition: acceptance_test_joint_limits.py:58
acceptance_test_joint_limits.AcceptanceTestJointLimits._drive_home
def _drive_home(self)
Definition: acceptance_test_joint_limits.py:119
acceptance_test_joint_limits.AcceptanceTestJointLimits._ask_for_permission
def _ask_for_permission(self, test_name)
Definition: acceptance_test_joint_limits.py:79
actionlib::SimpleActionClient
acceptance_test_joint_limits.AcceptanceTestJointLimits._current_joint_state_positions
def _current_joint_state_positions(self)
Definition: acceptance_test_joint_limits.py:87
acceptance_test_joint_limits.AcceptanceTestJointLimits._check_joint_limits
def _check_joint_limits(self)
Definition: acceptance_test_joint_limits.py:106
acceptance_test_joint_limits.AcceptanceTestJointLimits.joint_names
joint_names
Definition: acceptance_test_joint_limits.py:65
urdf_parser_py::urdf
acceptance_test_joint_limits.AcceptanceTestJointLimits.test_joint_limits_overstepping
def test_joint_limits_overstepping(self)
Definition: acceptance_test_joint_limits.py:245
acceptance_test_joint_limits.AcceptanceTestJointLimits.joint_velocity_limits
joint_velocity_limits
Definition: acceptance_test_joint_limits.py:69
acceptance_test_joint_limits.AcceptanceTestJointLimits.home_positions
home_positions
Definition: acceptance_test_joint_limits.py:66
acceptance_test_joint_limits.AcceptanceTestJointLimits.test_joint_limits_reaching
def test_joint_limits_reaching(self)
Definition: acceptance_test_joint_limits.py:237
acceptance_test_joint_limits.AcceptanceTestJointLimits.client
client
Definition: acceptance_test_joint_limits.py:61


prbt_support
Author(s):
autogenerated on Sat Nov 25 2023 03:51:49