00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 import numpy as np
00033 
00034 import PyKDL as kdl
00035 import rospy
00036 
00037 from sensor_msgs.msg import JointState
00038 import hrl_geom.transformations as trans
00039 from hrl_geom.pose_converter import PoseConv
00040 from kdl_parser import kdl_tree_from_urdf_model
00041 from urdf_parser_py.urdf import Robot
00042 
00043 def create_kdl_kin(base_link, end_link, urdf_filename=None, description_param="robot_description"):
00044     if urdf_filename is None:
00045         robot = Robot.from_parameter_server(key=description_param)
00046     else:
00047         f = file(urdf_filename, 'r')
00048         robot = Robot.from_xml_string(f.read())
00049         f.close()
00050     return KDLKinematics(robot, base_link, end_link)
00051 
00052 
00053 
00054 
00055 
00056 class KDLKinematics(object):
00057     
00058     
00059     
00060     
00061     
00062     
00063     
00064     def __init__(self, urdf, base_link, end_link, kdl_tree=None):
00065         if kdl_tree is None:
00066             kdl_tree = kdl_tree_from_urdf_model(urdf)
00067         self.tree = kdl_tree
00068         self.urdf = urdf
00069 
00070         base_link = base_link.split("/")[-1] 
00071         end_link = end_link.split("/")[-1] 
00072         self.chain = kdl_tree.getChain(base_link, end_link)
00073         self.base_link = base_link
00074         self.end_link = end_link
00075 
00076         
00077         self.joint_limits_lower = []
00078         self.joint_limits_upper = []
00079         self.joint_safety_lower = []
00080         self.joint_safety_upper = []
00081         self.joint_types = []
00082         for jnt_name in self.get_joint_names():
00083             jnt = urdf.joint_map[jnt_name]
00084             if jnt.limit is not None:
00085                 self.joint_limits_lower.append(jnt.limit.lower)
00086                 self.joint_limits_upper.append(jnt.limit.upper)
00087             else:
00088                 self.joint_limits_lower.append(None)
00089                 self.joint_limits_upper.append(None)
00090             if jnt.safety_controller is not None:
00091                 self.joint_safety_lower.append(jnt.safety_controller.soft_lower_limit)
00092                 self.joint_safety_upper.append(jnt.safety_controller.soft_upper_limit)
00093             elif jnt.limit is not None:
00094                 self.joint_safety_lower.append(jnt.limit.lower)
00095                 self.joint_safety_upper.append(jnt.limit.upper)
00096             else:
00097                 self.joint_safety_lower.append(None)
00098                 self.joint_safety_upper.append(None)
00099             self.joint_types.append(jnt.type)
00100         def replace_none(x, v):
00101             if x is None:
00102                 return v
00103             return x
00104         self.joint_limits_lower = np.array([replace_none(jl, -np.inf)
00105                                             for jl in self.joint_limits_lower])
00106         self.joint_limits_upper = np.array([replace_none(jl, np.inf)
00107                                             for jl in self.joint_limits_upper])
00108         self.joint_safety_lower = np.array([replace_none(jl, -np.inf)
00109                                             for jl in self.joint_safety_lower])
00110         self.joint_safety_upper = np.array([replace_none(jl, np.inf)
00111                                             for jl in self.joint_safety_upper])
00112         self.joint_types = np.array(self.joint_types)
00113         self.num_joints = len(self.get_joint_names())
00114 
00115         self._fk_kdl = kdl.ChainFkSolverPos_recursive(self.chain)
00116         self._ik_v_kdl = kdl.ChainIkSolverVel_pinv(self.chain)
00117         self._ik_p_kdl = kdl.ChainIkSolverPos_NR(self.chain, self._fk_kdl, self._ik_v_kdl)
00118         self._jac_kdl = kdl.ChainJntToJacSolver(self.chain)
00119         self._dyn_kdl = kdl.ChainDynParam(self.chain, kdl.Vector.Zero())
00120 
00121     def extract_joint_state(self, js, joint_names=None):
00122         if joint_names is None:
00123             joint_names = self.get_joint_names()
00124         q   = np.zeros(len(joint_names))
00125         qd  = np.zeros(len(joint_names))
00126         eff = np.zeros(len(joint_names))
00127         for i, joint_name in enumerate(joint_names):
00128             js_idx = js.name.index(joint_name)
00129             if js_idx < len(js.position) and q is not None:
00130                 q[i]   = js.position[js_idx]
00131             else:
00132                 q = None
00133             if js_idx < len(js.velocity) and qd is not None:
00134                 qd[i]  = js.velocity[js_idx]
00135             else:
00136                 qd = None
00137             if js_idx < len(js.effort) and eff is not None:
00138                 eff[i] = js.effort[js_idx]
00139             else:
00140                 eff = None
00141         return q, qd, eff
00142 
00143     
00144     
00145     def get_link_names(self, joints=False, fixed=True):
00146         return self.urdf.get_chain(self.base_link, self.end_link, joints, fixed)
00147 
00148     
00149     
00150     def get_joint_names(self, links=False, fixed=False):
00151         return self.urdf.get_chain(self.base_link, self.end_link,
00152                                    links=links, fixed=fixed)
00153 
00154     def get_joint_limits(self):
00155         return self.joint_limits_lower, self.joint_limits_upper
00156 
00157     def FK(self, q, link_number=None):
00158         if link_number is not None:
00159             end_link = self.get_link_names(fixed=False)[link_number]
00160         else:
00161             end_link = None
00162         homo_mat = self.forward(q, end_link)
00163         pos, rot = PoseConv.to_pos_rot(homo_mat)
00164         return pos, rot
00165 
00166 
00167     
00168     
00169     
00170     
00171     
00172     
00173     
00174     def forward(self, q, end_link=None, base_link=None):
00175         link_names = self.get_link_names()
00176         if end_link is None:
00177             end_link = self.chain.getNrOfSegments()
00178         else:
00179             end_link = end_link.split("/")[-1]
00180             if end_link in link_names:
00181                 end_link = link_names.index(end_link)
00182             else:
00183                 print "Target segment %s not in KDL chain" % end_link
00184                 return None
00185         if base_link is None:
00186             base_link = 0
00187         else:
00188             base_link = base_link.split("/")[-1]
00189             if base_link in link_names:
00190                 base_link = link_names.index(base_link)
00191             else:
00192                 print "Base segment %s not in KDL chain" % base_link
00193                 return None
00194         base_trans = self._do_kdl_fk(q, base_link)
00195         if base_trans is None:
00196             print "FK KDL failure on base transformation."
00197         end_trans = self._do_kdl_fk(q, end_link)
00198         if end_trans is None:
00199             print "FK KDL failure on end transformation."
00200         return base_trans**-1 * end_trans
00201 
00202     def _do_kdl_fk(self, q, link_number):
00203         endeffec_frame = kdl.Frame()
00204         kinematics_status = self._fk_kdl.JntToCart(joint_list_to_kdl(q),
00205                                                    endeffec_frame,
00206                                                    link_number)
00207         if kinematics_status >= 0:
00208             p = endeffec_frame.p
00209             M = endeffec_frame.M
00210             return np.mat([[M[0,0], M[0,1], M[0,2], p.x()],
00211                            [M[1,0], M[1,1], M[1,2], p.y()],
00212                            [M[2,0], M[2,1], M[2,2], p.z()],
00213                            [     0,      0,      0,     1]])
00214         else:
00215             return None
00216 
00217     
00218     
00219     
00220     
00221     
00222     
00223     
00224     
00225     
00226     
00227     def inverse(self, pose, q_guess=None, min_joints=None, max_joints=None):
00228         pos, rot = PoseConv.to_pos_rot(pose)
00229         pos_kdl = kdl.Vector(pos[0,0], pos[1,0], pos[2,0])
00230         rot_kdl = kdl.Rotation(rot[0,0], rot[0,1], rot[0,2],
00231                                rot[1,0], rot[1,1], rot[1,2],
00232                                rot[2,0], rot[2,1], rot[2,2])
00233         frame_kdl = kdl.Frame(rot_kdl, pos_kdl)
00234         if min_joints is None:
00235             min_joints = self.joint_safety_lower
00236         if max_joints is None:
00237             max_joints = self.joint_safety_upper
00238         mins_kdl = joint_list_to_kdl(min_joints)
00239         maxs_kdl = joint_list_to_kdl(max_joints)
00240         ik_p_kdl = kdl.ChainIkSolverPos_NR_JL(self.chain, mins_kdl, maxs_kdl,
00241                                               self._fk_kdl, self._ik_v_kdl)
00242 
00243         if q_guess == None:
00244             
00245             lower_lim = np.where(np.isfinite(min_joints), min_joints, 0.)
00246             upper_lim = np.where(np.isfinite(max_joints), max_joints, 0.)
00247             q_guess = (lower_lim + upper_lim) / 2.0
00248             q_guess = np.where(np.isnan(q_guess), [0.]*len(q_guess), q_guess)
00249 
00250         q_kdl = kdl.JntArray(self.num_joints)
00251         q_guess_kdl = joint_list_to_kdl(q_guess)
00252         if ik_p_kdl.CartToJnt(q_guess_kdl, frame_kdl, q_kdl) >= 0:
00253             return np.array(joint_kdl_to_list(q_kdl))
00254         else:
00255             return None
00256 
00257     
00258     
00259     
00260     
00261     
00262     
00263     def inverse_search(self, pose, timeout=1.):
00264         st_time = rospy.get_time()
00265         while not rospy.is_shutdown() and rospy.get_time() - st_time < timeout:
00266             q_init = self.random_joint_angles()
00267             q_ik = self.inverse(pose, q_init)
00268             if q_ik is not None:
00269                 return q_ik
00270         return None
00271 
00272     
00273     
00274     
00275     
00276     
00277     
00278     def jacobian(self, q, pos=None):
00279         j_kdl = kdl.Jacobian(self.num_joints)
00280         q_kdl = joint_list_to_kdl(q)
00281         self._jac_kdl.JntToJac(q_kdl, j_kdl)
00282         if pos is not None:
00283             ee_pos = self.forward(q)[:3,3]
00284             pos_kdl = kdl.Vector(pos[0]-ee_pos[0], pos[1]-ee_pos[1],
00285                                   pos[2]-ee_pos[2])
00286             j_kdl.changeRefPoint(pos_kdl)
00287         return kdl_to_mat(j_kdl)
00288 
00289     
00290     
00291     
00292     
00293     def inertia(self, q):
00294         h_kdl = kdl.JntSpaceInertiaMatrix(self.num_joints)
00295         self._dyn_kdl.JntToMass(joint_list_to_kdl(q), h_kdl)
00296         return kdl_to_mat(h_kdl)
00297 
00298     
00299     
00300     
00301     
00302     def cart_inertia(self, q):
00303         H = self.inertia(q)
00304         J = self.jacobian(q)
00305         return np.linalg.inv(J * np.linalg.inv(H) * J.T)
00306 
00307     
00308     
00309     
00310     
00311     def joints_in_limits(self, q):
00312         lower_lim = self.joint_limits_lower
00313         upper_lim = self.joint_limits_upper
00314         return np.all([q >= lower_lim, q <= upper_lim], 0)
00315 
00316     
00317     
00318     
00319     
00320     def joints_in_safe_limits(self, q):
00321         lower_lim = self.joint_safety_lower
00322         upper_lim = self.joint_safety_upper
00323         return np.all([q >= lower_lim, q <= upper_lim], 0)
00324 
00325     
00326     
00327     
00328     
00329     def clip_joints_safe(self, q):
00330         lower_lim = self.joint_safety_lower
00331         upper_lim = self.joint_safety_upper
00332         return np.clip(q, lower_lim, upper_lim)
00333 
00334     
00335     
00336     
00337     def random_joint_angles(self):
00338         lower_lim = self.joint_safety_lower
00339         upper_lim = self.joint_safety_upper
00340         lower_lim = np.where(np.isfinite(lower_lim), lower_lim, -np.pi)
00341         upper_lim = np.where(np.isfinite(upper_lim), upper_lim, np.pi)
00342         zip_lims = zip(lower_lim, upper_lim)
00343         return np.array([np.random.uniform(min_lim, max_lim) for min_lim, max_lim in zip_lims])
00344 
00345     
00346     
00347     
00348     
00349     
00350     
00351     def difference_joints(self, q1, q2):
00352         diff = np.array(q1) - np.array(q2)
00353         diff_mod = np.mod(diff, 2 * np.pi)
00354         diff_alt = diff_mod - 2 * np.pi
00355         for i, continuous in enumerate(self.joint_types == 'continuous'):
00356             if continuous:
00357                 if diff_mod[i] < -diff_alt[i]:
00358                     diff[i] = diff_mod[i]
00359                 else:
00360                     diff[i] = diff_alt[i]
00361         return diff
00362 
00363     
00364     
00365     
00366     def inverse_biased(self, pose, q_init, q_bias, q_bias_weights, rot_weight=1.,
00367                        bias_vel=0.01, num_iter=100):
00368         
00369         q_out = np.mat(self.inverse_search(pose)).T
00370         for i in range(num_iter):
00371             pos_fk, rot_fk = PoseConv.to_pos_rot(self.forward(q_out))
00372             delta_twist = np.mat(np.zeros((6, 1)))
00373             pos_delta = pos - pos_fk
00374             delta_twist[:3,0] = pos_delta
00375             rot_delta = np.mat(np.eye(4))
00376             rot_delta[:3,:3] = rot * rot_fk.T
00377             rot_delta_angles = np.mat(trans.euler_from_matrix(rot_delta)).T
00378             delta_twist[3:6,0] = rot_delta_angles
00379             J = self.jacobian(q_out)
00380             J[3:6,:] *= np.sqrt(rot_weight)
00381             delta_twist[3:6,0] *= np.sqrt(rot_weight)
00382             J_tinv = np.linalg.inv(J.T * J + np.diag(q_bias_weights) * np.eye(len(q_init))) * J.T
00383             q_bias_diff = q_bias - q_out
00384             q_bias_diff_normed = q_bias_diff * bias_vel / np.linalg.norm(q_bias_diff)
00385             delta_q = q_bias_diff_normed + J_tinv * (delta_twist - J * q_bias_diff_normed)
00386             q_out += delta_q
00387             q_out = np.mat(self.clip_joints_safe(q_out.T.A[0])).T
00388         return q_out
00389 
00390     
00391     
00392     def inverse_biased_search(self, pos, rot, q_bias, q_bias_weights, rot_weight=1.,
00393                               bias_vel=0.01, num_iter=100, num_search=20):
00394         
00395         q_sol_min = []
00396         min_val = 1000000.
00397         for i in range(num_search):
00398             q_init = self.random_joint_angles()
00399             q_sol = self.inverse_biased(pos, rot, q_init, q_bias, q_bias_weights, rot_weight=1.,
00400                                         bias_vel=bias_vel, num_iter=num_iter)
00401             cur_val = np.linalg.norm(np.diag(q_bias_weights) * (q_sol - q_bias))
00402             if cur_val < min_val:
00403                 min_val = cur_val
00404                 q_sol_min = q_sol
00405         return q_sol_min
00406 
00407 
00408 def kdl_to_mat(m):
00409     mat =  np.mat(np.zeros((m.rows(), m.columns())))
00410     for i in range(m.rows()):
00411         for j in range(m.columns()):
00412             mat[i,j] = m[i,j]
00413     return mat
00414 
00415 def joint_kdl_to_list(q):
00416     if q == None:
00417         return None
00418     return [q[i] for i in range(q.rows())]
00419 
00420 def joint_list_to_kdl(q):
00421     if q is None:
00422         return None
00423     if type(q) == np.matrix and q.shape[1] == 0:
00424         q = q.T.tolist()[0]
00425     q_kdl = kdl.JntArray(len(q))
00426     for i, q_i in enumerate(q):
00427         q_kdl[i] = q_i
00428     return q_kdl
00429 
00430 def main():
00431     import sys
00432     def usage():
00433         print("Tests for kdl_parser:\n")
00434         print("kdl_parser <urdf file>")
00435         print("\tLoad the URDF from file.")
00436         print("kdl_parser")
00437         print("\tLoad the URDF from the parameter server.")
00438         sys.exit(1)
00439 
00440     if len(sys.argv) > 2:
00441         usage()
00442     if len(sys.argv) == 2 and (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
00443         usage()
00444     if (len(sys.argv) == 1):
00445         robot = Robot.from_parameter_server()
00446     else:
00447         f = file(sys.argv[1], 'r')
00448         robot = Robot.from_xml_string(f.read())
00449         f.close()
00450 
00451     if True:
00452         import random
00453         base_link = robot.get_root()
00454         end_link = robot.link_map.keys()[random.randint(0, len(robot.link_map)-1)]
00455         print "Root link: %s; Random end link: %s" % (base_link, end_link)
00456         kdl_kin = KDLKinematics(robot, base_link, end_link)
00457         q = kdl_kin.random_joint_angles()
00458         print "Random angles:", q
00459         pose = kdl_kin.forward(q)
00460         print "FK:", pose
00461         q_new = kdl_kin.inverse(pose)
00462         print "IK (not necessarily the same):", q_new
00463         if q_new is not None:
00464             pose_new = kdl_kin.forward(q_new)
00465             print "FK on IK:", pose_new
00466             print "Error:", np.linalg.norm(pose_new * pose**-1 - np.mat(np.eye(4)))
00467         else:
00468             print "IK failure"
00469         J = kdl_kin.jacobian(q)
00470         print "Jacobian:", J
00471         M = kdl_kin.inertia(q)
00472         print "Inertia matrix:", M
00473         if False:
00474             M_cart = kdl_kin.cart_inertia(q)
00475             print "Cartesian inertia matrix:", M_cart
00476 
00477     if True:
00478         rospy.init_node("kdl_kinematics")
00479         num_times = 20
00480         while not rospy.is_shutdown() and num_times > 0:
00481             base_link = robot.get_root()
00482             end_link = robot.link_map.keys()[random.randint(0, len(robot.link_map)-1)]
00483             print "Root link: %s; Random end link: %s" % (base_link, end_link)
00484             kdl_kin = KDLKinematics(robot, base_link, end_link)
00485             q = kdl_kin.random_joint_angles()
00486             pose = kdl_kin.forward(q)
00487             q_guess = kdl_kin.random_joint_angles()
00488             q_new = kdl_kin.inverse(pose, q_guess)
00489             if q_new is None:
00490                 print "Bad IK, trying search..."
00491                 q_search = kdl_kin.inverse_search(pose)
00492                 pose_search = kdl_kin.forward(q_search)
00493                 print "Result error:", np.linalg.norm(pose_search * pose**-1 - np.mat(np.eye(4)))
00494             num_times -= 1
00495 
00496 if __name__ == "__main__":
00497     main()