$search
00001 #!/usr/bin/python 00002 # Software License Agreement (BSD License) 00003 # 00004 # Copyright (c) 2009, Willow Garage, Inc. 00005 # All rights reserved. 00006 # 00007 # Redistribution and use in source and binary forms, with or without 00008 # modification, are permitted provided that the following conditions 00009 # are met: 00010 # 00011 # * Redistributions of source code must retain the above copyright 00012 # notice, this list of conditions and the following disclaimer. 00013 # * Redistributions in binary form must reproduce the above 00014 # copyright notice, this list of conditions and the following 00015 # disclaimer in the documentation and/or other materials provided 00016 # with the distribution. 00017 # * Neither the name of the Willow Garage nor the names of its 00018 # contributors may be used to endorse or promote products derived 00019 # from this software without specific prior written permission. 00020 # 00021 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00022 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00023 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00024 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00025 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00026 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00027 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00028 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00029 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00030 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00031 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00032 # POSSIBILITY OF SUCH DAMAGE. 00033 # 00034 # author: Kaijen Hsiao 00035 00036 ## @package interpolated_ik_motion_planner 00037 # Server to provide interpolated IK motion plans 00038 # relevant params: 00039 # num_steps: the number of steps to use when interpolating including start and finish (0 means use pos_spacing and 00040 # rot_spacing to calculate the number of steps, anything ==1 or <0 will become 2, start and finish) 00041 # consistent_angle: the max angle distance (in any joint) before we declare that the path is inconsistent 00042 # collision_check_resolution: how many steps between collision checks (0 or 1 is check every step; 2 is every other, etc.) 00043 # steps_before_abort: the number of steps in the plan (starting from the end and going backwards) that can be invalid 00044 # due to collisions or inconsistency before aborting (0 is abort as soon as you find one, 1 is allow one and still 00045 # continue, -1 or >=num_steps to never abort early) 00046 # pos_spacing: the max translation (m) to move the wrist between waypoints (only used if num_steps is 0) 00047 # rot_spacing: the max rotation (rad) to move the wrist between waypoints (only used if num_steps is 0) 00048 # collision_aware: if this is 0, collisions won't be checked for (returns non-collision aware IK solutions) 00049 # start_from_end: if this is 1, the planner searches for an IK solution for the end first, then works backwards from there 00050 # max_joint_vels: a list of maximum joint velocities to use when computing times and velocities for the joint trajectory (defaults to [.2]*7 if left empty) 00051 # max_joint_accs: a list of maximum accelerations to use when computing times and velocities for the joint trajectory (defaults to [.5]*7 if left empty) 00052 00053 # Parameters can be changed by calling the r_interpolated_ik_motion_plan_set_params service (or l_inter...) 00054 00055 00056 # The main service message is GetMotionPlan.srv, in arm_navigation_msgs. Parts that were hijacked 00057 # for the relevant inputs and outputs: 00058 00059 # inputs: 00060 # req.motion_plan_request.start_state.joint_state.name: the names of the arm joints in order (defaults to those provided by IKQuery, if not specified) 00061 # req.motion_plan_request.start_state.joint_state.position: a set of joint angles to stay sort of close to if possible (defaults to current joint angles if not specified) 00062 00063 # req.motion_plan_request.start_state.multi_dof_joint_state.pose: the start pose for the r_wrist_roll_link 00064 # req.motion_plan_request.start_state.multi_dof_joint_state.child_frame_id: the link to put at that pose (better be 'r_wrist_roll_link' or 'l_wrist_roll_link') 00065 # req.motion_plan_request.start_state.multi_dof_joint_state.frame_id: the frame for the start pose 00066 00067 # req.motion_plan_request.goal_constraints.position_constraints[0].position: the (x,y,z) goal position of the same link as the start pose 00068 # req.motion_plan_request.goal_constraints.position_constraints[0].header.frame_id: the frame that goal position is in 00069 # req.motion_plan_request.goal_constraints.orientation_constraints[0].orientation: the (x,y,z,w) goal orientation of the same link as the start pose 00070 # req.motion_plan_request.goal_constraints.orientation_constraints[0].header.frame_id: the frame that goal orientation is in 00071 00072 # req.motion_plan_request.workspace_parameters.ordered_collision_operations: the list of collision operations to modify the collision space 00073 # req.motion_plan_request.link_padding: links and collision-space padding to change from the default dynamically 00074 00075 # outputs: 00076 # res.trajectory.joint_trajectory.joint_names: the arm joint names in the normal order, as spit out by IKQuery 00077 # res.trajectory.joint_trajectory.points: a list of JointTrajectoryPoints whose 'positions' fields are lists of joint angles in a trajectory that gets you from start to goal (positions will be all 0s if a point on the path is out of reach), 00078 # 'velocities' fields are reasonable joint velocities, and 'time_from_start' fields are reasonable times to get to each waypoint. ('accelerations' will be empty.) 00079 # res.trajectory_error_codes: a list of ArmNavigationErrorCodes messages the same length as the trajectory, 00080 # with values for each trajectory point as follows: 00081 # ArmNavigationErrorCodes.SUCCESS (1): no problem 00082 # ArmNavigationErrorCodes.COLLISION_CONSTRAINTS_VIOLATED (-23): no non-colliding IK solution (colliding solution provided) 00083 # ArmNavigationErrorCodes.PATH_CONSTRAINTS_VIOLATED (-20): inconsistency in path between this point and the next point 00084 # ArmNavigationErrorCodes.JOINT_LIMITS_VIOLATED (-21): out of reach (no colliding solution) 00085 # ArmNavigationErrorCodes.PLANNING_FAILED (-1): aborted before getting to this point 00086 00087 # Everything else is completely ignored. 00088 00089 00090 import roslib; roslib.load_manifest('interpolated_ik_motion_planner') 00091 import rospy 00092 import interpolated_ik_motion_planner.ik_utilities as ik_utilities 00093 import math 00094 import sys 00095 import pdb 00096 from arm_navigation_msgs.srv import GetMotionPlan, GetMotionPlanResponse 00097 from arm_navigation_msgs.msg import ArmNavigationErrorCodes, RobotState 00098 from geometry_msgs.msg import PoseStamped, PointStamped, QuaternionStamped, Pose, Point, Quaternion 00099 from trajectory_msgs.msg import JointTrajectoryPoint 00100 from interpolated_ik_motion_planner.srv import SetInterpolatedIKMotionPlanParams, SetInterpolatedIKMotionPlanParamsResponse 00101 from sensor_msgs.msg import JointState 00102 00103 # class to provide the interpolated ik motion planner service 00104 class InterpolatedIKService: 00105 00106 def __init__(self, which_arm): #which_arm is 'r' or 'l' 00107 self.which_arm = which_arm 00108 self.node_name = which_arm+'_interpolated_ik_motion_planner_server' 00109 00110 rospy.init_node(self.node_name) 00111 00112 #get parameters from the parameter server 00113 00114 #the number of steps to use when interpolating (including start and finish) 00115 self.num_steps = rospy.get_param(self.node_name+'/num_steps', 6) 00116 00117 #the max angle distance (in any joint) before we declare that the path is inconsistent 00118 self.consistent_angle = rospy.get_param(self.node_name+'/consistent_angle', math.pi/9) 00119 00120 #how many steps between collision checks (0 or 1 is check every step; 2 is every other, etc.) 00121 self.collision_check_resolution = rospy.get_param(self.node_name+'/collision_check_resolution', 1) 00122 00123 #how many steps in the plan can be invalid before aborting 00124 self.steps_before_abort = rospy.get_param(self.node_name+'/steps_before_abort', 0) 00125 00126 #the max translation (m) to move the wrist between waypoints (only used if num_steps is 0) 00127 self.pos_spacing = rospy.get_param(self.node_name+'/pos_spacing', 0.01) 00128 00129 #the max rotation (rad) to move the wrist between waypoints (only used if num_steps is 0) 00130 self.rot_spacing = rospy.get_param(self.node_name+'/rot_spacing', .1) 00131 00132 #whether to check for collisions or not 00133 self.collision_aware = rospy.get_param(self.node_name+'/collision_aware', 1) 00134 00135 #if 1, starts the IK calculation from the end and works backwards (if 0, starts from the start) 00136 self.start_from_end = rospy.get_param(self.node_name+'/start_from_end', 0) 00137 00138 #max joint velocities to use when calculating times and vels for the trajectory 00139 self.max_joint_vels = rospy.get_param(self.node_name+'/max_joint_vels', [.1]*7) 00140 00141 #max joint accelerations to use when calculating times and vels for the trajectory 00142 self.max_joint_accs = rospy.get_param(self.node_name+'/max_joint_vels', [.25]*7) 00143 00144 #initialize an IKUtilities class object 00145 if which_arm == 'r': 00146 self.ik_utils = ik_utilities.IKUtilities('right') 00147 else: 00148 self.ik_utils = ik_utilities.IKUtilities('left') 00149 00150 #advertise interpolated IK service 00151 s1 = rospy.Service(which_arm+'_interpolated_ik_motion_plan', \ 00152 GetMotionPlan, self.interpolated_ik_motion_planner_callback) 00153 00154 #advertise param changing service 00155 s2 = rospy.Service(which_arm+'_interpolated_ik_motion_plan_set_params', \ 00156 SetInterpolatedIKMotionPlanParams, self.set_params_callback) 00157 00158 00159 ##add a header to a message with a 0 timestamp (good for getting the latest TF transform) 00160 def add_header(self, msg, frame): 00161 msg.header.frame_id = frame 00162 msg.header.stamp = rospy.Time() 00163 return msg 00164 00165 00166 ##pretty-print list to string 00167 def pplist(self, list): 00168 return ' '.join(['%2.3f'%x for x in list]) 00169 00170 00171 ##callback for the set_params service 00172 def set_params_callback(self, req): 00173 self.num_steps = req.num_steps 00174 self.consistent_angle = req.consistent_angle 00175 self.collision_check_resolution= req.collision_check_resolution 00176 self.steps_before_abort = req.steps_before_abort 00177 self.pos_spacing = req.pos_spacing 00178 self.rot_spacing = req.rot_spacing 00179 self.collision_aware = req.collision_aware 00180 self.start_from_end = req.start_from_end 00181 self.max_joint_vels = req.max_joint_vels 00182 self.max_joint_accs = req.max_joint_accs 00183 return SetInterpolatedIKMotionPlanParamsResponse() 00184 00185 00186 ##callback for get_interpolated_ik_motion_plan service 00187 def interpolated_ik_motion_planner_callback(self, req): 00188 00189 #names and angles for the joints in their desired order 00190 joint_names = req.motion_plan_request.start_state.joint_state.name 00191 start_angles = req.motion_plan_request.start_state.joint_state.position 00192 00193 #sanity-checking: joint_names and start_angles should be the same length, if any start_angles are specified 00194 if start_angles and len(joint_names) != len(start_angles): 00195 rospy.logerr("start_state.joint_state.name needs to be the same length as start_state.joint_state.position! Quitting") 00196 return 0 00197 00198 #reorder the start angles to the order needed by IK 00199 reordered_start_angles = [] 00200 00201 #get the current joint states for the robot 00202 #joint_states_msg = rospy.wait_for_message('joint_states', JointState, 10.0) 00203 #if not joint_states_msg: 00204 # rospy.logerr("unable to get joint_states message") 00205 # return 0 00206 00207 #get the desired start angles for each IK arm joint in turn from start_state.joint_state.position 00208 #(use the current angle if not specified) 00209 for joint_name in self.ik_utils.joint_names: 00210 00211 #desired start angle specified 00212 if joint_name in joint_names and start_angles: 00213 index = joint_names.index(joint_name) 00214 reordered_start_angles.append(start_angles[index]) 00215 else: 00216 rospy.logerr("missing joint angle, can't deal") 00217 return 0 00218 00219 #desired start angle not specified, use the current angle 00220 #elif 0: #joint_name in joint_states_msg.name: 00221 # index = joint_states_msg.name.index(joint_name) 00222 # current_position = joint_states_msg.position[index] 00223 # reordered_start_angles.append(current_position) 00224 00225 #malformed joint_states message? 00226 # else: 00227 # rospy.logerr("an expected arm joint,"+joint_name+"was not found!") 00228 # return 0 00229 00230 #get additional desired joint angles (such as for the gripper) to pass through to IK 00231 additional_joint_angles = [] 00232 additional_joint_names = [] 00233 for (ind, joint_name) in enumerate(joint_names): 00234 if joint_name not in self.ik_utils.joint_names: 00235 #rospy.loginfo("found %s"%joint_name) 00236 additional_joint_angles.append(start_angles[ind]) 00237 additional_joint_names.append(joint_name) 00238 IK_robot_state = None 00239 if additional_joint_angles: 00240 #rospy.loginfo("adding additional start angles for:"+str(additional_joint_names)) 00241 #rospy.loginfo("additional joint angles:"+str(additional_joint_angles)) 00242 IK_robot_state = RobotState() 00243 IK_robot_state.joint_state.name = additional_joint_names 00244 IK_robot_state.joint_state.position = additional_joint_angles 00245 00246 #check that the desired link is in the list of possible IK links (only r/l_wrist_roll_link for now) 00247 link_name = req.motion_plan_request.start_state.multi_dof_joint_state.child_frame_ids[0] 00248 if link_name != self.ik_utils.link_name: 00249 rospy.logerr("link_name not allowed: %s"%link_name) 00250 return 0 00251 00252 #the start pose for that link 00253 start_pose = req.motion_plan_request.start_state.multi_dof_joint_state.poses[0] 00254 00255 #the frame that start pose is in 00256 frame_id = req.motion_plan_request.start_state.multi_dof_joint_state.frame_ids[0] 00257 00258 #turn it into a PoseStamped 00259 start_pose_stamped = self.add_header(PoseStamped(), frame_id) 00260 start_pose_stamped.pose = start_pose 00261 00262 #the desired goal position 00263 goal_pos = req.motion_plan_request.goal_constraints.position_constraints[0].position 00264 00265 #the frame that goal position is in 00266 goal_pos_frame = req.motion_plan_request.goal_constraints.position_constraints[0].header.frame_id 00267 00268 #convert the position to base_link frame 00269 goal_ps = self.add_header(PointStamped(), goal_pos_frame) 00270 goal_ps.point = goal_pos 00271 goal_pos_list = self.ik_utils.point_stamped_to_list(goal_ps, 'base_link') 00272 00273 #the desired goal orientation 00274 goal_quat = req.motion_plan_request.goal_constraints.orientation_constraints[0].orientation 00275 00276 #the frame that goal orientation is in 00277 goal_quat_frame = req.motion_plan_request.goal_constraints.orientation_constraints[0].header.frame_id 00278 00279 #convert the quaternion to base_link frame 00280 goal_qs = self.add_header(QuaternionStamped(), goal_quat_frame) 00281 goal_qs.quaternion = goal_quat 00282 goal_quat_list = self.ik_utils.quaternion_stamped_to_list(goal_qs, 'base_link') 00283 00284 #assemble the goal pose into a PoseStamped 00285 goal_pose_stamped = self.add_header(PoseStamped(), 'base_link') 00286 goal_pose_stamped.pose = Pose(Point(*goal_pos_list), Quaternion(*goal_quat_list)) 00287 00288 #get the ordered collision operations, if there are any 00289 ordered_collision_operations = None #req.motion_plan_request.ordered_collision_operations 00290 #if ordered_collision_operations.collision_operations == []: 00291 # ordered_collision_operations = None 00292 00293 #get the link paddings, if there are any 00294 link_padding = None #req.motion_plan_request.link_padding 00295 #if link_padding == []: 00296 # link_padding = None 00297 00298 #RUN! Check the Cartesian path for consistent, non-colliding IK solutions 00299 (trajectory, error_codes) = self.ik_utils.check_cartesian_path(start_pose_stamped, \ 00300 goal_pose_stamped, reordered_start_angles, self.pos_spacing, self.rot_spacing, \ 00301 self.consistent_angle, self.collision_aware, self.collision_check_resolution, \ 00302 self.steps_before_abort, self.num_steps, ordered_collision_operations, \ 00303 self.start_from_end, IK_robot_state, link_padding) 00304 00305 #find appropriate velocities and times for the valid part of the resulting joint path (invalid parts set to 0) 00306 #if we're searching from the end, keep the end; if we're searching from the start, keep the start 00307 start_ind = 0 00308 stop_ind = len(error_codes) 00309 if self.start_from_end: 00310 for ind in range(len(error_codes)-1, 0, -1): 00311 if error_codes[ind]: 00312 start_ind = ind+1 00313 break 00314 else: 00315 for ind in range(len(error_codes)): 00316 if error_codes[ind]: 00317 stop_ind = ind 00318 break 00319 (times, vels) = self.ik_utils.trajectory_times_and_vels(trajectory[start_ind:stop_ind], self.max_joint_vels, self.max_joint_accs) 00320 times = [0]*start_ind + times + [0]*(len(error_codes)-stop_ind) 00321 vels = [[0]*7]*start_ind + vels + [[0]*7]*(len(error_codes)-stop_ind) 00322 00323 rospy.logdebug("trajectory:") 00324 for ind in range(len(trajectory)): 00325 rospy.logdebug("error code "+ str(error_codes[ind]) + " pos : " + self.pplist(trajectory[ind])) 00326 rospy.logdebug("") 00327 for ind in range(len(trajectory)): 00328 rospy.logdebug("time: " + "%5.3f "%times[ind] + "vels: " + self.pplist(vels[ind])) 00329 00330 #the response 00331 res = GetMotionPlanResponse() 00332 00333 #the arm joint names in the normal order, as spit out by IKQuery 00334 res.trajectory.joint_trajectory.joint_names = self.ik_utils.joint_names[:] 00335 00336 #a list of 7-lists of joint angles, velocities, and times for a trajectory that gets you from start to goal 00337 #(all 0s if there was no IK solution for a point on the path) 00338 res.trajectory.joint_trajectory.points = [] 00339 for i in range(len(trajectory)): 00340 joint_trajectory_point = JointTrajectoryPoint() 00341 joint_trajectory_point.positions = trajectory[i] 00342 joint_trajectory_point.velocities = vels[i] 00343 joint_trajectory_point.time_from_start = rospy.Duration(times[i]) 00344 res.trajectory.joint_trajectory.points.append(joint_trajectory_point) 00345 00346 #a list of ArmNavigationErrorCodes messages, one for each trajectory point, with values as follows: 00347 #ArmNavigationErrorCodes.SUCCESS (1): no problem 00348 #ArmNavigationErrorCodes.COLLISION_CONSTRAINTS_VIOLATED (-23): no non-colliding IK solution (colliding solution provided) 00349 #ArmNavigationErrorCodes.PATH_CONSTRAINTS_VIOLATED (-20): inconsistency in path between this point and the next point 00350 #ArmNavigationErrorCodes.JOINT_LIMITS_VIOLATED (-21): out of reach (no colliding solution) 00351 #ArmNavigationErrorCodes.PLANNING_FAILED (0): aborted before getting to this point 00352 error_code_dict = {0:ArmNavigationErrorCodes.SUCCESS, 1:ArmNavigationErrorCodes.COLLISION_CONSTRAINTS_VIOLATED, \ 00353 2:ArmNavigationErrorCodes.PATH_CONSTRAINTS_VIOLATED, 3:ArmNavigationErrorCodes.JOINT_LIMITS_VIOLATED, \ 00354 4:ArmNavigationErrorCodes.PLANNING_FAILED} 00355 00356 trajectory_error_codes = [ArmNavigationErrorCodes(val=error_code_dict[error_code]) for error_code in error_codes] 00357 res.trajectory_error_codes = trajectory_error_codes 00358 res.error_code.val = ArmNavigationErrorCodes.SUCCESS 00359 # rospy.loginfo("trajectory:") 00360 # for ind in range(len(trajectory)): 00361 # rospy.loginfo("error code "+ str(error_codes[ind]) + " pos : " + self.pplist(trajectory[ind])) 00362 # rospy.loginfo("") 00363 # for ind in range(len(trajectory)): 00364 # rospy.loginfo("time: " + "%5.3f "%times[ind] + "vels: " + self.pplist(vels[ind])) 00365 00366 return res 00367 00368 00369 if __name__ == "__main__": 00370 00371 if len(sys.argv) < 2 or sys.argv[1] != 'r' and sys.argv[1] != 'l': 00372 rospy.logerr("usage: interpolated_ik_motion_planner.py which_arm (which_arm is r or l)") 00373 sys.exit(1) 00374 00375 which_arm = sys.argv[1] 00376 interpolated_ik_service = InterpolatedIKService(which_arm) 00377 rospy.loginfo("Ready to serve interpolated IK motion plan requests.") 00378 00379 rospy.spin() 00380