00001 #! /usr/bin/python 00002 # 00003 # Base class for interacting with a realtime controller using an equilibrium point control 00004 # framework. 00005 # 00006 # Copyright (c) 2012, Georgia Tech Research Corporation 00007 # All rights reserved. 00008 # 00009 # Redistribution and use in source and binary forms, with or without 00010 # modification, are permitted provided that the following conditions are met: 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 copyright 00014 # notice, this list of conditions and the following disclaimer in the 00015 # documentation and/or other materials provided with the distribution. 00016 # * Neither the name of the Georgia Tech Research Corporation nor the 00017 # names of its contributors may be used to endorse or promote products 00018 # derived from this software without specific prior written permission. 00019 # 00020 # THIS SOFTWARE IS PROVIDED BY GEORGIA TECH RESEARCH CORPORATION ''AS IS'' AND 00021 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 00022 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00023 # DISCLAIMED. IN NO EVENT SHALL GEORGIA TECH BE LIABLE FOR ANY DIRECT, INDIRECT, 00024 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 00025 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 00026 # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 00027 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 00028 # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 00029 # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00030 # 00031 # Author: Kelsey Hawkins 00032 00033 from threading import Lock 00034 import numpy as np 00035 import copy 00036 00037 import roslib 00038 roslib.load_manifest('hrl_pr2_arms') 00039 00040 import rospy 00041 00042 from urdf_parser_py.urdf import URDF 00043 from pykdl_utils.joint_kinematics import JointKinematics 00044 00045 00046 ## 00047 # Base class for interacting with a realtime controller using an equilibrium point control 00048 # frame work. An equilibrium point is a set of parameters representing the state of the 00049 # controller at which no torques would be applied. For a PID controller, this represents 00050 # the joint angles where the error would be zero. 00051 class EPArmBase(JointKinematics): 00052 ## 00053 # Constructor 00054 # @param arm_side Used to signify side of robot for similar arms 00055 # (e.g. 'r' for right, 'l' for left) 00056 # @param urdf URDF object of robot. 00057 # @param base_link Name of the root link of the kinematic chain. 00058 # Will fill in arm_side if %s is in string. 00059 # @param end_link Name of the end link of the kinematic chain. 00060 # Will fill in arm_side if %s is in string. 00061 # @param controller_name Optional name of the controller which will be subscribed to 00062 # to obtain controller information 00063 # @param kdl_tree Optional KDL.Tree object to use. If None, one will be generated 00064 # from the URDF. 00065 # @param timeout Time in seconds to wait for the /joint_states topic. 00066 def __init__(self, arm_side, urdf, base_link, end_link, controller_name=None, 00067 kdl_tree=None, timeout=1.): 00068 if "%s" in base_link: 00069 base_link = base_link % arm_side 00070 if "%s" in end_link: 00071 end_link = end_link % arm_side 00072 if controller_name is not None and "%s" in controller_name: 00073 controller_name = controller_name % arm_side 00074 super(EPArmBase, self).__init__(urdf, base_link, end_link, kdl_tree, timeout) 00075 self.arm_side = arm_side 00076 self.controller_name = controller_name 00077 self.ep = None # equilibrium point 00078 self.ep_time = None # time ep was last recorded 00079 self.ep_lock = Lock() 00080 self.ctrl_state_lock = Lock() 00081 self.ctrl_state_dict = {} # stores information from the realtime controller 00082 00083 ## 00084 # Returns the current pose of the tooltip 00085 # @return 4x4 np.mat homogeneous matrix 00086 def get_end_effector_pose(self): 00087 return self.forward() 00088 00089 ## 00090 # Returns the current equilibrium point as read from the controller 00091 # @return equilibrium point 00092 def get_ep(self): 00093 with self.ep_lock: 00094 if self.ep is None: 00095 rospy.logwarn("[ep_arm_base] Equilibrium point not read yet.") 00096 return copy.copy(self.ep) 00097 00098 ## 00099 # Commands the arm to move to desired equilbrium point 00100 def set_ep(self, *args): 00101 raise RuntimeError('Unimplemented Function') 00102 00103 ## 00104 # Wait until we have the ep from the controller 00105 # @param timeout Time in seconds at which we break if we haven't recieved the EP. 00106 def wait_for_ep(self, timeout=1.): 00107 start_time = rospy.get_time() 00108 r = rospy.Rate(100) 00109 while not rospy.is_shutdown() and rospy.get_time() - start_time < timeout: 00110 with self.ep_lock: 00111 if self.ep is not None: 00112 return True 00113 r.sleep() 00114 return False 00115 00116 ## 00117 # Returns the current state of the realtime controller. The state is filled in 00118 # by the subclasses. 00119 # @return dict with realtime controller information. 00120 def get_controller_state(self): 00121 with self.ctrl_state_lock: 00122 if self.controller_name is None: 00123 rospy.logerror("[ep_arm_base] get_controller_state NOT IMPLEMENTED!") 00124 return None 00125 elif len(self.ctrl_state_dict) == 0: 00126 rospy.logwarn("[ep_arm_base] Controller state not yet published.") 00127 return None 00128 return self.ctrl_state_dict 00129 00130 ## 00131 # Returns whether the equilibrium point has been recorded for some time. 00132 # @param stale_time Time in seconds to pass to consider the EP stale. 00133 # @return True if the EP is stale. 00134 def is_ep_stale(self, stale_time=1.): 00135 with self.ep_lock: 00136 if self.ep is None: 00137 rospy.logwarn("[ep_arm_base] Equilibrium point not read yet.") 00138 return True 00139 return rospy.get_time() - self.ep_time > stale_time 00140 00141 ## 00142 # Method to be implemented by subclasses to record the equilibrium point. 00143 def _save_ep(self, ep): 00144 with self.ep_lock: 00145 self.ep_time = rospy.get_time() 00146 self.ep = ep 00147 00148 def create_ep_arm(arm_side, arm_type=EPArmBase, urdf_filename=None, **args): 00149 if urdf_filename is None: 00150 robot = URDF.load_from_parameter_server(verbose=False) 00151 else: 00152 robot = URDF.load_xml_file(urdf_filename, verbose=False) 00153 return arm_type(arm_side, robot, **args)