00001 # 00002 # Any robot that wants to use EPC should implement the functions 00003 # sketched out in the HRLArm and HRLArmKinematics 00004 # 00005 # Standards: 00006 # pos is a 3x1 numpy matrix representing a position 00007 # rot is a 3x3 numpy matrix represetning a rotation 00008 # All values are computed in the arm's reference frame (frame 0) 00009 # 00010 00011 import numpy as np, math 00012 import copy 00013 from threading import RLock 00014 00015 ## 00016 # Abstract class which represents a basic structure for controlling 00017 # an arm. An equilibrium point (ep) represents a commanded goal for the 00018 # arm which is passed to the underlying controller. A basic assumption of 00019 # equilibrium point control is that the underlying controller doesn't necessarily 00020 # reach the arm commanded goal due to contact, gravity, low gains in the controller, etc. 00021 # Thus we employ another layer of control which relocates the equilibrium point 00022 # based on the task at hand. 00023 class HRLArm(object): 00024 def __init__(self, kinematics): 00025 # object of class derived from HRLArmKinematics 00026 self.kinematics = kinematics 00027 self.ep = None # equilibrium point 00028 self.q = None # angles 00029 self.qdot = None # angular velocity 00030 self.lock = RLock() 00031 00032 #----------------- abstract functions --------------------- 00033 ## 00034 # Commands the arm to move to desired equilbrium point 00035 def set_ep(self, *args): 00036 raise RuntimeError('Unimplemented Function') 00037 00038 # publish different viz markers. 00039 def publish_rviz_markers(self): 00040 raise RuntimeError('Unimplemented Function') 00041 00042 ## 00043 # Resets the equilibrium point based on some estimate 00044 def reset_ep(self): 00045 raise RuntimeError('Unimplemented Function') 00046 #---------------------------------------------------------- 00047 00048 ## 00049 # Records the current joint angles. This must be updated regularly 00050 # by the child class. 00051 # @param q array-like of joint angles 00052 def set_joint_angles(self, q): 00053 with self.lock: 00054 self.q = copy.copy(q) 00055 00056 ## 00057 # Returns the current joint angles of the arm 00058 # @return array-like of joint angles 00059 def get_joint_angles(self): 00060 with self.lock: 00061 return copy.copy(self.q) 00062 00063 ## 00064 # Returns the current equilibrium point 00065 # @return equilibrium point 00066 def get_ep(self): 00067 with self.lock: 00068 return copy.copy(self.ep) 00069 00070 ## 00071 # Returns the current pose of the tooltip 00072 # @return (pos, rot) 00073 def get_end_effector_pose(self): 00074 q = self.get_joint_angles() 00075 if q is None: 00076 return None 00077 else: 00078 return self.kinematics.FK(q) 00079 00080 00081 ## 00082 # Abstract class containing kinematics interfaces for an arm 00083 class HRLArmKinematics(object): 00084 ## 00085 # @param n_jts Number of joints of the arm 00086 # @param tool_pos Postion offset of the tool from the last link in the kinematic chain 00087 # @param tool_rot Rotation offset of the tool from the last link in the kinematic chain 00088 def __init__(self, n_jts, arm_base_pos=np.matrix([0.]*3).T, arm_base_rot=np.matrix(np.eye(3)), 00089 tool_pos=np.matrix([0.]*3).T, tool_rot=np.matrix(np.eye(3))): 00090 self.n_jts = n_jts 00091 self.set_arm_base(arm_base_pos, arm_base_rot) 00092 self.set_tooltip(tool_pos, tool_rot) 00093 00094 #----------------- abstract functions --------------------- 00095 ## 00096 # Returns FK for the last link in the kinematic chain. 00097 # Must be implemented by child class. 00098 # @param q Joint angles in radians 00099 # @param link_number Link in the kinematic chain to return FK for 00100 # 0 represents the ground frame, 1 returns the first link's frame, ... 00101 # n_jts + 1 returns the tooltip's frame 00102 # @return pos (3x1) np matrix, rot (3x3) np matrix 00103 def FK_vanilla(self, q, link_number=None): 00104 raise RuntimeError('Unimplemented Function') 00105 00106 ## 00107 # Computes IK for the last link in the kinematic chain. 00108 # Must be implemented by child class. 00109 # @param pos Desired link position (3x1 np matrix) 00110 # @param rot Desired link rotation (3x3 np matrix) 00111 # @param q_guess Estimate of the desired joint angles which seeds the IK solver 00112 def IK_vanilla(self, pos, rot, q_guess=None): 00113 raise RuntimeError('Unimplemented Function') 00114 00115 ## 00116 # Computes Jacobian in the arm's frame given joint angles at the specified position. 00117 # Must be implemented by child class. 00118 # @param q Joint angles for Jacobian 00119 # @param pos Position at which the Jacobian is centered. If None, the tooltip 00120 # location from FK is used. 00121 # @return Jacobian 6xN np matrix 00122 def jacobian_vanilla(self, q, pos=None): 00123 raise RuntimeError('Unimplemented Function') 00124 #---------------------------------------------------------- 00125 00126 ## 00127 # Returns FK for the last link in the kinematic chain. 00128 # Must be implemented by child class 00129 # @param q Joint angles in radians 00130 # @param link_number Link in the kinematic chain to return FK for 00131 # 0 represents the ground frame, 1 returns the first link's frame, ... 00132 # n_jts + 1 (and None) returns the tooltip's frame 00133 # @return pos (3x1) np matrix, rot (3x3) np matrix 00134 def FK(self, q, link_number=None): 00135 if link_number == None or link_number > self.n_jts: 00136 use_end_affector = True 00137 link_number = None 00138 else: 00139 use_end_affector = False 00140 00141 pos, rot = self.FK_vanilla(q, link_number) 00142 00143 # offset arm base to FK end 00144 pos = self.arm_base_pos + self.arm_base_rot * pos 00145 rot = self.arm_base_rot * rot 00146 00147 if use_end_affector: 00148 # offset end location to tooltip 00149 tooltip_baseframe = rot * self.tooltip_pos 00150 pos += tooltip_baseframe 00151 rot = rot * self.tooltip_rot 00152 return pos, rot 00153 00154 ## 00155 # Computes IK for the tooltip. The desired location is first transformed 00156 # back into the last link's frame and IK is performed on that location. 00157 # @param pos Desired link position (3x1 np matrix) 00158 # @param rot Desired link rotation (3x3 np matrix) 00159 # @param q_guess Estimate of the desired joint angles which seeds the IK solver 00160 def IK(self, pos, rot, q_guess=None): 00161 base_pos = self.arm_base_rot.T * pos - self.arm_base_rot.T * self.arm_base_pos 00162 base_rot = self.arm_base_rot.T * rot 00163 last_link_pos = base_pos - base_rot * self.tooltip_rot.T * self.tooltip_pos 00164 last_link_rot = base_rot * self.tooltip_rot.T 00165 return self.IK_vanilla(last_link_pos, last_link_rot, q_guess) 00166 00167 ## 00168 # Computes Jacobian in the arm's frame given joint angles at the specified position. 00169 # @param q Joint angles for Jacobian 00170 # @param pos Position at which the Jacobian is centered. If None, the tooltip 00171 # location from FK is used. 00172 # @return Jacobian 6xN np matrix 00173 def jacobian(self, q, pos=None): 00174 if pos is None: 00175 pos = self.FK(q) 00176 return self.jacobian_vanilla(q, pos) 00177 00178 ## 00179 # Sets the offset of the first link in the kinematic chain 00180 # @param arm_base_pos Postion offset of the first link in the kinematic chain 00181 # @param arm_base_rot Rotation offset of the first link in the kinematic chain 00182 def set_arm_base(self, arm_base_pos, arm_base_rot=np.matrix(np.eye(3))): 00183 self.arm_base_pos = arm_base_pos 00184 self.arm_base_rot = arm_base_rot 00185 00186 ## 00187 # Sets the offset of the tool from the last link in the kinematic chain 00188 # @param tool_pos Postion offset of the tool from the last link in the kinematic chain 00189 # @param tool_rot Rotation offset of the tool from the last link in the kinematic chain 00190 def set_tooltip(self, tool_pos, tool_rot=np.matrix(np.eye(3))): 00191 self.tooltip_pos = tool_pos 00192 self.tooltip_rot = tool_rot 00193