pick_and_place.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 """
00004     pick_and_place.py - Version 0.1 2014-08-01
00005     
00006     Command the gripper to grasp a target object and move it to a new
00007     location, all while avoiding simulated obstacles.
00008     
00009     Created for the Pi Robot Project: http://www.pirobot.org
00010     Copyright (c) 2014 Patrick Goebel. All rights reserved.
00011     
00012     Adapted to the Turtlebot arm by Jorge Santos
00013 
00014     This program is free software; you can redistribute it and/or modify
00015     it under the terms of the GNU General Public License as published by
00016     the Free Software Foundation; either version 2 of the License, or
00017     (at your option) any later version.5
00018     
00019     This program is distributed in the hope that it will be useful,
00020     but WITHOUT ANY WARRANTY; without even the implied warranty of
00021     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00022     GNU General Public License for more details at:
00023     
00024     http://www.gnu.org/licenses/gpl.html
00025 """
00026 
00027 import rospy, sys
00028 import moveit_commander
00029 from geometry_msgs.msg import PoseStamped
00030 from moveit_commander import MoveGroupCommander, PlanningSceneInterface
00031 from moveit_msgs.msg import PlanningScene, ObjectColor
00032 from moveit_msgs.msg import Grasp, GripperTranslation
00033 from moveit_msgs.msg import MoveItErrorCodes
00034 from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint
00035 from tf.transformations import quaternion_from_euler
00036 from copy import deepcopy
00037 
00038 GROUP_NAME_ARM = 'arm'
00039 GROUP_NAME_GRIPPER = 'gripper'
00040 
00041 GRIPPER_FRAME = 'gripper_link'
00042 
00043 GRIPPER_OPENED = [0.053]
00044 GRIPPER_CLOSED = [0.001]
00045 GRIPPER_NEUTRAL = [0.028]
00046 GRASP_OVERTIGHTEN = 0.002
00047 
00048 GRIPPER_JOINT_NAMES = ['gripper_joint']
00049 
00050 GRIPPER_EFFORT = [1.0]
00051 
00052 REFERENCE_FRAME = '/base_link'
00053 
00054 class MoveItDemo:
00055     def __init__(self):
00056         # Initialize the move_group API
00057         moveit_commander.roscpp_initialize(sys.argv)
00058 
00059         rospy.init_node('moveit_demo')
00060 
00061         # Use the planning scene object to add or remove objects
00062         scene = PlanningSceneInterface()
00063 
00064         # Create a scene publisher to push changes to the scene
00065         self.scene_pub = rospy.Publisher('planning_scene', PlanningScene)
00066 
00067         # Create a publisher for displaying gripper poses
00068         self.gripper_pose_pub = rospy.Publisher('gripper_pose', PoseStamped)
00069 
00070         # Create a dictionary to hold object colors
00071         self.colors = dict()
00072 
00073         # Initialize the move group for the right arm
00074         arm = MoveGroupCommander(GROUP_NAME_ARM)
00075 
00076         # Initialize the move group for the right gripper
00077         gripper = MoveGroupCommander(GROUP_NAME_GRIPPER)
00078 
00079         # Get the name of the end-effector link
00080         end_effector_link = arm.get_end_effector_link()
00081 
00082         # Allow some leeway in position (meters) and orientation (radians)
00083         arm.set_goal_position_tolerance(0.04)
00084         arm.set_goal_orientation_tolerance(0.1)
00085 
00086         # Allow replanning to increase the odds of a solution
00087         arm.allow_replanning(True)
00088 
00089         # Set the right arm reference frame
00090         arm.set_pose_reference_frame(REFERENCE_FRAME)
00091 
00092         # Allow 5 seconds per planning attempt
00093         arm.set_planning_time(5)
00094 
00095         # Set a limit on the number of pick attempts before bailing
00096         max_pick_attempts = 5
00097 
00098         # Set a limit on the number of place attempts
00099         max_place_attempts = 5
00100 
00101         # Give the scene a chance to catch up
00102         rospy.sleep(2)
00103 
00104         # Give each of the scene objects a unique name
00105         table_id = 'table'
00106         box1_id = 'box1'
00107         box2_id = 'box2'
00108         target_id = 'target'
00109         tool_id = 'tool'
00110 
00111         # Remove leftover objects from a previous run
00112         scene.remove_world_object(table_id)
00113         scene.remove_world_object(box1_id)
00114         scene.remove_world_object(box2_id)
00115         scene.remove_world_object(target_id)
00116         scene.remove_world_object(tool_id)
00117 
00118         # Remove any attached objects from a previous session
00119         scene.remove_attached_object(GRIPPER_FRAME, target_id)
00120 
00121         # Give the scene a chance to catch up
00122         rospy.sleep(1)
00123 
00124         # Start the arm in the "arm_up" pose stored in the SRDF file
00125         arm.set_named_target('right_up')
00126         arm.go()
00127 
00128         # Open the gripper to the neutral position
00129         gripper.set_joint_value_target(GRIPPER_NEUTRAL)
00130         gripper.go()
00131 
00132         rospy.sleep(1)
00133 
00134         # Set the height of the table off the ground
00135         table_ground = 0.4
00136 
00137         # Set the dimensions of the scene objects [l, w, h]
00138         table_size = [0.2, 0.7, 0.01]
00139         box1_size = [0.1, 0.05, 0.05]
00140         box2_size = [0.05, 0.05, 0.15]
00141 
00142         # Set the target size [l, w, h]
00143         target_size = [0.02, 0.01, 0.12]
00144 
00145         # Add a table top and two boxes to the scene
00146         table_pose = PoseStamped()
00147         table_pose.header.frame_id = REFERENCE_FRAME
00148         table_pose.pose.position.x = 0.4
00149         table_pose.pose.position.y = 0.0
00150         table_pose.pose.position.z = table_ground + table_size[2] / 2.0
00151         table_pose.pose.orientation.w = 1.0
00152         scene.add_box(table_id, table_pose, table_size)
00153 
00154         box1_pose = PoseStamped()
00155         box1_pose.header.frame_id = REFERENCE_FRAME
00156         box1_pose.pose.position.x = table_pose.pose.position.x - 0.04
00157         box1_pose.pose.position.y = -0.15
00158         box1_pose.pose.position.z = table_ground + table_size[2] + box1_size[2] / 2.0
00159         box1_pose.pose.orientation.w = 1.0
00160         scene.add_box(box1_id, box1_pose, box1_size)
00161 
00162         box2_pose = PoseStamped()
00163         box2_pose.header.frame_id = REFERENCE_FRAME
00164         box2_pose.pose.position.x = table_pose.pose.position.x - 0.06
00165         box2_pose.pose.position.y = 0.13
00166         box2_pose.pose.position.z = table_ground + table_size[2] + box2_size[2] / 2.0
00167         box2_pose.pose.orientation.w = 1.0
00168         scene.add_box(box2_id, box2_pose, box2_size)
00169 
00170         # Set the target pose in between the boxes and on the table
00171         target_pose = PoseStamped()
00172         target_pose.header.frame_id = REFERENCE_FRAME
00173         target_pose.pose.position.x = table_pose.pose.position.x - 0.03
00174         target_pose.pose.position.y = 0.02
00175         target_pose.pose.position.z = table_ground + table_size[2] + target_size[2] / 2.0
00176         target_pose.pose.orientation.w = 1.0
00177 
00178         # Add the target object to the scene
00179         scene.add_box(target_id, target_pose, target_size)
00180 
00181         # Make the table red and the boxes orange
00182         self.setColor(table_id, 0.8, 0, 0, 1.0)
00183         self.setColor(box1_id, 0.8, 0.4, 0, 1.0)
00184         self.setColor(box2_id, 0.8, 0.4, 0, 1.0)
00185 
00186         # Make the target yellow
00187         self.setColor(target_id, 0.9, 0.9, 0, 1.0)
00188 
00189         # Send the colors to the planning scene
00190         self.sendColors()
00191 
00192         # Set the support surface name to the table object
00193         arm.set_support_surface_name(table_id)
00194 
00195         # Specify a pose to place the target after being picked up
00196         place_pose = PoseStamped()
00197         place_pose.header.frame_id = REFERENCE_FRAME
00198         place_pose.pose.position.x = table_pose.pose.position.x - 0.07
00199         place_pose.pose.position.y = -0.06
00200         place_pose.pose.position.z = table_ground + table_size[2] + target_size[2] / 2.0
00201         place_pose.pose.orientation.w = 1.0
00202 
00203         # Initialize the grasp pose to the target pose
00204         grasp_pose = target_pose
00205 
00206         # Shift the grasp pose by half the width of the target to center it
00207         grasp_pose.pose.position.y -= target_size[1] / 2.0
00208 
00209         # Generate a list of grasps
00210         grasps = self.make_grasps(grasp_pose, [target_id], [target_size[1] - GRASP_OVERTIGHTEN])
00211 
00212         # Publish the grasp poses so they can be viewed in RViz
00213         for grasp in grasps:
00214             self.gripper_pose_pub.publish(grasp.grasp_pose)
00215             rospy.sleep(0.2)
00216 
00217         # Track success/failure and number of attempts for pick operation
00218         result = MoveItErrorCodes.FAILURE
00219         n_attempts = 0
00220 
00221         # Repeat until we succeed or run out of attempts
00222         while result != MoveItErrorCodes.SUCCESS and n_attempts < max_pick_attempts:
00223             result = arm.pick(target_id, grasps)
00224             n_attempts += 1
00225             rospy.loginfo("Pick attempt: " + str(n_attempts))
00226             rospy.sleep(0.2)
00227 
00228         # If the pick was successful, attempt the place operation
00229         if result == MoveItErrorCodes.SUCCESS:
00230             success = False
00231             n_attempts = 0
00232 
00233             # Generate valid place poses
00234             places = self.make_places(place_pose)
00235 
00236             # Repeat until we succeed or run out of attempts
00237             while not success and n_attempts < max_place_attempts:
00238                 for place in places:
00239                     # Publish the place poses so they can be viewed in RViz
00240                     self.gripper_pose_pub.publish(place)
00241                     rospy.sleep(0.2)
00242 
00243                     success = arm.place(target_id, place)
00244                     if success:
00245                         break
00246                 n_attempts += 1
00247                 rospy.loginfo("Place attempt: " + str(n_attempts))
00248                 rospy.sleep(0.2)
00249 
00250             if not success:
00251                 rospy.loginfo("Place operation failed after " + str(n_attempts) + " attempts.")
00252         else:
00253             rospy.loginfo("Pick operation failed after " + str(n_attempts) + " attempts.")
00254 
00255         # Return the arm to the "resting" pose stored in the SRDF file (passing through right_up)
00256         arm.set_named_target('right_up')
00257         arm.go()
00258         arm.set_named_target('resting')
00259         arm.go()
00260 
00261         # Open the gripper to the neutral position
00262         gripper.set_joint_value_target(GRIPPER_NEUTRAL)
00263         gripper.go()
00264 
00265         rospy.sleep(1)
00266 
00267         # Shut down MoveIt cleanly
00268         moveit_commander.roscpp_shutdown()
00269 
00270         # Exit the script
00271         moveit_commander.os._exit(0)
00272 
00273     # Get the gripper posture as a JointTrajectory
00274     def make_gripper_posture(self, joint_positions):
00275         # Initialize the joint trajectory for the gripper joints
00276         t = JointTrajectory()
00277 
00278         # Set the joint names to the gripper joint names
00279         t.joint_names = GRIPPER_JOINT_NAMES
00280 
00281         # Initialize a joint trajectory point to represent the goal
00282         tp = JointTrajectoryPoint()
00283 
00284         # Assign the trajectory joint positions to the input positions
00285         tp.positions = joint_positions
00286 
00287         # Set the gripper effort
00288         tp.effort = GRIPPER_EFFORT
00289 
00290         tp.time_from_start = rospy.Duration(1.0)
00291 
00292         # Append the goal point to the trajectory points
00293         t.points.append(tp)
00294 
00295         # Return the joint trajectory
00296         return t
00297 
00298     # Generate a gripper translation in the direction given by vector
00299     def make_gripper_translation(self, min_dist, desired, vector):
00300         # Initialize the gripper translation object
00301         g = GripperTranslation()
00302 
00303         # Set the direction vector components to the input
00304         g.direction.vector.x = vector[0]
00305         g.direction.vector.y = vector[1]
00306         g.direction.vector.z = vector[2]
00307 
00308         # The vector is relative to the gripper frame
00309         g.direction.header.frame_id = GRIPPER_FRAME
00310 
00311         # Assign the min and desired distances from the input
00312         g.min_distance = min_dist
00313         g.desired_distance = desired
00314 
00315         return g
00316 
00317     # Generate a list of possible grasps
00318     def make_grasps(self, initial_pose_stamped, allowed_touch_objects, grasp_opening=[0]):
00319         # Initialize the grasp object
00320         g = Grasp()
00321 
00322         # Set the pre-grasp and grasp postures appropriately;
00323         # grasp_opening should be a bit smaller than target width
00324         g.pre_grasp_posture = self.make_gripper_posture(GRIPPER_OPENED)
00325         g.grasp_posture = self.make_gripper_posture(grasp_opening)
00326 
00327         # Set the approach and retreat parameters as desired
00328         g.pre_grasp_approach = self.make_gripper_translation(0.01, 0.1, [1.0, 0.0, 0.0])
00329         g.post_grasp_retreat = self.make_gripper_translation(0.1, 0.15, [0.0, -1.0, 1.0])
00330 
00331         # Set the first grasp pose to the input pose
00332         g.grasp_pose = initial_pose_stamped
00333 
00334         # Pitch angles to try
00335         pitch_vals = [0, 0.1, -0.1, 0.2, -0.2, 0.4, -0.4]
00336 
00337         # Yaw angles to try
00338         yaw_vals = [0]
00339 
00340         # A list to hold the grasps
00341         grasps = []
00342 
00343         # Generate a grasp for each pitch and yaw angle
00344         for y in yaw_vals:
00345             for p in pitch_vals:
00346                 # Create a quaternion from the Euler angles
00347                 q = quaternion_from_euler(0, p, y)
00348 
00349                 # Set the grasp pose orientation accordingly
00350                 g.grasp_pose.pose.orientation.x = q[0]
00351                 g.grasp_pose.pose.orientation.y = q[1]
00352                 g.grasp_pose.pose.orientation.z = q[2]
00353                 g.grasp_pose.pose.orientation.w = q[3]
00354 
00355                 # Set and id for this grasp (simply needs to be unique)
00356                 g.id = str(len(grasps))
00357 
00358                 # Set the allowed touch objects to the input list
00359                 g.allowed_touch_objects = allowed_touch_objects
00360 
00361                 # Don't restrict contact force
00362                 g.max_contact_force = 0
00363 
00364                 # Degrade grasp quality for increasing pitch angles
00365                 g.grasp_quality = 1.0 - abs(p)
00366 
00367                 # Append the grasp to the list
00368                 grasps.append(deepcopy(g))
00369 
00370         # Return the list
00371         return grasps
00372 
00373     # Generate a list of possible place poses
00374     def make_places(self, init_pose):
00375         # Initialize the place location as a PoseStamped message
00376         place = PoseStamped()
00377 
00378         # Start with the input place pose
00379         place = init_pose
00380 
00381         # A list of x shifts (meters) to try
00382         x_vals = [0, 0.005, 0.01, 0.015, -0.005, -0.01, -0.015]
00383 
00384         # A list of y shifts (meters) to try
00385         y_vals = [0, 0.005, 0.01, 0.015, -0.005, -0.01, -0.015]
00386 
00387         # A list of pitch angles to try
00388         # pitch_vals = [0, 0.005, -0.005, 0.01, -0.01, 0.02, -0.02]
00389 
00390         pitch_vals = [0]
00391 
00392         # A list of yaw angles to try
00393         yaw_vals = [0]
00394 
00395         # A list to hold the places
00396         places = []
00397 
00398         # Generate a place pose for each angle and translation
00399         for y in yaw_vals:
00400             for p in pitch_vals:
00401                 for y in y_vals:
00402                     for x in x_vals:
00403                         place.pose.position.x = init_pose.pose.position.x + x
00404                         place.pose.position.y = init_pose.pose.position.y + y
00405 
00406                         # Create a quaternion from the Euler angles
00407                         q = quaternion_from_euler(0, p, y)
00408 
00409                         # Set the place pose orientation accordingly
00410                         place.pose.orientation.x = q[0]
00411                         place.pose.orientation.y = q[1]
00412                         place.pose.orientation.z = q[2]
00413                         place.pose.orientation.w = q[3]
00414 
00415                         # Append this place pose to the list
00416                         places.append(deepcopy(place))
00417 
00418         # Return the list
00419         return places
00420 
00421     # Set the color of an object
00422     def setColor(self, name, r, g, b, a=0.9):
00423         # Initialize a MoveIt color object
00424         color = ObjectColor()
00425 
00426         # Set the id to the name given as an argument
00427         color.id = name
00428 
00429         # Set the rgb and alpha values given as input
00430         color.color.r = r
00431         color.color.g = g
00432         color.color.b = b
00433         color.color.a = a
00434 
00435         # Update the global color dictionary
00436         self.colors[name] = color
00437 
00438     # Actually send the colors to MoveIt!
00439     def sendColors(self):
00440         # Initialize a planning scene object
00441         p = PlanningScene()
00442 
00443         # Need to publish a planning scene diff
00444         p.is_diff = True
00445 
00446         # Append the colors from the global color dictionary
00447         for color in self.colors.values():
00448             p.object_colors.append(color)
00449 
00450         # Publish the scene diff
00451         self.scene_pub.publish(p)
00452 
00453 if __name__ == "__main__":
00454     MoveItDemo()


turtlebot_arm_moveit_demos
Author(s): Jorge Santos
autogenerated on Thu Jun 6 2019 20:54:33