singulation_executive.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # Software License Agreement (BSD License)
00003 #
00004 #  Copyright (c) 2012, Georgia Institute of Technology
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 Georgia Institute of Technology nor the names of
00018 #     its 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 import roslib; roslib.load_manifest('tabletop_pushing')
00035 import rospy
00036 import actionlib
00037 import hrl_pr2_lib.linear_move as lm
00038 import hrl_pr2_lib.pr2 as pr2
00039 import hrl_lib.tf_utils as tfu
00040 from geometry_msgs.msg import PoseStamped
00041 import tf
00042 import numpy as np
00043 from tabletop_pushing.srv import *
00044 from tabletop_pushing.msg import *
00045 from math import sin, cos, pi, fabs
00046 import sys
00047 
00048 GRIPPER_PUSH = 0
00049 GRIPPER_SWEEP = 1
00050 OVERHEAD_PUSH = 2
00051 OVERHEAD_PULL = 3
00052 
00053 class SingulationExecutive:
00054 
00055     def __init__(self, use_fake_push_pose=False):
00056         rospy.init_node('tabletop_executive_node',log_level=rospy.DEBUG)
00057         # TODO: Determine workspace limits for max here
00058         self.min_push_dist = rospy.get_param('~min_push_dist', 0.07)
00059         self.max_push_dist = rospy.get_param('~mix_push_dist', 0.3)
00060         self.use_overhead_x_thresh = rospy.get_param('~use_overhead_x_thresh',
00061                                                      0.55)
00062         self.use_sweep_angle_thresh = rospy.get_param('~use_sweep_angle_thresh',
00063                                                      pi*0.4)
00064         self.use_pull_angle_thresh = rospy.get_param('~use_sweep_angle_thresh',
00065                                                      pi*0.525)
00066         self.use_same_side_y_thresh = rospy.get_param('~use_same_side_y_thresh',
00067                                                      0.3)
00068         self.use_same_side_x_thresh = rospy.get_param('~use_same_side_x_thresh',
00069                                                       0.8)
00070 
00071         # TODO: Replace these parameters with learned / perceived values
00072         # The offsets should be removed and learned implicitly
00073         self.gripper_x_offset = rospy.get_param('~gripper_push_start_x_offset',
00074                                                 -0.05)
00075         self.gripper_y_offset = rospy.get_param('~gripper_push_start_y_offset',
00076                                                 0.0)
00077         self.gripper_start_z = rospy.get_param('~gripper_push_start_z',
00078                                                 -0.25)
00079 
00080         self.sweep_x_offset = rospy.get_param('~gripper_sweep_start_x_offset',
00081                                               -0.01)
00082         self.sweep_y_offset = rospy.get_param('~gripper_sweep_start_y_offset',
00083                                               0.03)
00084         self.sweep_start_z = rospy.get_param('~gripper_sweep_start_z',
00085                                               -0.22)
00086 
00087         self.overhead_x_offset = rospy.get_param('~overhead_push_start_x_offset',
00088                                                  0.00)
00089         self.overhead_y_offset = rospy.get_param('~overhead_push_start_y_offset',
00090                                                  0.00)
00091         self.overhead_start_z = rospy.get_param('~overhead_push_start_z',
00092                                                  -0.25)
00093         self.pull_dist_offset = rospy.get_param('~overhead_pull_dist_offset',
00094                                                 0.05)
00095         self.pull_start_z = rospy.get_param('~overhead_push_start_z',
00096                                             -0.27)
00097         # Setup service proxies
00098         self.push_pose_proxy = rospy.ServiceProxy('get_push_pose', PushVector)
00099         self.gripper_push_proxy = rospy.ServiceProxy('gripper_push',
00100                                                      GripperPush)
00101         self.gripper_pre_push_proxy = rospy.ServiceProxy('gripper_pre_push',
00102                                                          GripperPush)
00103         self.gripper_post_push_proxy = rospy.ServiceProxy('gripper_post_push',
00104                                                           GripperPush)
00105         self.gripper_pre_sweep_proxy = rospy.ServiceProxy('gripper_pre_sweep',
00106                                                           GripperPush)
00107         self.gripper_sweep_proxy = rospy.ServiceProxy('gripper_sweep',
00108                                                       GripperPush)
00109         self.gripper_post_sweep_proxy = rospy.ServiceProxy('gripper_post_sweep',
00110                                                            GripperPush)
00111         self.overhead_pre_push_proxy = rospy.ServiceProxy('overhead_pre_push',
00112                                                           GripperPush)
00113         self.overhead_push_proxy = rospy.ServiceProxy('overhead_push',
00114                                                       GripperPush)
00115         self.overhead_post_push_proxy = rospy.ServiceProxy('overhead_post_push',
00116                                                            GripperPush)
00117         self.overhead_pre_pull_proxy = rospy.ServiceProxy('overhead_pre_pull',
00118                                                           GripperPush)
00119         self.overhead_pull_proxy = rospy.ServiceProxy('overhead_pull',
00120                                                       GripperPush)
00121         self.overhead_post_pull_proxy = rospy.ServiceProxy('overhead_post_pull',
00122                                                            GripperPush)
00123         self.raise_and_look_proxy = rospy.ServiceProxy('raise_and_look',
00124                                                        RaiseAndLook)
00125         self.table_proxy = rospy.ServiceProxy('get_table_location', LocateTable)
00126         # Bookkeeping fof fake pushes
00127         self.use_fake_push_pose = use_fake_push_pose
00128         self.push_count = 0
00129 
00130     def run(self, num_pushes=1, use_guided=True):
00131         # Get table height and raise to that before anything else
00132         self.raise_and_look()
00133         # Initialize push pose
00134         self.initialize_push_pose();
00135 
00136         # NOTE: Should exit before reaching num_pushes, this is just a backup
00137         for i in xrange(num_pushes):
00138             pose_res = self.request_singulation_push(use_guided)
00139             # raw_input('Hit any key to continue')
00140             # continue
00141             if pose_res is None:
00142                 rospy.logwarn("pose_res is None. Exiting pushing");
00143                 break
00144             if pose_res.no_push:
00145                 rospy.loginfo("No push. Exiting pushing.");
00146                 break
00147             rospy.loginfo('Performing push #' + str(i+1))
00148             # Decide push based on the orientation returned
00149             rospy.loginfo('Push start_point: (' + str(pose_res.start_point.x) +
00150                           ', ' + str(pose_res.start_point.y) +
00151                           ', ' + str(pose_res.start_point.z) + ')')
00152             rospy.loginfo('Push angle: ' + str(pose_res.push_angle))
00153             rospy.loginfo('Push dist: ' + str(pose_res.push_dist))
00154 
00155             # TODO: Make this a function
00156             # Choose push behavior
00157             if fabs(pose_res.push_angle) > self.use_pull_angle_thresh:
00158                 push_opt = OVERHEAD_PULL
00159             elif pose_res.start_point.x < self.use_overhead_x_thresh:
00160                 push_opt = OVERHEAD_PUSH
00161             elif fabs(pose_res.push_angle) > self.use_sweep_angle_thresh:
00162                 push_opt = GRIPPER_SWEEP
00163             else:
00164                 push_opt = GRIPPER_PUSH
00165 
00166             # TODO: Make this a function
00167             # Choose arm
00168             if (fabs(pose_res.start_point.y) > self.use_same_side_y_thresh or
00169                 pose_res.start_point.x > self.use_same_side_x_thresh):
00170                 if (pose_res.start_point.y < 0):
00171                     which_arm = 'r'
00172                     rospy.loginfo('Setting arm to right because of limits')
00173                 else:
00174                     which_arm = 'l'
00175                     rospy.loginfo('Setting arm to left because of limits')
00176             elif pose_res.push_angle > 0:
00177                 which_arm = 'r'
00178                 rospy.loginfo('Setting arm to right because of angle')
00179             else:
00180                 which_arm = 'l'
00181                 rospy.loginfo('Setting arm to left because of angle')
00182 
00183             push_dist = pose_res.push_dist
00184             push_dist = max(min(push_dist, self.max_push_dist),
00185                             self.min_push_dist)
00186             if push_opt == GRIPPER_PUSH:
00187                 self.gripper_push_object(push_dist, which_arm, pose_res)
00188             if push_opt == GRIPPER_SWEEP:
00189                 self.sweep_object(push_dist, which_arm, pose_res)
00190             if push_opt == OVERHEAD_PUSH:
00191                 self.overhead_push_object(push_dist, which_arm, pose_res)
00192             if push_opt == OVERHEAD_PULL:
00193                 self.overhead_pull_object(push_dist, which_arm, pose_res)
00194             rospy.loginfo('Done performing push behavior.\n')
00195 
00196         if not (pose_res is None):
00197             rospy.loginfo('Singulated objects: ' + str(pose_res.singulated))
00198             rospy.loginfo('Final estimate of ' + str(pose_res.num_objects) +
00199                           ' objects')
00200 
00201     def request_singulation_push(self, use_guided=True, which_arm='l'):
00202         if (self.use_fake_push_pose):
00203             return request_fake_singulation_push(which_arm)
00204         pose_req = PushVectorRequest()
00205         pose_req.use_guided = use_guided
00206         pose_req.initialize = False
00207         pose_req.no_push_calc = False
00208         rospy.loginfo("Calling push pose service")
00209         try:
00210             pose_res = self.push_pose_proxy(pose_req)
00211             return pose_res
00212         except rospy.ServiceException, e:
00213             rospy.logwarn("Service did not process request: %s"%str(e))
00214             return None
00215 
00216     def request_fake_singulation_push(self, which_arm):
00217         pose_res = PushVectorResponse()
00218         push_opt = GRIPPER_PUSH
00219         if push_opt == GRIPPER_PUSH:
00220             pose_res = PushVectorResponse()
00221             pose_res.start_point.x = 0.5
00222             pose_res.header.frame_id = '/torso_lift_link'
00223             pose_res.header.stamp = rospy.Time(0)
00224             if which_arm == 'l':
00225                 pose_res.start_point.y = 0.3 - self.push_count*0.15
00226             else:
00227                 pose_res.start_point.y = -0.3 + self.push_count*0.15
00228             pose_res.start_point.z = -0.22
00229         elif push_opt == GRIPPER_SWEEP:
00230             pose_res.header.frame_id = '/torso_lift_link'
00231             pose_res.header.stamp = rospy.Time(0)
00232             pose_res.start_point.x = 0.75
00233             if which_arm == 'l':
00234                 pose_res.start_point.y = 0.05
00235             else:
00236                 pose_res.start_point.y = -0.15
00237             pose_res.start_point.z = -0.25
00238         elif push_opt == OVERHEAD_PUSH:
00239             pose_res.header.frame_id = '/torso_lift_link'
00240             pose_res.header.stamp = rospy.Time(0)
00241             pose_res.start_point.x = 0.7
00242             pose_res.start_point.y = 0.0
00243             pose_res.start_point.z = -0.25
00244             pose_res.push_dist = 0.15
00245             pose_res.push_angle = 0.75*pi
00246         return pose_res
00247 
00248     def initialize_push_pose(self):
00249         pose_req = PushVectorRequest()
00250         pose_req.initialize = True
00251         pose_req.use_guided = True
00252         pose_req.no_push_calc = False
00253         rospy.loginfo('Initializing push pose service.')
00254         self.push_pose_proxy(pose_req)
00255 
00256     def raise_and_look(self):
00257         table_req = LocateTableRequest()
00258         table_req.recalculate = True
00259         raise_req = RaiseAndLookRequest()
00260         raise_req.point_head_only = True
00261         raise_req.camera_frame = 'openni_rgb_frame'
00262         # First make sure the head is looking the correct way before estimating
00263         # the table centroid
00264         # Also make sure the arms are out of the way
00265         raise_req.init_arms = True
00266         rospy.loginfo("Moving head and arms")
00267         raise_res = self.raise_and_look_proxy(raise_req)
00268         try:
00269             rospy.loginfo("Getting table pose")
00270             table_res = self.table_proxy(table_req);
00271         except rospy.ServiceException, e:
00272             rospy.logwarn("Service did not process request: %s"%str(e))
00273             return
00274         if not table_res.found_table:
00275             return
00276         raise_req.table_centroid = table_res.table_centroid
00277 
00278         # TODO: Make sure this requested table_centroid is valid
00279 
00280         rospy.loginfo("Raising spine");
00281         raise_req.point_head_only = False
00282         raise_req.init_arms = False
00283         raise_res = self.raise_and_look_proxy(raise_req)
00284 
00285     def gripper_push_object(self, push_dist, which_arm, pose_res):
00286         # Convert pose response to correct push request format
00287         push_req = GripperPushRequest()
00288         push_req.start_point.header = pose_res.header
00289         push_req.start_point.point = pose_res.start_point
00290         push_req.arm_init = True
00291         push_req.arm_reset = True
00292         push_req.high_arm_init = True
00293 
00294         # Use the sent wrist yaw
00295         wrist_yaw = pose_res.push_angle
00296         push_req.wrist_yaw = wrist_yaw
00297         push_req.desired_push_dist = push_dist + abs(self.gripper_x_offset)
00298 
00299         # Offset pose to not hit the object immediately
00300         push_req.start_point.point.x += self.gripper_x_offset*cos(wrist_yaw)
00301         push_req.start_point.point.y += self.gripper_x_offset*sin(wrist_yaw)
00302         push_req.start_point.point.z = self.gripper_start_z
00303         push_req.left_arm = (which_arm == 'l')
00304         push_req.right_arm = not push_req.left_arm
00305 
00306         rospy.loginfo("Calling gripper pre push service")
00307         pre_push_res = self.gripper_pre_push_proxy(push_req)
00308         rospy.loginfo("Calling gripper push service")
00309         push_res = self.gripper_push_proxy(push_req)
00310         rospy.loginfo("Calling gripper post push service")
00311         post_push_res = self.gripper_post_push_proxy(push_req)
00312 
00313     def sweep_object(self, push_dist, which_arm, pose_res):
00314         # Convert pose response to correct push request format
00315         sweep_req = GripperPushRequest()
00316         sweep_req.left_arm = (which_arm == 'l')
00317         sweep_req.right_arm = not sweep_req.left_arm
00318         push_req.high_arm_init = True
00319 
00320         # if sweep_req.left_arm:
00321         if pose_res.push_angle > 0:
00322             y_offset_dir = -1
00323         else:
00324             y_offset_dir = +1
00325 
00326         # Correctly set the wrist yaw
00327         if pose_res.push_angle > 0.0:
00328             wrist_yaw = pose_res.push_angle - pi/2
00329         else:
00330             wrist_yaw = pose_res.push_angle + pi/2
00331         sweep_req.wrist_yaw = wrist_yaw
00332         sweep_req.desired_push_dist = -y_offset_dir*(self.sweep_y_offset +
00333                                                      push_dist)
00334 
00335         # Set offset in x y, based on distance
00336         sweep_req.start_point.header = pose_res.header
00337         sweep_req.start_point.point = pose_res.start_point
00338         sweep_req.start_point.point.x += self.sweep_x_offset
00339         sweep_req.start_point.point.y += y_offset_dir*self.sweep_y_offset
00340         sweep_req.start_point.point.z = self.sweep_start_z
00341         sweep_req.arm_init = True
00342         sweep_req.arm_reset = True
00343 
00344         rospy.loginfo("Calling gripper pre sweep service")
00345         pre_sweep_res = self.gripper_pre_sweep_proxy(sweep_req)
00346         rospy.loginfo("Calling gripper sweep service")
00347         sweep_res = self.gripper_sweep_proxy(sweep_req)
00348         rospy.loginfo("Calling gripper post sweep service")
00349         post_sweep_res = self.gripper_post_sweep_proxy(sweep_req)
00350 
00351     def overhead_push_object(self, push_dist, which_arm, pose_res):
00352         # Convert pose response to correct push request format
00353         push_req = GripperPushRequest()
00354         push_req.start_point.header = pose_res.header
00355         push_req.start_point.point = pose_res.start_point
00356         push_req.arm_init = True
00357         push_req.arm_reset = True
00358         push_req.high_arm_init = True
00359 
00360         # Correctly set the wrist yaw
00361         wrist_yaw = pose_res.push_angle
00362         push_req.wrist_yaw = wrist_yaw
00363         push_req.desired_push_dist = push_dist
00364 
00365         # Offset pose to not hit the object immediately
00366         push_req.start_point.point.x += self.overhead_x_offset*cos(wrist_yaw)
00367         push_req.start_point.point.y += self.overhead_x_offset*sin(wrist_yaw)
00368         push_req.start_point.point.z = self.overhead_start_z
00369         push_req.left_arm = (which_arm == 'l')
00370         push_req.right_arm = not push_req.left_arm
00371 
00372         rospy.loginfo("Calling pre overhead push service")
00373         pre_push_res = self.overhead_pre_push_proxy(push_req)
00374         rospy.loginfo("Calling overhead push service")
00375         push_res = self.overhead_push_proxy(push_req)
00376         rospy.loginfo("Calling post overhead push service")
00377         post_push_res = self.overhead_post_push_proxy(push_req)
00378 
00379     def overhead_pull_object(self, push_dist, which_arm, pose_res):
00380         # Convert pose response to correct push request format
00381         push_req = GripperPushRequest()
00382         push_req.start_point.header = pose_res.header
00383         push_req.start_point.point = pose_res.start_point
00384         push_req.arm_init = True
00385         push_req.arm_reset = True
00386         push_req.high_arm_init = True
00387 
00388         wrist_yaw = pose_res.push_angle
00389         # Correctly set the wrist yaw
00390         while wrist_yaw < -pi*0.5:
00391             wrist_yaw += pi
00392         while wrist_yaw > pi*0.5:
00393             wrist_yaw -= pi
00394         push_req.wrist_yaw = wrist_yaw
00395         # Add offset distance to push to compensate
00396         push_req.desired_push_dist = push_dist + self.pull_dist_offset
00397 
00398         # Offset pose to not hit the object immediately
00399         rospy.loginfo('Pre pull offset (x,y): (' +
00400                       str(push_req.start_point.point.x) + ', ' +
00401                       str(push_req.start_point.point.y) + ')')
00402         push_req.start_point.point.x += self.pull_dist_offset*cos(wrist_yaw)
00403         push_req.start_point.point.y += self.pull_dist_offset*sin(wrist_yaw)
00404         push_req.start_point.point.z = self.pull_start_z
00405         push_req.left_arm = (which_arm == 'l')
00406         push_req.right_arm = not push_req.left_arm
00407 
00408         rospy.loginfo('Post pull offset (x,y): (' +
00409                       str(push_req.start_point.point.x) + ', ' +
00410                       str(push_req.start_point.point.y) + ')')
00411 
00412         rospy.loginfo("Calling pre overhead pull service")
00413         pre_push_res = self.overhead_pre_pull_proxy(push_req)
00414         rospy.loginfo("Calling overhead pull service")
00415         push_res = self.overhead_pull_proxy(push_req)
00416         rospy.loginfo("Calling post overhead pull service")
00417         post_push_res = self.overhead_post_pull_proxy(push_req)
00418 
00419 if __name__ == '__main__':
00420     guided = True
00421     node = SingulationExecutive(False)
00422     node.run(50, guided)


tabletop_pushing
Author(s): Tucker Hermans
autogenerated on Wed Nov 27 2013 11:59:44