sm_approach.py
Go to the documentation of this file.
00001 #! /usr/bin/python
00002 import roslib
00003 roslib.load_manifest('pr2_approach_table')
00004 roslib.load_manifest('rfid_behaviors')
00005 import rospy
00006 
00007 import tf
00008 import tf.transformations as tft
00009 import smach
00010 from smach_ros import SimpleActionState, ServiceState, IntrospectionServer
00011 import actionlib
00012 from geometry_msgs.msg import PoseStamped, PoseArray, Pose, Quaternion
00013 from move_base_msgs.msg import MoveBaseAction
00014 
00015 from pr2_approach_table.srv import ApproachSrv
00016 from pr2_approach_table.msg import ApproachAction, ApproachResult, ApproachGoal
00017 from rfid_behaviors.srv import FloatFloat_Int32 as RotateBackupSrv
00018 
00019 import numpy as np, math
00020 
00021 class CheckHeading(smach.State):
00022     def __init__(self, listener):
00023         smach.State.__init__(self,
00024                              outcomes=['succeeded', 'preempted'],
00025                              input_keys = ['target_pose'],  # PoseStamped
00026                              output_keys = ['angular_error']) # float
00027         self.listener = listener
00028         self.initialized = False
00029 
00030     def execute(self, userdata):
00031         ps_desired = userdata.target_pose
00032         self.GLOBAL_FRAME = ps_desired.header.frame_id
00033         
00034         if not self.initialized:
00035             self.initialized = True
00036             # self.listener = tf.TransformListener() # this is now passed in.
00037             rospy.logout( 'CheckHeading (smach): Waiting on transforms from (%s -> %s)'
00038                           % ( self.GLOBAL_FRAME, '/base_link' ))
00039             self.listener.waitForTransform( '/base_link',
00040                                             self.GLOBAL_FRAME,
00041                                             rospy.Time(0), timeout = rospy.Duration(30) )
00042             rospy.logout( 'CheckHeading (smach): Ready.' )
00043 
00044         try:
00045             ps = PoseStamped()
00046             ps.header.stamp = rospy.Time(0)
00047             ps.header.frame_id = '/base_link'
00048             ps.pose.orientation.w = 1.0
00049 
00050             ps_global = self.listener.transformPose( self.GLOBAL_FRAME, ps )
00051             efq = tft.euler_from_quaternion
00052             r,p,yaw_curr = efq(( ps_global.pose.orientation.x,
00053                                  ps_global.pose.orientation.y,
00054                                  ps_global.pose.orientation.z,
00055                                  ps_global.pose.orientation.w ))
00056             r,p,yaw_des = efq(( ps_desired.pose.orientation.x,
00057                                 ps_desired.pose.orientation.y,
00058                                 ps_desired.pose.orientation.z,
00059                                 ps_desired.pose.orientation.w ))
00060 
00061             rospy.logout( 'CheckHeading (smach): Error was %3.2f (deg)' % math.degrees(yaw_des - yaw_curr))
00062             userdata.angular_error = yaw_des - yaw_curr
00063         except:
00064             rospy.logout( 'CheckHeading (smach): TF failed.  Returning ang error of 0.0' )
00065             userdata.angular_error = 0.0
00066         return 'succeeded'
00067 
00068 
00069 class PoseSelection(smach.State):
00070     def __init__(self, listener):
00071         smach.State.__init__(self,
00072                              outcomes=['succeeded', 'aborted'],
00073                              input_keys=['candidate_poses'], # of type PoseArray!
00074                              output_keys=['selected_pose_global',   # of type PoseStamped!
00075                                           'movebase_pose_global' ]) # of type PoseStamped!
00076         self.listener = listener
00077         self.poses = []
00078         self.initialized = False
00079         self.ind = 0
00080         #self.GLOBAL_FRAME = '/odom_combined'
00081         self.GLOBAL_FRAME = '/map'
00082 
00083     def execute(self, userdata):
00084         rospy.logout( 'Executing POSE_SELECTION' )
00085 
00086         poses_frame = userdata.candidate_poses.header.frame_id
00087         #dist = userdata.approach_dist
00088         dist = 0.80
00089 
00090         if not self.initialized:
00091             self.initialized = True
00092             self.ind = 0
00093             # self.listener = tf.TransformListener()  # This is now passed in
00094             rospy.logout( 'PoseSelector (smach): Waiting on transforms from (%s -> %s)'
00095                           % ( self.GLOBAL_FRAME, poses_frame ))
00096             self.listener.waitForTransform( poses_frame,
00097                                             self.GLOBAL_FRAME,
00098                                             rospy.Time(0), timeout = rospy.Duration(30) )
00099             rospy.logout( 'PoseSelector (smach): Ready.' )
00100 
00101             
00102             # convert PoseArray to list of PoseStamped in global frame
00103             frame_adjusted = [ self.frame_adjust(i,poses_frame,dist) for i in userdata.candidate_poses.poses ]
00104             self.poses = [ i for i in frame_adjusted if i != None ] # in case the transforms fail.
00105 
00106         if len( self.poses ) == 0 or self.ind >= len( self.poses ): # we've tried everything...
00107             return 'aborted'
00108 
00109         # print 'POSES:\n', self.poses
00110         userdata.selected_pose_global = self.poses[ self.ind ][0]
00111         userdata.movebase_pose_global = self.poses[ self.ind ][1]
00112         # print 'SELECTED_POSE:\n', self.poses[ self.ind ]
00113         self.ind += 1
00114         return 'succeeded'
00115 
00116     def frame_adjust( self, pose, poses_frame, dist, arm_offset = 0.0 ):
00117         # Adjust all incoming candidate poses into a "Global" frame.
00118         # Positive arm offset to move the approach point "right"
00119         ps = PoseStamped()
00120         ps.header.frame_id = poses_frame
00121         ps.header.stamp = rospy.Time(0)
00122         ps.pose = pose
00123 
00124         # In some cases, we may want to move the pose right or left
00125         #   depending on which arm will be used for grasping.
00126         efq = tft.euler_from_quaternion
00127         r,p,y = efq(( pose.orientation.x,
00128                       pose.orientation.y,
00129                       pose.orientation.z,
00130                       pose.orientation.w ))
00131         ps.pose.position.x = pose.position.x + arm_offset * np.cos( y + np.pi/2 )
00132         ps.pose.position.y = pose.position.y + arm_offset * np.sin( y + np.pi/2 )
00133 
00134         # print ps, '\nto\n', self.GLOBAL_FRAME, '\ntime:', rospy.Time.now()
00135         
00136         try:
00137             ps_global = self.listener.transformPose( self.GLOBAL_FRAME, ps )
00138             ps_global.pose.position.z = 0.0
00139             # print 'PS:\n', ps, '\nPS_GLOBAL:\n', ps_global
00140 
00141             r,p,y = efq(( ps_global.pose.orientation.x,
00142                           ps_global.pose.orientation.y,
00143                           ps_global.pose.orientation.z,
00144                           ps_global.pose.orientation.w ))
00145 
00146             # We want to move to a position that is 40-cm back from the tabletop using navstack
00147             mps = PoseStamped() 
00148             mps.header.frame_id = ps_global.header.frame_id
00149             mps.header.stamp = rospy.Time.now()
00150             mps.pose.position.x = ps_global.pose.position.x + dist * np.cos( y )
00151             mps.pose.position.y = ps_global.pose.position.y + dist * np.sin( y )
00152             qfe = tft.quaternion_from_euler
00153             mps.pose.orientation = Quaternion( *qfe( 0.0, 0.0, y - np.pi ))
00154             # print 'MPS:\n', mps
00155 
00156             # Return value: selected_pose_global, movebase_pose_global.
00157             rv = [ ps_global, mps ]
00158 
00159         except:
00160             rospy.logout( 'PoseSelector (smach): TF failed. Ignoring pose.' )
00161             rv = None
00162 
00163         return rv # Return value: selected_pose_global (PoseStamped), movebase_pose_global (PoseStamped)
00164         
00165             
00166 
00167 def sm_approach_table( listener = None ):
00168     
00169     # for various states, you need a tf listener, but only one per thread supported.
00170     if listener == None:
00171         try:
00172             rospy.init_node( 'sm_approach' )
00173         except:
00174             rospy.logout( 'sm_approach_table: Node already initialized elsewhere' )
00175 
00176         listener = tf.TransformListener()
00177     
00178     # Python only allows one listener per thread.  This function has two (or more)
00179     #   classes that require the listener.  You can pass in one from higher in the
00180     #   hierarchy if you prefer.
00181     
00182     # Create a SMACH state machine
00183     sm = smach.StateMachine( outcomes = ['succeeded','aborted','preempted'],
00184                              input_keys = ['table_edge_poses'],  # type PoseArray
00185                              output_keys = ['movebase_pose_global', # type PoseStamped
00186                                             'table_edge_global']) # type PoseStamped
00187 
00188     # Open the container
00189     with sm:
00190         smach.StateMachine.add(
00191             'POSE_SELECTION',
00192             PoseSelection( listener = listener ),
00193             transitions = { 'succeeded' : 'MOVE_BASE' },
00194             remapping = {'candidate_poses':'table_edge_poses', # input (PoseArray)
00195                          'selected_pose_global':'table_edge_global', # output (PoseStamped)
00196                          'movebase_pose_global':'movebase_pose_global'}) # output (PoseStamped)
00197 
00198 
00199         smach.StateMachine.add(
00200             'MOVE_BASE',
00201             SimpleActionState( '/move_base',
00202                                MoveBaseAction,
00203                                goal_slots = ['target_pose'], # PoseStamped
00204                                outcomes = ['succeeded','aborted','preempted']),
00205             transitions = { 'succeeded' : 'CHECK_HEADING',
00206                             'aborted' : 'POSE_SELECTION' },
00207             remapping = {'target_pose':'movebase_pose_global'}) # input (PoseStamped)
00208 
00209         smach.StateMachine.add(
00210             'CHECK_HEADING',
00211             CheckHeading( listener = listener ),
00212             transitions = { 'succeeded':'ADJUST_HEADING' },
00213             remapping = { 'target_pose':'movebase_pose_global', # input (PoseStamped)
00214                           'angular_error':'angular_error' }) # output (float)
00215 
00216         smach.StateMachine.add(
00217             'ADJUST_HEADING',
00218             ServiceState( '/rotate_backup',
00219                           RotateBackupSrv,
00220                           request_slots = ['rotate']), # float (displace = 0.0)
00221             transitions = { 'succeeded':'MOVE_FORWARD' },
00222             remapping = {'rotate':'angular_error'})
00223         
00224 
00225         approach_goal = ApproachGoal()
00226         approach_goal.forward_vel = 0.05
00227         approach_goal.forward_mult = 0.50
00228         smach.StateMachine.add(
00229             'MOVE_FORWARD',
00230             SimpleActionState( '/approach_table/move_forward_act',
00231                                ApproachAction,
00232                                goal = approach_goal ),
00233             transitions = { 'succeeded' : 'succeeded' })
00234 
00235     return sm
00236 
00237 
00238             
00239             
00240 if __name__ == '__main__':
00241     rospy.init_node('smach_example_state_machine')
00242 
00243     # sm = sm_approach_table()
00244 
00245     sm = smach.StateMachine(outcomes=['succeeded','aborted','preempted'],
00246                             input_keys = [ 'approach_poses' ])
00247 
00248     p = Pose()
00249     p.position.x = 0.3879
00250     p.position.y = 0.79838
00251     p.position.z = 0.0
00252 
00253     p.orientation.z = -0.704
00254     p.orientation.w = 0.709
00255 
00256     pa = PoseArray()
00257     pa.header.frame_id = '/map'
00258     pa.poses = [ p ]
00259 
00260     sm.userdata.table_edge_poses = pa
00261 
00262     with sm:
00263         sm_table = sm_approach_table()
00264 
00265         smach.StateMachine.add(
00266             'APPROACH_TABLE',
00267             sm_table,
00268             remapping = {'table_edge_poses':'table_edge_poses', # input
00269                          'movebase_pose_global':'movebase_pose_global', # output
00270                          'table_edge_global':'table_edge_global'}, # output
00271             #transitions = {'succeeded':'MOVE_BACK'})
00272             transitions = {'succeeded':'succeeded'})
00273 
00274         # GRASP!
00275         
00276         # smach.StateMachine.add(
00277         #     'MOVE_BACK',
00278         #     SimpleActionState( '/move_base',
00279         #                        MoveBaseAction,
00280         #                        goal_slots = ['target_pose'], # PoseStamped
00281         #                        outcomes = ['succeeded','aborted','preempted']),
00282         #     transitions = { 'succeeded' : 'succeeded'},
00283         #     remapping = {'target_pose':'intermediate_pose'}) # input
00284 
00285         # GO DELIVER!
00286 
00287 
00288     sis = IntrospectionServer('Approach_Table', sm, '/SM_APPROACH_TABLE')
00289     sis.start()
00290 
00291     
00292     rospy.sleep( 3.0 )
00293     outcome = sm.execute()
00294     
00295     sis.stop()
00296 
00297     
00298 
00299             
00300                                
00301                                
00302 
00303     


pr2_approach_table
Author(s): Travis Deyle, Advisor: Prof. Charlie Kemp (Healthcare Robotics Lab at Georgia Tech)
autogenerated on Wed Nov 27 2013 12:06:43