articulate_cart_server.py
Go to the documentation of this file.
00001 #! /usr/bin/env python
00002 # Copyright (c) 2008, Willow Garage, Inc.
00003 # All rights reserved.
00004 #
00005 # Redistribution and use in source and binary forms, with or without
00006 # modification, are permitted provided that the following conditions are met:
00007 #
00008 #         * Redistributions of source code must retain the above copyright
00009 #             notice, this list of conditions and the following disclaimer.
00010 #         * Redistributions in binary form must reproduce the above copyright
00011 #             notice, this list of conditions and the following disclaimer in the
00012 #             documentation and/or other materials provided with the distribution.
00013 #         * Neither the name of the Willow Garage, Inc. nor the names of its
00014 #             contributors may be used to endorse or promote products derived from
00015 #             this software without specific prior written permission.
00016 #
00017 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027 # POSSIBILITY OF SUCH DAMAGE.
00028 
00029 # Author: Jon Scholz
00030 
00031 PKG="articulate_cart"
00032 import roslib; roslib.load_manifest(PKG)
00033 import rospy
00034 import tf
00035 import thread
00036 import numpy
00037 
00038 from manipulation_transforms.srv import *
00039 
00040 from std_msgs.msg import *
00041 from geometry_msgs.msg import *
00042 #from pr2_controllers_msgs.msg import *
00043 from math import pi, pow, atan, sin, cos, sqrt
00044 
00045 class ArticulateCartException(Exception):
00046   pass
00047 
00048 class articulateCartServer:
00049   """ A server for manipulating a cart being grasped by the grippers
00050   of the pr2.  Uses manipulation_transforms to compute gripper poses satisfying
00051   incoming goal requests.  Goals for the cart pose are specified in the
00052   base_footprint frame.  manipulation_transforms is used to map this goal to
00053   gripper poses in the base_footprint frame, which are then written
00054   to the goal topics for teleop_controllers (make sure to specify base_footprint
00055   as the frame_id rather than torso_lift_link).
00056   Accepts goals as PoseStamped messages, and issues PoseStamped messages
00057   to the arms.
00058   """
00059 
00060   # create messages that are used to publish feedback/result
00061   _r_arm_SSE = 0
00062   _l_arm_SSE = 0
00063   _r_arm_err_twist = Twist()
00064   _l_arm_err_twist = Twist()
00065   errthresh = 0.02
00066 
00067   ##
00068   # @param use_tf_grippers: lookup gripper poses from tf and re-initialize manipulation_transforms
00069   # @param use_gripper_midpoint: set object initial pose to midpoint between grippers, and re-initialize manipulation_transforms
00070   # @param tf_object_name: if non-empty, attempt to lookup object initial pose on tf with given frame name, and re-initialize manipulation_transforms 
00071   #
00072   def __init__(self, use_tf_grippers, use_gripper_midpoint, tf_object_name):
00073     # A tf listener for reading gripper poses and stuff
00074     self._tl = tf.TransformListener()
00075 
00076     ## Attach to all topics and services
00077     self._initialize_ros_coms()
00078 
00079     # Wait till initial pose has been set
00080     self.goal_received = False
00081     self.goal_sub = rospy.Subscriber('cart_pushing/ready_pose_achieved', rospy.Header, self.ready_pose_cb)
00082     rospy.loginfo('Articulate cart initialization waiting for ready pose before proceeding')
00083     ready_pose_counter = 0
00084     while not rospy.is_shutdown() and not self.goal_received:
00085        rospy.sleep(0.1)
00086        ready_pose_counter += 1
00087        if ready_pose_counter > 100:
00088          raise ArticulateCartException('Timed out when waiting for confirmation of ready pose achieved')
00089     rospy.loginfo('Articulate cart received ready pose')
00090     
00091     ## Initialize object and gripper poses according to constructor params
00092     self._initialize_poses(use_tf_grippers, use_gripper_midpoint, tf_object_name)
00093 
00094     ## Mutex for pose-related members {_cart_pose_tup, _cart_pose_msg, _pose_resp, _twist_req, _cart_pose_euler}
00095     self._pose_mutex = thread.allocate_lock()
00096         
00097     # Indicator fake twist controller loop
00098     self._fake_twist_loop_started = False # Toggled by first incoming twist message
00099 
00100     ## Spin thread for posture updater
00101     thread.start_new_thread(self._posture_loop, ("elbowmidr", "elbowmidl")) # elbowdownr, elbowdownl
00102     
00103     ## All done:
00104     print "Articulate Cart Server Ready!"
00105 
00106   def ready_pose_cb(self, msg):
00107     self.goal_received = True
00108 
00109   def _initialize_ros_coms(self):
00110     ## subscriber for goal topic
00111     self._goal_sub = rospy.Subscriber('~command_twist', TwistStamped, self._fake_twist_goal_cb)
00112 
00113     
00114     ## publisher for arm cartesian pose/twist controller
00115     self._r_arm_pose_pub = rospy.Publisher('/r_cart/command_pose', PoseStamped)
00116     self._l_arm_pose_pub = rospy.Publisher('/l_cart/command_pose', PoseStamped)
00117     self._r_arm_twist_pub = rospy.Publisher('/r_cart/command_twist', TwistStamped)
00118     self._l_arm_twist_pub = rospy.Publisher('/l_cart/command_twist', TwistStamped)
00119     
00120     ## Publishers for teleop_controllers' posture topics
00121     self._r_posture_pub = rospy.Publisher("/r_cart/command_posture", Float64MultiArray)
00122     self._l_posture_pub = rospy.Publisher("/l_cart/command_posture", Float64MultiArray)
00123 
00124     ## Publisher for when when an invalid pose is received
00125     self.invalid_pose_pub = rospy.Publisher("~invalid_pose", Empty)
00126     
00127     ## subscriber for arm state errors
00128     self._r_arm_sub = rospy.Subscriber('/r_cart/state/x_err', Twist, self._r_arm_err_cb)
00129     self._l_arm_sub = rospy.Subscriber('/l_cart/state/x_err', Twist, self._l_arm_err_cb)
00130     self._r_arm_err_lock = thread.allocate_lock()
00131     self._l_arm_err_lock = thread.allocate_lock()
00132     
00133     ## get handles for manipulation_transforms service calls
00134     rospy.loginfo("Waiting for solver services")
00135     rospy.wait_for_service("manipulation_transforms_server/MapObjectPoseToEffectors", 10)
00136     rospy.wait_for_service("manipulation_transforms_server/LoadInitialTransforms", 10)
00137     self._solver_reloadTransforms_srv = rospy.ServiceProxy("manipulation_transforms_server/LoadInitialTransforms",
00138                                                            LoadInitialTransforms)
00139     self._cartPoseToGrippers_srv = rospy.ServiceProxy("manipulation_transforms_server/MapObjectPoseToEffectors",
00140                                                         MapObjectPoseToEffectors)
00141     rospy.loginfo("Solver services connected")
00142 
00143     
00144       
00145   def _initialize_poses(self, use_tf_grippers, use_gripper_midpoint, tf_object_name):
00146     ## Update initial effector poses if requested
00147     call_reload=False
00148     if use_tf_grippers:
00149         ## Read current gripper transforms (needed if reloading solver, or computing drift)
00150         self._r_pose_tup = self.get_tf_transform('base_footprint', 'r_gripper_tool_frame')
00151         self._l_pose_tup = self.get_tf_transform('base_footprint', 'l_gripper_tool_frame')
00152         if self._r_pose_tup is not None and self._l_pose_tup is not None:
00153             use_defaults = False
00154         else:
00155             rospy.logerr("TF lookup of gripper poses failed.  Using default grasp transforms from the parameter server")
00156         
00157         call_reload=True
00158     else:
00159       # Load poses from param server
00160       self._r_pose_tup = self.get_transform_param("cart_pushing/r_gripper_grasp")
00161       self._l_pose_tup = self.get_transform_param("cart_pushing/l_gripper_grasp")
00162     
00163     ## Set object poses from grippers if requested:    
00164     if use_gripper_midpoint:
00165         # Compute object orientation as midpoint between gripper orientations
00166         self._cart_pose_tup = self.interpolate_pose_tup(self._r_pose_tup, self._l_pose_tup, 0.5)
00167         #obj_trans = ((numpy.array(list(self._r_pose_tup[0])) + numpy.array(list(self._l_pose_tup[0])))/2).tolist()            
00168         call_reload=True
00169     elif tf_object_name:
00170         # Load object orientation from tf
00171         self._cart_pose_tup = self.get_tf_transform('base_footprint', tf_object_name)
00172         call_reload=True
00173     else:
00174         ## read object pose from parameter server
00175         self._cart_pose_tup = self.get_transform_param("cart_pushing/cart_init_pose")
00176         
00177     ## Create a PoseStamped message with initial cart pose
00178     self._cart_pose_msg = PoseStamped()
00179     self._cart_pose_msg.pose.position = geometry_msgs.msg.Point(*self._cart_pose_tup[0])
00180     self._cart_pose_msg.pose.orientation = geometry_msgs.msg.Quaternion(*self._cart_pose_tup[1])
00181     self._cart_pose_msg.header.frame_id = "base_footprint"
00182     self._cart_pose_msg.header.stamp = rospy.Time.now()
00183 
00184     # Pre-compute euler pose (for fake twist controller)
00185     self._cart_pose_euler = self.pose_msg_to_euler_list(self._cart_pose_msg.pose)
00186 
00187     ## Pre-compute relative pose of left gripper (for twist cb drift error)
00188     self._r_pose_mtr = self.pose_tup_to_matrix(self._r_pose_tup)
00189     self._l_pose_mtr = self.pose_tup_to_matrix(self._l_pose_tup)
00190     self._l_relpose = self.get_rel_pose(self._r_pose_mtr, self._l_pose_mtr)
00191 
00192     ## Load cart workspace constraints from parameter server
00193     self._cart_x_min = rospy.get_param("cart_pushing/x_min")
00194     self._cart_x_max = rospy.get_param("cart_pushing/x_max")
00195     self._cart_y_min = rospy.get_param("cart_pushing/y_min")
00196     self._cart_y_max = rospy.get_param("cart_pushing/y_max")
00197     self._cart_t_min = rospy.get_param("cart_pushing/t_min")
00198     self._cart_t_max = rospy.get_param("cart_pushing/t_max")
00199 
00200     ## Reload solver transforms if necessary
00201     if call_reload:
00202         rospy.loginfo("Reloading solver grasp transforms with current gripper poses")
00203         self.set_transform_param("cart_pushing/cart_init_pose", self._cart_pose_tup)
00204         self.set_transform_param("cart_pushing/r_gripper_grasp", self._r_pose_tup)
00205         self.set_transform_param("cart_pushing/l_gripper_grasp", self._l_pose_tup)
00206         resp = self._solver_reloadTransforms_srv("")
00207         if not resp.success:
00208           rospy.logerror("Failed to reload solver transforms!")
00209         else:
00210           rospy.loginfo("Reloaded solver transforms using current gripper poses")
00211 
00212     ## Hard-code a set of arm postures
00213     self._postures = {
00214       'off': [],
00215       'mantis': [0, 1, 0,  -1, 3.14, -1, 3.14],
00216       'elbowupr': [-0.79,0,-1.6,  9999, 9999, 9999, 9999],
00217       'elbowupl': [0.79,0,1.6 , 9999, 9999, 9999, 9999],
00218       'old_elbowupr': [-0.79,0,-1.6, -0.79,3.14, -0.79,5.49],
00219       'old_elbowupl': [0.79,0,1.6, -0.79,3.14, -0.79,5.49],
00220       'elbowdownr': [-0.028262077316910873, 1.2946342642324222, -0.25785640577652386, 
00221                       -1.5498884526859626, -31.278913849571776, -1.0527644894829107, -1.8127318367654268],
00222       'elbowdownl': [-0.0088195719039858515, 1.2834828245284853, 0.20338442004843196, 
00223                       -1.5565279256852611, -0.096340012666916802, -1.0235018652439782, 1.7990893054129216],
00224       'elbowmidr': [-0.28990347455256005, 1.089312348420358, -0.94309188122730947, -1.3356881736879038, 
00225                      0.42316056966725524, -1.3491191190220519, -5.3263966597959937],
00226       'elbowmidl': [0.52521589892944154, 1.0820586985900948, 1.0342420867971822, -1.3718169587386895, 
00227                     -6.6154390746853364, -1.3118240320657639, -10.572623351665454]
00228       }
00229              
00230 
00231   def _fake_twist_goal_cb(self, msg):
00232     """An alternative callback for twist messages that spawns an internal controller to simulate
00233     the object twist action using pose commands"""
00234     rospy.logdebug('Read goal: [%f,%f,%f][%f,%f,%f]', 
00235                   msg.twist.linear.x,
00236                   msg.twist.linear.y,
00237                   msg.twist.linear.z,
00238                   msg.twist.angular.x,
00239                   msg.twist.angular.y,
00240                   msg.twist.angular.z)
00241     
00242     # Save twist request for fake controller
00243     with self._pose_mutex:
00244       self._twist_req = msg.twist
00245 
00246     # if this is the first incoming message, spin the controller thread
00247     if self._fake_twist_loop_started == False:
00248         self._fake_twist_loop_started = True;
00249         thread.start_new_thread(self._fake_twist_controller, ()) # at default frequency
00250       
00251   def _r_arm_err_cb(self, twist):
00252     self._r_arm_err_lock.acquire()
00253     self._r_arm_err_twist=twist;
00254     self._r_arm_SSE = self.get_twist_SSE(twist)
00255     self._r_arm_err_lock.release()
00256 
00257   def _l_arm_err_cb(self, twist):
00258     self._l_arm_err_lock.acquire()
00259     self._l_arm_err_twist=twist
00260     self._l_arm_SSE = self.get_twist_SSE(twist)
00261     self._l_arm_err_lock.release()
00262 
00263   def _posture_loop(self, r_posture, l_posture):
00264     """ Send updates to the posture topic every ROS second """
00265     while not rospy.is_shutdown():
00266       self._r_posture_pub.publish(Float64MultiArray(data = self._postures[r_posture]))
00267       self._l_posture_pub.publish(Float64MultiArray(data = self._postures[l_posture]))
00268       rospy.sleep(1.0)
00269 
00270   def _fake_twist_controller(self, r = 20, recovery_horizon = 5):
00271       """ Simulates the twist applied to the object, and updates the gripper poses accordingly.
00272       """
00273 
00274       counter = 0
00275 
00276       # Initialize object pose list as Euler representation of cart pose
00277       with self._pose_mutex:
00278           self._cart_pose_euler = self.pose_msg_to_euler_list(self._cart_pose_msg.pose)
00279       rate = rospy.Rate(r)
00280       t = 1.0/r
00281       
00282       while not rospy.is_shutdown():
00283           with self._pose_mutex:
00284             # if counter % 200 == 0:
00285             #  rospy.loginfo('Cart rel pose is %s', self._cart_pose_euler)
00286             counter += 1
00287 
00288             if self._check_cart_pose(self._cart_pose_euler):
00289               # Default case: current cart pose is valid; let's make sure it stays that way
00290               new_pose = forward_simulate_twist(self._cart_pose_euler, self._twist_req, t)
00291               if self._check_cart_pose(new_pose):
00292                 self.set_new_pose(new_pose)
00293               else:
00294                 self.invalid_pose_pub.publish()
00295                 rospy.logwarn('Applying %s for %s secs from %s would lead to infeasible pose %s; ignoring.',
00296                               self._twist_req, t, self._cart_pose_euler, new_pose)
00297 
00298             else:
00299               # 'Recovery' case: current pose is not valid; we check if the twist is bringing
00300               # us back into validity
00301               succeeded = False
00302               for i in xrange(recovery_horizon):
00303                 new_pose = forward_simulate_twist(self._cart_pose_euler, self._twist_req, i*t)
00304                 if self._check_cart_pose(new_pose):
00305                   # Twist is bringing us back to validity, so we accept it
00306                   rospy.loginfo('Current pose %s invalid, but %s leads to valid pose %s after %s seconds',
00307                                 self._cart_pose_euler, self._twist_req, i*t)
00308                   self.set_new_pose(new_pose)
00309                   succeeded = True
00310                   break
00311               if not succeeded:
00312                 self.invalid_pose_pub.publish()
00313                 rospy.logwarn('Currently in collision at %s, and twist %s not helping',
00314                               self._cart_pose_euler, self._twist_req)
00315           rate.sleep()
00316 
00317   def set_new_pose (self, p):
00318       self._cart_pose_euler = p
00319       self._cart_pose_msg.pose = self.euler_list_to_pose_msg(p)
00320       self._pose_resp = self._cartPoseToGrippers_srv(self._cart_pose_msg)
00321       self._pose_command_action() 
00322       
00323   def _pose_command_action(self):
00324     """Send the target effector poses to the arm control topics
00325     Assumes that self._pose_resp is up-to-date"""
00326     if len(self._pose_resp.effector_poses) != 2:
00327       rospy.logerr("Expected 2 gripper poses, but got %d" % len(self._pose_resp.effector_poses))
00328     else:
00329       self._r_arm_pose_pub.publish(self._pose_resp.effector_poses[0])
00330       self._l_arm_pose_pub.publish(self._pose_resp.effector_poses[1])
00331     
00332     #print self._pose_resp.effector_poses
00333     # Uncomment to wait for error to drop before returning
00334     # while self._l_arm_SSE + self._r_arm_SSE > self.errthresh and not rospy.is_shutdown():
00335     #   print self._l_arm_SSE + self._r_arm_SSE
00336 
00337   def get_transform_param(self, name):
00338       """Returns the named transform as a pose tuple
00339       
00340       Arguments:
00341       - `name`: string matching a pose on the param server
00342       """
00343       param = rospy.get_param(name)
00344       rospy.loginfo('Parameter %s is %s', name, param)
00345       return (tuple(param["position"]), tuple(param["orientation"]))  
00346   
00347 
00348   def set_transform_param(self, name, pose_tup):
00349       """Sets the provided transform param tuple on the paramater server in "cart format"
00350       
00351       Arguments:
00352       - `pose_tup`: a tuple containing ((trans),(rot))
00353       - `name`: a string to set
00354       """
00355       rospy.set_param(name, {"position":pose_tup[0], "orientation":pose_tup[1]})
00356   
00357   def pose_tup_to_matrix(self, tup):
00358       """Returns a numpy 4x4 transform array from a pose provided in a tuple ((vector), (quaternion))"""
00359       mtr = tf.transformations.quaternion_matrix(tup[1])
00360       mtr[0][3] = tup[0][0]
00361       mtr[1][3] = tup[0][1]
00362       mtr[2][3] = tup[0][2]
00363       return mtr
00364 
00365   def pose_tup_to_msg(self, tup):
00366     """Returns Pose message assembled from tf query tuple """
00367     msg = Pose()
00368     msg.position.x = tup[0][0]
00369     msg.position.y = tup[0][1]
00370     msg.position.z = tup[0][2]
00371     msg.orientation.x = tup[1][1]
00372     msg.orientation.y = tup[1][2]
00373     msg.orientation.z = tup[1][2]
00374     msg.orientation.w = tup[1][3]
00375     return msg
00376   
00377   def get_rel_pose(self, p1, p2):
00378       """Compute relative pose of p2 in frame of p1 (4x4 numpy arrays)"""      
00379       return numpy.dot(numpy.linalg.inv(p1), p2)
00380 
00381   def pose_msg_to_euler_list(self, Pose):
00382       """Returns a list representation of Pose [[vector],[euler angles]]"""
00383       return [[Pose.position.x,
00384                Pose.position.y,
00385                Pose.position.z],
00386                list(tf.transformations.euler_from_quaternion([Pose.orientation.x,
00387                                                               Pose.orientation.y,
00388                                                               Pose.orientation.z,
00389                                                               Pose.orientation.w]))]
00390   def euler_list_to_pose_msg(self, euler_pose):
00391       """ convert to pose represented as a list [[vector],[euler angles]] to a Pose msg"""
00392       quat = tf.transformations.quaternion_from_euler(*euler_pose[1])
00393       return Pose(position=Point(*euler_pose[0]),
00394                   orientation=Quaternion(*quat))
00395       
00396   def twist_msg_to_tup(self, msg):
00397       """Returns a twist tuple ((linear), (angular)) from parameters in a Twist message """
00398       return ((msg.linear.x, msg.linear.y, msg.linear.z),
00399               (msg.angular.x, msg.angular.y, msg.angular.z))
00400 
00401   def twist_tup_to_msg(self, tup):
00402       """ Returns a Twist message from parameters in a tuple ((linear), (angular))"""
00403       msg=Twist()
00404       msg.linear.x = tup[0][0]
00405       msg.linear.y = tup[0][1]
00406       msg.linear.z = tup[0][2]
00407       msg.angular.x = tup[1][0]
00408       msg.angular.y = tup[1][1]
00409       msg.angular.z = tup[1][2]  
00410       return msg
00411         
00412   def interpolate_pose_tup(self, p1, p2, blend=0.5):
00413       """Returns weighted interpolation of two pose tuples ((vector), (quaternion))"""
00414       linear = blend*numpy.array(list(p1[0])) + (1-blend)*numpy.array(list(p2[0]))
00415       angular = tf.transformations.quaternion_slerp(p1[1], p2[1], blend)
00416       return (linear.tolist(), angular.tolist())
00417   
00418   def interpolate_twist_tup(self, t1, t2, blend=0.5):
00419       """Returns a weighted average of two twist tuples ((linear), (angular))"""
00420       linear = blend*numpy.array(list(t1[0])) + (1-blend)*numpy.array(list(t2[0]))
00421       angular = blend*numpy.array(list(t1[1])) + (1-blend)*numpy.array(list(t2[1]))
00422       return (linear.tolist(), angular.tolist())
00423 
00424   def get_tf_transform(self, ref_frame, target_frame):
00425       """An exception-handling method for querying a transform from tf"""
00426       try:
00427           self._tl.waitForTransform(ref_frame, target_frame, rospy.Time(0), rospy.Duration(4.0))
00428           return self._tl.lookupTransform(ref_frame, target_frame, rospy.Time(0)) 
00429       except (tf.Exception, tf.LookupException, tf.ConnectivityException) as ex:
00430           print ex
00431           return None
00432   
00433   def get_l_drift_twist(self):
00434       """ Uses pose call to solver to compute error at left gripper, and returns as twist
00435       """
00436       #TODO: debug this
00437       
00438       # Compute target pose for left arm
00439       #r_pose_tup = self.get_tf_transform('base_footprint', 'r_gripper_tool_frame')
00440       #r_pose_mtr = self.pose_tup_to_matrix(r_pose_tup)
00441       #l_pose_goal =  
00442       
00443       # Compute relative transform to actual pose 
00444       ##########################################################
00445       # Compute actual relative pose of l_gripper in r_gripper frame
00446       #r_pose_tup = self.get_tf_transform('base_footprint', 'r_gripper_tool_frame')
00447       #l_pose_tup = self.get_tf_transform('base_footprint', 'l_gripper_tool_frame')
00448       #r_pose_mtr = self.pose_tup_to_matrix(r_pose_tup)
00449       #l_pose_mtr = self.pose_tup_to_matrix(l_pose_tup)
00450       #l_relpose_actual = self.get_rel_pose(r_pose_mtr, l_pose_mtr)
00451       
00452       # Compute transform from expected to actual
00453       #l_pose_err = self.get_rel_pose(self._l_relpose, l_relpose_actual)
00454       
00455       # Convert to euler for interpretation as a twist tuple      
00456       #return (tuple(tf.transformations.translation_from_matrix(l_pose_err)),
00457       #        tf.transformations.euler_from_matrix(l_pose_err))
00458       return ((0,0,0),(0,0,0))
00459       
00460   def get_twist_SSE(self, twist):
00461     return sum([pow(twist.linear.x,2),
00462                pow(twist.linear.y,2),
00463                pow(twist.linear.z,2),
00464                pow(twist.angular.x,2),
00465                pow(twist.angular.y,2),
00466                pow(twist.angular.z,2)])
00467   
00468   def _check_cart_pose(self, pose):
00469     """ Returns true if pose is reachable given workspace constraints of cart"""
00470     if pose[0][0] < self._cart_x_min or pose[0][0] > self._cart_x_max or \
00471        pose[0][1] < self._cart_y_min or pose[0][1] > self._cart_y_max or \
00472        pose[1][2] < self._cart_t_min or pose[1][2] > self._cart_t_max:
00473        return False
00474     
00475     return True
00476 
00477 
00478 ############################################################
00479 # Non-member utility functions
00480 ############################################################
00481 
00482 def forward_simulate_twist (pose, twist, t):
00483     """Forward simulate twist from pose for t seconds and return the resulting pose"""
00484     return [[pose[0][0] + twist.linear.x*t,
00485              pose[0][1] + twist.linear.y*t,
00486              pose[0][2] + twist.linear.z*t],
00487             [pose[1][0] + twist.angular.x*t,
00488              pose[1][1] + twist.angular.y*t,
00489              pose[1][2] + twist.angular.z*t]]
00490 
00491 
00492 ############################################################
00493 # Node
00494 ############################################################
00495   
00496 if __name__ == '__main__':
00497   from optparse import OptionParser
00498   parser = OptionParser(usage="")
00499   parser.add_option("-g", action="store_true", dest="use_tf_grippers", default=False,
00500                     help="use current gripper poses for initial grasp transforms (otherwise leaves defaults from param server)")
00501   parser.add_option("-m", "--midpoint", action="store_true", dest="midpoint", default=False,
00502                     help="set object initial pose to midpoint between grippers [default=%default]")
00503   parser.add_option("-o", "--object", dest="object", default="",
00504                     help="attempt to load an initial object transform with name OBJECT from tf [default=\"%default\"]")
00505   (options, args) = parser.parse_args()
00506   
00507   rospy.init_node(PKG+"_server")
00508   server = articulateCartServer(options.use_tf_grippers, options.midpoint, options.object)
00509   
00510   rospy.spin()
00511 
00512 
00513   


articulate_cart
Author(s): Jonathan Scholz
autogenerated on Tue Jan 7 2014 11:10:35