MAGIKS  1.1
Manipulator General Inverse Kinematic Solver
 All Classes Namespaces Files Functions Variables Pages
pr2_kinematics.py
Go to the documentation of this file.
1 ## @file PR2_kinematics.py
2 # @brief Contains specific functions that define all geometric and kinematic parameters for PR2 robot
3 # @author Nima Ramezani Taghiabadi
4 #
5 # PhD Researcher
6 # Faculty of Engineering and Information Technology
7 # University of Technology Sydney (UTS)
8 # Broadway, Ultimo, NSW 2007, Australia
9 # Phone No. : +61(0) 4 5027 4611
10 # Email(1) : nima.ramezani@gmail.com
11 # Email(2) : Nima.RamezaniTaghiabadi@uts.edu.au
12 # @version 5.0
13 #
14 # Start Revision 02 June 2015
15 # Last Revision 02 June 2015
16 
17 '''
18 Changes from version 4.0:
19  1- Fixed Mode respected by functions:
20  set_target(), larm_endeffector_position(), rarm_endeffector_position()
21 
22  2- Function name changes:
23  larm_endeffector_position(): larm_end_position()
24  rarm_endeffector_position(): rarm_end_position()
25  larm_endeffector_orientation(): larm_end_orientation()
26  rarm_endeffector_orientation(): rarm_end_orientation()
27 
28 '''
29 
30 import copy, math, sys, numpy as np
31 import general_python as pygen
32 import pr2_arm_kinematics
33 
34 from interval import interval, inf, imath
35 from math_tools import general_math as gen
36 from math_tools.geometry import rotation as rotlib, trigonometry as trig, trajectory as trajlib
37 from math_tools.algebra import vectors_and_matrices as vecmat
38 
39 from magiks.magiks_core import general_magiks as genkin
40 
41 drc = math.pi/180.00
42 
43 default_ql = drc*np.array([-130.0, 69.0 , -180.0, - 131.0, -180.0, -130.0, -180.0, 0.8/drc, -1000.0, -1000.0, -180.0, -40.0, 60.0, - 44.0, -131.0, -180.0, -130.0, -180.0])
44 default_qh = drc*np.array([ 30.0, 170.0, 44.0, - 8.5, 180.0, 0.00, 180.0, 1.14/drc, 1000.0, 1000.0, 180.0, 130.0, 170.0, 180.0, - 8.5, 180.0, 0.00, 180.0])
45 all_control_modes = ['Fixed-Base', 'Free-Base']
46 
47 ## @brief Contains properties and methods managing the kinematics of the PR2 robot.
48 # This class contains all properties and methods required for forward and inverse kinematic
49 # computations and kinematic control of the PR2 robot in two control modes: Fixed-Base and Free-Base
50 # In fixed-base mode, the body of the robot is assumed fixed (No navigation) and only the arms can move
51 # while in free-base mode, the robot trunk is free to navigate.
52 # Each control mode can be set for one of the arms as the reference arm (Endeffector)
53 # Dual arm control (two endeffectors) is not supported in this version
54 # PR2 has 18 degrees of freedom. The Robot will have two arms that each of them contains its associated joint values
55 # in its \b config property.
56 class PR2(object):
57  ## The Class Constructor:
58  # @param a0 A float specifying the value of /f$ a_0 /f$ in the DH parameters of the arm
59  # @param b0 A float specifying the distance of line connecting the two arms from the center of robot base
60  # @param d2 A float specifying the value of /f$ d_2 /f$ in the DH parameters of the arm
61  # @param d4 A float specifying the value of /f$ d_4 /f$ in the DH parameters of the arm
62  # @param d7 A float specifying the length of the gripper
63  # @param l0 A float specifying half of the distance between the arms
64  # @param ql A numpy vector of size 18 containing the lower bounds of the arm joints
65  # @param qh A numpy vector of size 18 containing the upper bounds of the arm joints
66  def __init__(self, a0 = 0.1, b0 = 0.05, d2 = 0.4, d4 = 0.321, d7 = 0.168, l0 = 0.188, ql = default_ql, qh = default_qh, run_magiks = False):
67 
68  assert (len(ql) == 18) and (len(qh) == 18), "Error from " + __name__ + " Constructor" + ": ql and qh must be numpy vectors of size 18"
69  ## A numpy vector of size 7 containing the lower bounds of the arm joints
70  self.ql = ql
71 
72  ## A numpy vector of size 7 containing the upper bounds of the arm joints
73  self.qh = qh
74 
75  ## A numpy vector of size 18 containing a set of reference values for the joints.
76  #
77  # This parameter can be used as reference joint values for redundancy optimization.
78  # The redundancy will be used to set the joints as close as possible to these reference values.
79  # This property is by default set as the middle of joint ranges.
80  self.qm = (qh + ql)/2
81 
82  ## A numpy vector of size 18 containing the current values of the joints.
83  # This property should not be manipulated by the user. Use method Set_Config() to change this property.
84  # The joints are in the following order:
85  # q[0] : Right Arm-Shoulder Pan Joint,
86  # q[1] : Right Arm-Shoulder Lift Joint,
87  # q[2] : Right Arm-Upper Arm Roll Joint,
88  # q[3] : Right Arm-Elbow Flex Joint,
89  # q[4] : Right Arm-Forearm Roll Joint,
90  # q[5] : Right Arm-Wrist Flex Joint,
91  # q[6] : Right Arm-Wrist Roll Joint,
92  # q[7] : "h_ts" the telescoping prismatic joint that lifts up and shifts down the trunk and arms,
93  # q[8] : "X_BO" the X position of the robot base,
94  # q[9] : "Y_BO" the Y position of the robot base,
95  # q[10] : "tau" the rotation angle of the robot base around "z" axis,
96  # q[11] to q[17] are designated for the left arm in the same order as the right arm
97  self.q = (qh + ql)/2
98 
99  ## Specifies the objective function for redundancy optimization.
100  # this property should not be set by the user directly. Use method "self.set_ofuncode" to set this property
101  # code legend:
102  # 1: midrange distance
103  # 0: distance from current configuration
104  self.ofuncode = 1
105 
106  self.W = np.zeros(11)
107  for i in [0, 1, 2, 3, 5, 7]:
108  self.W[i] = 1.0/((qh[i] - ql[i])**2)
109 
110  ## An instance of class \ref packages.nima.robotics.kinematics.special_geometries.pr2.pr2_arm_kinematics.PR2_ARM()
111  # containing the kinematic properties and methods of the right arm
112  self.rarm = pr2_arm_kinematics.PR2_ARM(a0 = a0, d2 = d2, d4 = d4, ql = ql[0:7] , qh = qh[0:7] , W = self.W[0:7], run_magiks = run_magiks)
113 
114  ## An instance of class \ref packages.nima.robotics.kinematics.special_geometries.pr2.pr2_arm_kinematics.PR2_ARM()
115  # containing the kinematic properties and methods of the left arm
116  self.larm = pr2_arm_kinematics.PR2_ARM(a0 = a0, d2 = d2, d4 = d4, ql = ql[11:18], qh = qh[11:18], W = self.W[0:7], run_magiks = run_magiks)
117 
118  self.d7 = d7
119  self.b0 = b0
120  self.l0 = l0
121  self.p_EFR_WR = np.array([0.0, 0.0, self.d7])
122  self.p_EFL_WL = np.array([0.0, 0.0, self.d7])
123 
124  # sets all angles to midrange by default
125  self.set_config(self.qm)
126 
127  self.control_mode = "Fixed-Base" # Other Alternative: "Free-Base"
128 
129  ## A boolean specifying the reference arm for the endeffector.
130  # If True, the endeeffector is the end of left arm gripper, False if right arm
131  self.larm_reference = False
132 
133  ## A numpy vector of size 3 representing the desired position of the Endeffector.
134  # Recommended to be used as a read only property. Use function set_target() to change the value of this property
135  self.xd = None
136 
137  ## A \f$ 3 \times 3 \f$ rotation matrix specifying the desired endeffector orientation for the IK problem.
138  # Recommended not to be set directly. Always use function set_target() to change this property.
139  self.Rd = None
140 
141  ## Use this function to check if a given value for specific joint is in its feasibility range
142  # @param i An integer between 0 to 17 specifying the joint number to be checked
143  # @param qi A float parameter specifying the value for the i-th joint
144  # @return A boolean: True if the given joint angle qi is in feasible range for the i-th joint (within the specified joint limits for that joint)
145  def joint_in_range(self,i, qi):
146  '''
147  Example
148  '''
149  if i in range(0, 7):
150  return self.rarm.config.joint_in_range(i, qi)
151  elif i in range(11, 18):
152  return self.larm.config.joint_in_range(i - 11, qi)
153  else:
154  if i == 10: # if base rotation angle is selected, it should be taken into the standard range
155  qi = trig.angle_standard_range(qi)
156 
157  if abs(qi - self.ql[i]) < gen.epsilon:
158  qi = self.ql[i]
159  if abs(qi - self.qh[i]) < gen.epsilon:
160  qi = self.qh[i]
161 
162  return ((qi <= self.qh[i]) and (qi >= self.ql[i]))
163 
164  ## Use this function to check if all values of the given joint vector are in their feasibility range
165  # @param qd A numpy vector of size 18 containing the values to be checked
166  # @return A boolean: If The given joints "qd" are out of the range specified by properties: ql and qh, returns False, otherwise returns True
167  def all_joints_in_range(self, qd):
168  '''
169  Example
170  '''
171  #Are all the arm given joints in range ?
172  flag = self.rarm.config.all_joints_in_range(qd[0:7])
173  flag = flag and self.larm.config.all_joints_in_range(qd[11:18])
174 
175  for i in range(7, 11):
176  flag = flag and self.joint_in_range(i, qd[i])
177  return flag
178 
179  ## Dumps the given partial joint values in the given full set of PR2 joints and returns the modified full set of joints
180  # This function does change object configuration.
181  # @param q_partial A numpy vector of size 7 or 11. If size is 7, it should contain arm joint values
182  # and if 11, it should have arm joint values plus four trunk location parameters: \f$ h_{ts} = q[7] , x = q[8], y = q[9], \theta = q[10] \f$
183  # @param q_full A numpy vector of size 18 containing all joint values of PR2, if Null, current object configuration will be set
184  # @return A numpy vector of size 18 containing the full configuration of PR2 embedded q_partial
185  def full_config(self, q_partial, q_full = None , is_left_arm = False):
186  if q_full == None:
187  q_full = self.q
188  permit = type(q_full) in [np.ndarray, tuple, list]
189  permit = permit and (len(q_full) == 18)
190  assert permit, pygen.err_str(__name__, self.__class__.__name__,sys._getframe().f_code.co_name, 'q_full must be a tuple, numpy vector or array of size 18')
191 
192  if q_partial == None:
193  return None
194 
195  permit = type(q_partial) in [np.ndarray, tuple, list]
196  nqp = len(q_partial)
197  permit = permit and (nqp in [7,11])
198  assert permit, pygen.err_str(__name__, self.__class__.__name__,sys._getframe().f_code.co_name, 'q_partial must be a tuple, numpy vector or array of size 7 or 11')
199 
200  qf = copy.copy(q_full)
201  if nqp == 7:
202  if is_left_arm:
203  qf[11:18] = q_partial
204  else:
205  qf[0:7] = q_partial
206  else:
207  if is_left_arm:
208  qf[7:11] = q_partial[7:11]
209  qf[11:18] = q_partial[0:7]
210  else:
211  qf[0:11] = q_partial
212  return qf
213 
214  ## protected
215  def set_ofuncode(self, new_ofuncode):
216 
217  if (new_ofuncode > 2) or (new_ofuncode < 0):
218  print "set_ofuncode_error: The given objective function code is not supported"
219  return 0
220 
221  self.ofuncode = new_ofuncode
222  self.ofun_computed = False
224  self.div_phi_ofun_updated = False
225 
226  ## Use this function to find the current value of the objective function
227  # @param None
228  # @return An instance of type float containing the value of the objective function
230  if not self.ofun_computed:
231  self.ofun = self.objective_function_for(self.q[0:11])
232  self.ofun_computed = True
233  return self.ofun
234 
235  ## Sets the robot configuration to the desired given joint values
236  # @param qd A numpy array of 7 elements containing the values of the given joints
237  # @return A boolean: True if the given configuration is successfully set, False if the given configuration is not successfully set.
238  # (If False is returned, the configuration will not chenge)
239  def set_config(self, qd):
240 
241  if not len(qd) == 18: #If the class is initialized in "dual_arm" mode, this number should be 18
242  print "set_config error: Number of input elements must be 18 in dual arm mode"
243  return False
244 
245  if self.all_joints_in_range(qd) and self.rarm.set_config(qd[0:7]) and self.larm.set_config(qd[11:18]):
246  self.q[0:7] = trig.angles_standard_range(qd[0:7])
247  self.q[7:10] = qd[7:10]
248  self.q[10:18] = trig.angles_standard_range(qd[10:18])
249 
250  self.C = math.cos(self.q[10])
251  self.S = math.sin(self.q[10])
252 
253  self.p_BO = np.array([qd[8] , qd[9],0.0])
254  self.p_BL_BO = np.array([self.b0 , self.l0, qd[7]])
255  self.p_BR_BO = np.array([self.b0 ,- self.l0, qd[7]])
256 
257  self.R_B = np.array([[ self.C, - self.S, 0.0 ],
258  [ self.S, self.C, 0.0 ],
259  [ 0.0 , 0.0 , 1.0]])
260 
261  self.p_EFR = None
262  self.p_EFL = None
263  self.R_WR = None
264  self.R_WL = None
265  self.redun_jacob_updated = False
269  self.div_theta_ofun_updated = False
270  self.div_phi_ofun_updated = False
271  self.ofun_computed = False
273 
274  return True
275  else:
276  print "Error from PR2().set_config(): Given joints are not in their feasible range"
277  for i in range(18):
278  if qd[i] > self.qh[i]:
279  print "Joint No. ", i , " is ", qd[i], " greater than maximum: ", self.qh[i]
280  if qd[i] < self.ql[i]:
281  print "Joint No. ", i , " is ", qd[i], " lower than minimum: ", self.ql[i]
282  return False
283 
284  ## Sets the endeffector target to a desired position and orientation. The target must be specified for the end of gripper of the reference arm.
285  # If the object is in Fixed-Base mode, then the target should be given w.r.t. the base (torso).
286  # If the object is in Free-Base mode, then the target should be given w.r.t. the global(lab) frame.
287  # @param target_position A numpy array of 3 elements specifying the desired endeffector position
288  # @param target_orientation A \f$ 3 \times 3 \f$ numpy rotation matrix representing the desired endeffector orientation
289  # @return A boolean: True if the given pose is successfully set, False if the given pose is not successfully set because of an error
290  # (If False is returned, properties self.xd and self.RD will not chenge)
291  def set_target(self, target_position, target_orientation):
292  '''
293  sets the endeffector target to the given position and orientation
294  variables self.xd and self.Rd should not be manipulated by the user. Always use this function
295  '''
296  self.xd = np.copy(target_position)
297  self.Rd = np.copy(target_orientation)
298 
299  arm = self.reference_arm()
300  if self.larm_reference:
301  pe = self.p_EFL_WL
302  p0 = self.p_BL_BO
303  else:
304  pe = self.p_EFR_WR
305  p0 = self.p_BR_BO
306 
307  # Specify the relative target for the arm
308  '''
309  print "in ST: p_BR_BO = ", self.p_BR_BO
310 
311  print "rel_ef_pos[2] = ", -self.p_BR_BO[2] + target_position[2] - target_orientation[2,2]*self.d7
312 
313  relative_endeffector_position = - self.p_BR_BO + np.dot(self.R_B.T,target_position - self.p_BO - np.dot(target_orientation,self.p_EFR_WR))
314  print "rel_ef_pos[2] = ", relative_endeffector_position[2]
315  '''
316  assert self.control_mode in all_control_modes, pygen.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, self.control_mode + " is an invalid value for control mode")
317  if self.control_mode == 'Fixed-Base':
318  rel_end_position = - p0 + target_position - np.dot(target_orientation, pe)
319  rel_end_orientation = target_orientation
320  else:
321  rel_end_position = - p0 + np.dot(self.R_B.T,target_position - self.p_BO - np.dot(target_orientation, pe))
322  rel_end_orientation = np.dot(self.R_B.T, target_orientation)
323 
324  #set the relative pose target for the arm
325  arm.set_target(rel_end_position, rel_end_orientation)
326 
327  def set_posture(self, posture):
328  if posture == 'praying':
329  qr = np.array([ -6.85916588e-06, 1.59104071e+00, 1.04472774e-03, -2.49505670e-01, -3.26593628e-03, -3.35053472e-01, -1.65636744e-03])
330  ql = np.array([ -3.57102700e-04, 1.59100714e+00, -5.27852219e-04, -2.48833571e-01, 3.85286996e-03, -3.36172240e-01, 1.43031683e-03])
331  qd = np.copy(self.q)
332  qd[0:7] = ql
333  qd[11:18] = qr
334  self.set_config(qd)
335  elif posture == 'writing':
336  qd = np.array([ -1.26299826e-01, 1.77046412e+00, -1.02862191e+00, -1.36864905e+00, -2.31195189e+00, -1.29253137e+00,
337  1.71195615e-01, 8.02176174e-01, -2.12167293e-03, 2.32811863e-04, 7.05358701e-03, 4.01010384e-01,
338  2.44565260e+00, 7.10476515e-01, -1.30808585e+00, 1.15357810e+00, -4.49485156e-01, -2.46943329e+00])
339  self.set_config(qd)
340  elif posture == 'catch_the_board_with_right_hand':
341  qr = np.array([ 0.08842831, 2.34773988, 0.07718497, -1.32087472, -0.71994221, -1.02121596, -1.56155048])
342  qd = np.copy(self.q)
343  qd[11:18] = qr
344  self.set_config(qd)
345  else:
346  assert False, "Error from PR2.set_posture(): Unknown Posture"
347 
348  ## Finds all the feasible solutions of the Inverse Kinematic problem for given redundant parameter vector \f$ \phi \f$.
349  # @param phi Is a numpy vector of five elements containing the values of five redundant parameters in free-base mode.
350  # The values of phi specify the following parameters: \n
351  # phi[0] : The first joint angle q[0] (Shoulder-Pan joint angle) \n
352  # phi[1] : The \b r cylindrical coordinate of the wrist (wrist joint center) position
353  # w.r.t. the arm base (shoulder-pan joint center) in torso coordinate system:
354  # \f[
355  # r = p_x^2 + p_y^2
356  # \f] where \f$ p_x \f$ and \f$ p_y \f$ are the \b x and \b y cartesian coordinates of the wrist position
357  # w.r.t. the arm base in torso coordinate system. \n
358  # phi[2] : \f$ p_z \f$ or the \b z cartesian coordinate of the wrist position
359  # w.r.t. the arm base in torso coordinate system. \n
360  # phi[3] : The \b \f$ \psi \f$ cylindrical coordinate of the wrist (wrist joint center) position w.r.t. the arm base in torso CS:
361  # \f[
362  # \psi = \arctan( \frac {p_x} {p_y})
363  # \f] (\f$ p_x \f$ and \f$ p_y \f$ can be determined from phi[1] and phi[3]) \n
364  # phi[4] : \b \f$ \tau \f$ or the rotation angle of the trunc base around z axis
365  # @return An array of numpy vectors. Each numpy vector is a feasible solution and contains 11 elements.
366  # The first 7 elements are the arm joints(left arm if property larm_reference is True and right arm, otherwise)
367  # and the next 4 elements contain the values of non-arm degrees of freedom (q[7] to q[10])
368  # This function only returns the feasible IK solutions but does NOT set the configuration so the joint values do not change
369  # The output will be \b None if given redundant parameters are invalid or out of feasible range.
370  def all_IK_solutions(self, phi):
371  if not self.larm_reference:
372  jn = 0
373  arm = self.rarm
374  l0 = - self.l0
375  p_EF_W = self.p_EFR_WR
376  else:
377  jn = 7
378  arm = self.larm
379  l0 = self.l0
380  p_EF_W = self.p_EFL_WL
381 
382  if not self.joint_in_range(jn, phi[0]):
383  print "Error from "+__name__+func_name+": The first given redundant parameter (phi[0]) is out of feasible range"
384  return None
385 
386  if not self.joint_in_range(10, phi[4]):
387  print "Error from "+__name__+func_name+": The fifth given redundant parameter (phi[4], tau or trunc rotation angle) is out of feasible range"
388  return None
389 
390  # Todo: check later for phi[2]
391 
392  #keep the current target of the arm
393  save_arm_target_position = copy.copy(arm.xd)
394  save_arm_target_orientation = copy.copy(arm.Rd)
395 
396  # Set parameters based on the given redundancy vector phi:
397  r = phi[1]
398  pz = phi[2]
399  psi = phi[3]
400  tau = phi[4]
401 
402  # Calculate the relative endeffector position:
403 
404  px = r*math.sin(psi)
405  py = r*math.cos(psi)
406 
407  p_W_B = np.array([px, py, pz])
408 
409 
410  # Calculate the relative endeffector orientation:
411  R_B = rotlib.rot_z(tau) # orientation of robot base
412  R_W = self.Rd
413  p_EF = self.xd
414  R_W_B = np.dot(R_B.T, R_W)
415 
416  #set the new target for the arm associated with the given phi
417 
418  arm.set_target(p_W_B, R_W_B)
419 
420  #Find all the solutions for the arm IK
421  solution_set = []
422  arm_solution_set = arm.all_IK_solutions(phi[0])
423 
424  if len(arm_solution_set) == 0:
425  print "IK Error: No solution for the arm for given phi"
426  else:
427 
428  # Calculate the telescopic prismatic joint:
429 
430  h_ts = - pz + self.xd[2] - self.Rd[2,2]*self.d7
431 
432  #calculate the base position:
433 
434  p_B_BO = np.array([self.b0 , l0, h_ts])
435 
436  p_BO = p_EF - np.dot(R_B,(p_B_BO + p_W_B)) - np.dot(R_W, p_EF_W)
437 
438  assert gen.equal(p_BO[2], 0.0) # z coordinate of the base must be zero
439 
440  # Now we have all the joints(all 11 degrees of freedom) insert extra DOFs to the arm solution set
441 
442  for arm_solution in arm_solution_set:
443  solution = np.zeros(11)
444  solution[0:7] = arm_solution
445  solution[7] = h_ts # append q[7] = h_ts
446  solution[8] = p_BO[0] # append q[8] = X_BO
447  solution[9] = p_BO[1] # append q[9] = Y_BO
448  solution[10] = tau # append q[10] = tau
449  solution_set.append(solution) # append the solution to the solution set
450 
451  #return back the previous target of the arm
452 
453  arm.set_target(save_arm_target_position, save_arm_target_orientation)
454 
455  return solution_set
456 
457  ## Use this function to get the value of the objective function for a given configuration \b \a q
458  # Why has this function been written?
459  # Sometimes you want to find the value of the objective function for a configuration but
460  # you don't want to set that configuration (you don't want to take the manipulator to that configuration)
461  # In this case, you can not use function "self.objective_function()" which gives the value of the objective
462  # function for the current configuration of the robot.
463  # @param q A numpy vector of size 11 containing the joint values of the reference arm followed by the four non-arm joints
464  # @return A float indicating the value of the objective function for the given q
465  def objective_function_for(self, q):
466 
467  if not self.larm_reference:
468  qc = self.q[0:11]
469  qm = self.qm[0:11]
470  else:
471  qc = np.append(self.q[11:18] , self.q[7:11])
472  qm = np.append(self.qm[11:18], self.qm[7:11])
473 
474  if self.ofuncode == 0:
475  delta = trig.angles_standard_range(q - qc)
476  ofun = np.dot(delta.T, self.W*delta)
477  elif self.ofuncode == 1:
478  delta = trig.angles_standard_range(q - qm)
479  ofun = np.dot(delta.T, self.W*delta)
480  else:
481  assert False, "Error from "+__name__ + func_name + ": Value "+str(self.ofuncode)+" for argument ofuncode is not supported"
482  return ofun
483 
484  ## Finds the solution of the Inverse Kinematic problem for given redundant parameter vector \f$ phi \f$ in free-base mode.
485  # In case of redundant solutions, the one corresponding to the lowest objective function is selected.
486  # Class property ofuncode specifies the objective function. legend for ofuncode:
487  # \li ofuncode = 0 (Default) the solution closest to current joint angles will be selected
488  # \li ofuncode = 1 the solution corresponding to the lowest midrange distance is selected
489  # This function does NOT set the configuration so the joints do not change
490  # @param phi A numpy vector of size 5 containing the values of the five \em redundant parameters
491  # @return A numpy vector of size 5 containing the IK solution corresponding to the given redundant parameters and the lowest value of the objective function.
492  # The first 7 elements contain the joints of the reference arm and the last four, are the values of the non-arm joints.
493  def IK_config(self, phi):
494  assert self.control_mode in all_control_modes
495  if self.control_mode == 'Fixed-Base':
496  assert type(phi) is float, pygen.err_str(__name__, self.__class__.__name__,sys._getframe().f_code.co_name, 'given redundant parameter must be a float in Fixed-Base mode')
497  arm = self.reference_arm()
498  q_arm = arm.IK_config(phi = phi)
499  return self.full_config(q_arm, is_left_arm = self.larm_reference)
500  else:
501  permit = type(phi) in [np.ndarray, float, list]
502  if permit:
503  permit = permit and len(phi) == 5
504  assert permit, pygen.err_str(__name__, self.__class__.__name__,sys._getframe().f_code.co_name, 'given redundant parameter must be an array of size 5 in Free-Base mode')
505  solution_set = self.all_IK_solutions(phi)
506  if len(solution_set) == 0:
507  print "IK_config error: No solution found within the feasible joint ranges for the given redundant parameters"
508  return None
509 
510  ofun_min = float("inf")
511  for i in range(0, len(solution_set)):
512  solution = solution_set[i]
513  ofun = self.objective_function_for(solution)
514 
515  if ofun < ofun_min:
516  ofun_min = ofun
517  i_min = i
518 
519  return self.full_config(solution_set[i_min], is_left_arm = self.larm_reference)
520 
521  ## Use this function to get the current position of the right gripper (Arm Endeffector) w.r.t the arm base.
522  # @param None
523  # @return A numpy vector of 3 elements containing the position of the right arm gripper w.r.t. the torso shoulder pan joint center.
525  R_WR_B = self.rarm.wrist_orientation()
526  p_WR_BR = self.rarm.wrist_position()
527  p_EFR_BR = p_WR_BR + np.dot(R_WR_B, self.p_EFR_WR)
528  return(p_EFR_BR)
529 
530  ## Use this function to get the current position of the left gripper (Arm Endeffector) w.r.t the arm base.
531  # @param None
532  # @return A numpy vector of 3 elements containing the position of the left arm gripper (endeffector position)
533  # w.r.t. the torso shoulder pan joint center.
535  R_WL_B = self.larm.wrist_orientation()
536  p_WL_BL = self.larm.wrist_position()
537  p_EFL_BL = p_WL_BL + np.dot(R_WL_B, self.p_EFL_WL)
538  return(p_EFL_BL)
539 
540  ## Use this function to get the current position of the left gripper (Arm Endeffector) w.r.t the torso.
541  # @param None
542  # @return A numpy vector of 3 elements containing the left arm gripper position vector
543  # (endeffector position) w.r.t. the torso at the origin
544  # \b Note: The torso origin is at the floor footprint (projection) of the middle point
545  # between the two shoulder pan joint centers.
547  p_EFL_BL = self.pos_larm_grip_wrt_tor_shpan()
548  p_EFL_BO = self.p_BL_BO + p_EFL_BL
549  return(p_EFL_BO)
550 
551  ## Use this function to get the current position of the right gripper (Arm Endeffector) w.r.t the torso.
552  # @param None
553  # @return A numpy vector of 3 elements containing the right arm gripper position vector
554  # (endeffector position) w.r.t. the torso at the origin
555  # \b Note: The torso origin is at the floor footprint (projection) of the middle point
556  # between the two shoulder pan joint centers.
558  p_EFR_BR = self.pos_rarm_grip_wrt_tor_shpan()
559  p_EFR_BO = self.p_BR_BO + p_EFR_BR
560  return(p_EFR_BO)
561 
562  ## Use this function to get the current global position of the right gripper (Arm Endeffector).
563  # @param None
564  # @return A numpy vector of 3 elements containing the global cartesian coordiantes of the end of right arm gripper.
565  def pos_rarm_grip(self):
566  p_EFR_BO = self.pos_rarm_grip_wrt_tor()
567  return(self.p_BO + p_EFR_BO)
568 
569  ## Use this function to get the current global position of the left gripper (Arm Endeffector).
570  # @param None
571  # @return A numpy vector of 3 elements containing the global cartesian coordiantes of the end of left arm gripper.
572  def pos_larm_grip(self):
573  p_EFL_BO = self.pos_larm_grip_wrt_tor()
574  return(self.p_BO + p_EFL_BO)
575 
576  ## Returns the current position of the endeffector or gripper end for the right arm.
577  # @param relative A boolean: If True, the gripper position is returned w.r.t. the base (torso), otherwise, w.r.t. the global coordinate system
578  # If relative is None (default value), it will be set to True if the current control mode is 'Fixed-Base' and False if otherwise
579  # @return A numpy vector of size 3, containing the current position of the right arm gripper endpoint.
580  def rarm_end_position(self, relative = None ):
581  assert self.control_mode in all_control_modes, pygen.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, self.control_mode + " is an invalid value for control mode")
582  relative = pygen.check_type(relative, [bool], __name__, self.__class__.__name__, sys._getframe().f_code.co_name, 'relative', default = self.control_mode == 'Fixed-Base')
583 
584  if relative:
585  return self.pos_rarm_grip_wrt_tor()
586  else:
587  if self.p_EFR == None:
588  self.R_WR = self.rarm_end_orientation(relative = False)
589  self.p_WR_BR = self.rarm.wrist_position()
590  self.p_EFR = self.p_BO + np.dot(self.R_B,(self.p_BR_BO + self.p_WR_BR)) + np.dot(self.R_WR, self.p_EFR_WR)
591  return copy.copy(self.p_EFR)
592 
593  ## Returns the current position of the endeffector or gripper end for the left arm.
594  # @param relative A boolean: If True, the gripper position is returned w.r.t. the base (torso), otherwise, w.r.t. the global coordinate system
595  # If relative is None (default value), it will be set to True if the current control mode is 'Fixed-Base' and False if otherwise
596  # @return A numpy vector of size 3, containing the current position of the left arm gripper endpoint.
597  def larm_end_position(self, relative = None):
598  pygen.check_valid(self.control_mode, all_control_modes, __name__, self.__class__.__name__, sys._getframe().f_code.co_name, 'control_mode')
599  relative = pygen.check_type(relative, [bool], __name__, self.__class__.__name__, sys._getframe().f_code.co_name, 'relative', default = self.control_mode == 'Fixed-Base')
600 
601  if relative:
602  return self.pos_larm_grip_wrt_tor()
603  else:
604  if self.p_EFL == None:
605  self.R_WL = self.larm_end_orientation(relative = False)
606  self.p_WL_BL = self.larm.wrist_position()
607  self.p_EFL = self.p_BO + np.dot(self.R_B,(self.p_BL_BO + self.p_WL_BL)) + np.dot(self.R_WL, self.p_EFL_WL)
608  return copy.copy(self.p_EFL)
609 
610  ## Returns the current position of the endeffector or gripper end for the reference arm.
611  # @param relative A boolean: If True, the gripper position is returned w.r.t. the base (torso), otherwise, w.r.t. the global coordinate system
612  # If relative is None (default value), it will be set to True if the current control mode is 'Fixed-Base' and False if otherwise
613  # @return A numpy vector of size 3, containing the current position of the reference arm gripper endpoint.
614  def end_position(self, relative = None):
615  if self.larm_reference:
616  return self.larm_end_position(relative = relative)
617  else:
618  return self.rarm_end_position(relative = relative)
619 
620  def rarm_end_orientation(self, relative = None):
621  pygen.check_valid(self.control_mode, all_control_modes, __name__, self.__class__.__name__, sys._getframe().f_code.co_name, 'control_mode')
622  relative = pygen.check_type(relative, [bool], __name__, self.__class__.__name__, sys._getframe().f_code.co_name, 'relative', default = self.control_mode == 'Fixed-Base')
623  '''
624  Returns the cartesian coordiantes of the right arm gripper as the endeffector frame.
625  '''
626 
627  if relative:
628  return self.rarm.wrist_orientation()
629  else:
630  if self.R_WR == None:
631  self.R_WR_B = self.rarm.wrist_orientation()
632  self.R_WR = np.dot(self.R_B, self.R_WR_B)
633  return copy.copy(self.R_WR)
634 
635  def larm_end_orientation(self, relative = None):
636  pygen.check_valid(self.control_mode, all_control_modes, __name__, self.__class__.__name__, sys._getframe().f_code.co_name, 'control_mode')
637  relative = pygen.check_type(relative, [bool], __name__, self.__class__.__name__, sys._getframe().f_code.co_name, 'relative', default = self.control_mode == 'Fixed-Base')
638  '''
639  Returns the cartesian coordiantes of the left arm gripper as the endeffector frame.
640  '''
641  if relative:
642  return self.larm.wrist_orientation()
643  else:
644  if self.R_WL == None:
645  self.R_WL_B = self.larm.wrist_orientation()
646  self.R_WL = np.dot(self.R_B, self.R_WL_B)
647  return copy.copy(self.R_WL)
648 
649  def end_orientation(self, relative = True):
650  if self.larm_reference:
651  return self.larm_end_orientation(relative = relative)
652  else:
653  return self.rarm_end_orientation(relative = relative)
654 
655  def div_theta_err(self):
656  '''
657  Returns the divergence of the vector of kinematic constraints "e" in respect with the joints
658  It is a 9*9 matrix
659  e_ij = rond e_i / rond q_j (for i,j = 0,..,8)
660  '''
661  if not self.larm_reference:
662  arm = self.rarm
663  l0 = self.l0
664  else:
665  arm = self.larm
666  l0 = - self.l0
667 
668  if not self.div_theta_err_updated:
669 
670  self.E = np.zeros((9,9))
671  self.F = np.zeros((9,5))
672 
673  phi = np.zeros(5)
674 
675  [s0, s1, s2, s3, s4, s5, s6] = arm.config.s
676  [c0, c1, c2, c3, c4, c5, c6] = arm.config.c
677 
678  tau = self.q[10]
679  C5 = math.cos(tau)
680  S5 = math.sin(tau)
681 
682  [s10] = arm.config.s1_mult1
683  [s20,s21, s22, s210] = arm.config.s2_mult1
684  [s30, s31, s32, s310, s320, s321, s322, s33,s3210] = arm.config.s3_mult1
685  [c0s0,c0s1,c0s2,c0s10,c0s20,c0s21,c0s30,c0s31,c0s32,c0s321] = arm.config.c0_mult
686  [c10, c1s0, c1s1, c1s2, c1s3, c1s10, c1s20,c1s30,c1s32,c1s320] = arm.config.c1_mult
687  [c20,c21,c22,c210,c2s0,c2s1,c2s2,c2s3,c2s10,c2s20,c2s30,c2s31,c2s310,c21s30,c20s31] = arm.config.c2_mult
688  [c30,c31,c32,c33,c3s0,c3s1,c3s2,c3s3,c3s10,c3s20,c3s21,c3s30,c310,c320,c321,c3210,c32s0,c32s10] = arm.config.c3_mult
689  [c21s0,c31s0,c321s0] = arm.config.s0_mult
690  [c10s1,c20s1,c30s1, c32s1,c320s1] = arm.config.s1_mult2
691  [c10s2,c20s2,c30s2, c32s2,c310s2] = arm.config.s2_mult2
692  [c10s3, c20s3, c30s3, c21s3, c210s3,s332] = arm.config.s3_mult2
693  [c10s32, c31s20, s54] = arm.config.cs_mult
694  [c54, c5s4, c5s5] = arm.config.c5_mult
695  [s64, s65, c4s6, c5s6, c54s6, s654] = arm.config.s6_mult
696  [c64, c65, c6s4, c6s5, c654, C6s54] = arm.config.c6_mult
697 
698  phi = self.redundant_parameters()
699 
700  Rd = self.end_orientation(relative = False)
701  xd = self.end_position(relative = False)
702 
703  [Q, Q2, d42, d44, a00, d22_plus_d44, foura00d44, alpha] = arm.additional_dims
704 
705  r2 = phi[1]**2
706  rho2 = r2 + phi[2]**2
707  R2 = rho2 - arm.l**2
708  a0 = arm.a0
709  d2 = arm.d2
710  d4 = arm.d4
711 
712  a = Rd[:,2]
713  n = Rd[:,0]
714  b = xd - self.d7*a
715 
716  ax_c5_m_ay_s5 = a[0]*C5 - a[1]*S5
717  ax_s5_p_ay_c5 = a[0]*S5 + a[1]*C5
718 
719  nx_c5_m_ny_s5 = n[0]*C5 - n[1]*S5
720  nx_s5_p_ny_c5 = n[0]*S5 + n[1]*C5
721 
722  C14 = math.cos(phi[0] + phi[3])
723  S14 = math.sin(phi[0] + phi[3])
724  C45 = math.cos(phi[4] + phi[3])
725  S45 = math.sin(phi[4] + phi[3])
726 
727  self.E[0,2] = - 2*d42*s3
728  self.E[2,1] = arm.d4*s321
729  self.E[3,4] = -s5
730  self.E[4,3] = c4*s5
731  self.E[4,4] = c5*s4
732 
733  self.E[5,4] = c65
734  self.E[5,5] = - s65
735  self.E[8,8] = 1.0
736  self.E[2,6] = 1.0
737 
738  self.E[1,1] = 2*a00*d44*s33*c2s2
739  self.E[1,2] = 2*a0*d42*s3*phi[1]*S14 + (a0**2)*(d4**2)*(s2**2)*(2*c3*s3)
740  # Alternative: self.E[1,2] = - s3*d42*(2*c3*d42 - R2) + 2*c3s3*(a0**2)*(d4**2)*(s2**2)
741 
742  self.E[6,6] = 1.0
743  self.F[6,2] = 1.0
744 
745  self.E[2,0] = - arm.d2*s1 - arm.d4*(c3s1 + c21s3)
746  self.E[2,2] = - arm.d4*(c1s3 + c32s1)
747  self.E[3,0] = ax_c5_m_ay_s5*(c20s31 - c310) + ax_s5_p_ay_c5*(c2s310 - c31s0) + a[2]*(c3s1 + c21s3)
748  self.E[3,1] = ax_c5_m_ay_s5*(c2s30 + c10s32) - ax_s5_p_ay_c5*(c20s3 - c1s320) - a[2]*s321
749  self.E[3,2] = ax_c5_m_ay_s5*(c3s20 - c3210 + c0s31) - ax_s5_p_ay_c5*(c30s2 + c321s0 - s310) + a[2]*(c1s3 + c32s1)
750  self.E[4,0] = - ax_c5_m_ay_s5*c0s21 - ax_s5_p_ay_c5*s210 - a[2]*c1s2
751  self.E[4,1] = ax_c5_m_ay_s5*(c210 -s20) + ax_s5_p_ay_c5*(c0s2 + c21s0) - a[2]*c2s1
752 
753  self.E[5,0] = nx_c5_m_ny_s5*(- c20s31 + c310) + nx_s5_p_ny_c5*(- c2s310 + c31s0) - n[2]*(c3s1 + c21s3)
754  self.E[5,1] = - nx_c5_m_ny_s5*(c2s30 + c10s32) + nx_s5_p_ny_c5*(c20s3 - c1s320) + n[2]*s321
755  self.E[5,2] = nx_c5_m_ny_s5*(- c3s20 + c3210 - c0s31) + nx_s5_p_ny_c5*(c30s2 + c321s0 - s310) - n[2]*(c1s3 + c32s1)
756  self.E[7,7] = 1.0
757  self.E[8,8] = 1.0
758 
759  self.F[0,2] = -2*phi[2]
760 
761  self.F[7,1] = S45
762  self.F[8,1] = C45
763 
764  self.F[0,0] = 2*arm.a0*phi[1]*C14
765  self.F[0,1] = 2*arm.a0*S14 - 2*phi[1]
766  self.F[0,3] = 2*arm.a0*phi[1]*C14
767 
768  self.F[1,1] = 2*phi[1]*arm.a0*(phi[1]*S14 - arm.a0)
769  self.F[1,2] = 2*phi[1]*arm.a0*S14*phi[2]
770 
771  self.F[3,0] = ax_c5_m_ay_s5 *(c0s32 + c21s30 + c3s10) + ax_s5_p_ay_c5*(s320 - c210s3 - c30s1)
772  self.F[3,4] = - ax_s5_p_ay_c5*(s320 - c210*s3 - c30*s1) - ax_c5_m_ay_s5*(c0*s32 + c21*s30 + c3s10)
773  self.F[4,0] = ax_c5_m_ay_s5*(c20 - c1s20) + ax_s5_p_ay_c5*(c2s0 + c10s2)
774  self.F[4,4] = - ax_c5_m_ay_s5*(c20 - c1s20) - ax_s5_p_ay_c5*(c2s0 + c10s2)
775  self.F[5,0] = - nx_c5_m_ny_s5*(c0s32 + c21s30 + c3s10) + nx_s5_p_ny_c5*(-s320 + c210s3 + c30s1)
776  self.F[5,4] = + nx_c5_m_ny_s5*(c0s32 + c21s30 + c3s10) - nx_s5_p_ny_c5*(-s320 + c210s3 + c30s1)
777  self.F[7,4] = phi[1]*C45 + C5*l0
778  self.F[8,4] = - phi[1]*S45 - S5*l0
779  self.F[7,3] = phi[1]*C45
780  self.F[8,3] = - phi[1]*S45
781 
782  self.div_theta_err_updated = True
783 
784  return copy.copy(self.E)
785 
786  ## Use this function to get the current values of the five redundant parameters of the PR2 robot in free-base mode.
788  '''
789  @return A numpy vector of 5 elements containing the redundant parameters phi according to the parametrization specified in <provide link>
790 
791  phi[0] = The first arm joint angle (Shoulder Pan joint)
792  phi[1] = The "r" coordinate of the relative wrist position(in cylinderical coordinates) phi[1]^2 = px^2 + py^2
793  phi[2] = The "z" coordinate of the relative wrist position(in cylinderical coordinates) phi[2] = pz
794  phi[3] = The "psi" coordinate of the relative wrist position(in cylinderical coordinates) phi[3] = arctan(x/y)
795  phi[4] = The rotation of the base. Same as "tau" or the 10th joint angle (phi[4] = q[10] = tau)
796  '''
797  if self.larm_reference:
798  arm = self.larm
799  else:
800  arm = self.rarm
801 
802  if not self.redundant_parameters_updated:
803  p = arm.wrist_position()
804  r2 = p[0]**2 + p[1]**2
805  self.phi = np.zeros(5)
806  self.phi[0] = arm.config.q[0]
807  self.phi[1] = math.sqrt(r2)
808  self.phi[2] = p[2]
809  self.phi[3] = math.atan2(p[0],p[1])
810  self.phi[4] = self.q[10]
811  self.redundant_parameters_updated = True
812  return copy.copy(self.phi)
813 
814  def div_phi_err(self):
815  '''
816  Returns the divergence of the vector of kinematic constraints "e" in respect with the redundant parameters "phi"
817  It is a 9*5 matrix
818  f_ij = rond e_i / rond phi_j (for i = 0,..,8 & j = 0,..,4)
819  the value of "self.F" is computed in function "div_theta_e" to avoid repeated calculations
820  '''
821  if not self.div_theta_err_updated:
822  self.div_theta_err()
823  return copy.copy(self.F)
824 
825  def redundancy_jacobian(self):
826  '''
827  Returns the redundancy jacobian
828  Redundancy Jacobian is the partial derivative of the joints in respect with redundant parameters
829  RJ is a 9*5 matrix because the first and the last joints (q[0] and q[10]) are themselves selected as the redundant parameters
830  '''
831  if self.larm_reference:
832  arm = self.larm
833  else:
834  arm = self.rarm
835 
836  if not self.redun_jacob_updated:
837 
838  d42 = arm.d2*arm.d4
839 
840  [s0, s1, s2, s3, s4, s5, s6] = arm.config.s
841  [c0, c1, c2, c3, c4, c5, c6] = arm.config.c
842 
843  s321 = s3*s2*s1
844  c65 = c6*c5
845  s65 = s6*s5
846 
847 
848  p = self.end_position()
849 
850  r2 = p[0]**2 + p[1]**2
851  p1 = math.sqrt(r2)
852  p2 = arm.xd[2]
853 
854 
855  C4 = math.cos(self.q[10])
856  S4 = math.sin(self.q[10])
857 
858  RJ = np.zeros((9,5))
859 
860  E = self.div_theta_err()
861  F = self.div_phi_err()
862 
863 
864  RJ[2,0] = F[0,0]/(2*d42*s3)
865  RJ[2,1] = F[0,1]/(2*d42*s3)
866  RJ[2,2] = -p2/(d42*s3)
867  RJ[2,3] = F[0,3]/(2*d42*s3)
868 
869  RJ[8,1] = -F[8,1]
870  RJ[8,3] = -F[8,3]
871  RJ[8,4] = -F[8,4]
872 
873  RJ[6,2] = - 1.0
874 
875  RJ[ 7 , 1 ] = -F[7,1]
876  RJ[ 7 , 3 ] = -F[7,3]
877  RJ[ 7 , 4 ] = -F[7,4]
878  '''
879  RJ[7,0] = -RJ[8,0]*E[7,8]/E[7,7]
880  RJ[7,1] = (2*p1 - RJ[8,1]*E[7,8])/E[7,7]
881  RJ[7,2] = -RJ[8,2]*E[7,8]/E[7,7]
882  RJ[7,3] = -RJ[8,3]*E[7,8]/E[7,7]
883  RJ[7,4] = -(F[7,4] + RJ[8,4]*E[7,8])/E[7,7]
884  '''
885  RJ[1,0] = -RJ[2,0]*E[1,2]/E[1,1]
886  RJ[1,1] = -(F[1,1] + RJ[2,1]*E[1,2])/E[1,1]
887  RJ[1,2] = -(F[1,2] + RJ[2,2]*E[1,2])/E[1,1]
888  RJ[1,3] = -RJ[2,3]*E[1,2]/E[1,1]
889  RJ[1,4] = -RJ[2,4]*E[1,2]/E[1,1]
890 
891  RJ[0,0] = -(RJ[6,0] + RJ[2,0]*E[2,2] + RJ[1,0]*arm.d4*s321)/E[2,0]
892  RJ[0,1] = -(RJ[6,1] + RJ[2,1]*E[2,2] + RJ[1,1]*arm.d4*s321)/E[2,0]
893  RJ[0,2] = -(RJ[6,2] + RJ[2,2]*E[2,2] + RJ[1,2]*arm.d4*s321)/E[2,0]
894  RJ[0,3] = -(RJ[6,3] + RJ[2,3]*E[2,2] + RJ[1,3]*arm.d4*s321)/E[2,0]
895  RJ[0,4] = -(RJ[6,4] + RJ[2,4]*E[2,2] + RJ[1,4]*arm.d4*s321)/E[2,0]
896 
897  RJ[4,0] = (F[3,0] + RJ[0,0]*E[3,0] + RJ[1,0]*E[3,1] + RJ[2,0]*E[3,2])/s5
898  RJ[4,1] = (RJ[0,1]*E[3,0] + RJ[1,1]*E[3,1] + RJ[2,1]*E[3,2])/s5
899  RJ[4,2] = (RJ[0,2]*E[3,0] + RJ[1,2]*E[3,1] + RJ[2,2]*E[3,2])/s5
900  RJ[4,3] = (RJ[0,3]*E[3,0] + RJ[1,3]*E[3,1] + RJ[2,3]*E[3,2])/s5
901  RJ[4,4] = (- F[3,0] + RJ[0,4]*E[3,0] + RJ[1,4]*E[3,1] + RJ[2,4]*E[3,2])/s5
902 
903  RJ[3,0] = -(F[4,0] + RJ[0,0]*E[4,0] + RJ[1,0]*E[4,1] + RJ[4,0]*c5*s4)/(c4*s5)
904  RJ[3,1] = -(RJ[0,1]*E[4,0] + RJ[1,1]*E[4,1] + RJ[4,1]*c5*s4)/(c4*s5)
905  RJ[3,2] = -(RJ[0,2]*E[4,0] + RJ[1,2]*E[4,1] + RJ[4,2]*c5*s4)/(c4*s5)
906  RJ[3,3] = -(RJ[0,3]*E[4,0] + RJ[1,3]*E[4,1] + RJ[4,3]*c5*s4)/(c4*s5)
907  RJ[3,4] = -(F[4,4] + RJ[0,4]*E[4,0] + RJ[1,4]*E[4,1] + RJ[4,4]*c5*s4)/(c4*s5)
908 
909  RJ[5,0] = (F[5,0] + RJ[0,0]*E[5,0] + RJ[1,0]*E[5,1] + RJ[2,0]*E[5,2] + RJ[4,0]*c65)/s65
910  RJ[5,1] = (RJ[0,1]*E[5,0] + RJ[1,1]*E[5,1] + RJ[2,1]*E[5,2] + RJ[4,1]*c65)/s65
911  RJ[5,2] = (RJ[0,2]*E[5,0] + RJ[1,2]*E[5,1] + RJ[2,2]*E[5,2] + RJ[4,2]*c65)/s65
912  RJ[5,3] = (RJ[0,3]*E[5,0] + RJ[1,3]*E[5,1] + RJ[2,3]*E[5,2] + RJ[4,3]*c65)/s65
913  RJ[5,4] = (- F[5,0] + RJ[0,4]*E[5,0] + RJ[1,4]*E[5,1] + RJ[2,4]*E[5,2] + RJ[4,4]*c65)/s65
914 
915  self.RJ = RJ
916  self.redun_jacob_updated = True
917  return copy.copy(self.RJ)
918 
919  def redundancy_jacobian_extended(self):
920  '''
921  returns the extended redundancy jacobian which is "11 x 5" matrix.
922  It is the redundancy jacobian with two rows inserted At its first and last rows.
923  These rows are corresponding to the first and last joints (shoulder pan joint and base rotation angle).
924  '''
925  if not self.redun_jacob_ext_updated:
926  self.ERJ = np.zeros((11,5))
927  RJ = self.redundancy_jacobian()
928  for i in range(9):
929  self.ERJ[i+1,:] = RJ[i,:]
930  self.ERJ[0,0] = 1.0
931  self.ERJ[10,4] = 1.0
932  self.redun_jacob_ext_updated = True
933  return copy.copy(self.ERJ)
934 
935  def div_theta_ofun(self):
936  '''
937  Returns the divergence of the objective function in respect with the joint angles
938  argument "ofun" specifies the code of the objective function which is set to 0 by default
939  '''
940  if not self.div_theta_ofun_updated:
941  self.DTG = np.zeros(11)
942  for i in range(11):
943  self.DTG[i] = 2*self.W[i]*(self.q[i] - self.qm[i])
944  self.div_theta_ofun_updated = True
945  return copy.copy(self.DTG)
946 
947  def div_phi_ofun(self):
948  '''
949  Returns the divergence of the objective function in respect with the redundant parameters "phi"
950  argument "ofun" specifies the code of the objective function which is set to 0 by default.
951  The code legend is like function "div_theta_ofun"
952  '''
953  if not self.div_phi_ofun_updated:
954  J = self.redundancy_jacobian_extended()
955  self.DFG = np.dot(J.T, self.div_theta_ofun())
956  self.div_phi_ofun_updated = True
957  return copy.copy(self.DFG)
958 
959  def joint_stepsize_interval(self, direction):
960  '''
961  Returns an interval for the step size by which the joints are allowed to move in the given "direction"
962  "direction" is a "n X 1" vector and specifies the direction of motion in the jointspace.
963  '''
964 
965  stepsize_interval = interval([-inf, inf])
966  for i in range(11): # for each joint
967  if self.W[i] != 0: # if the joint is limited
968  joint_correction_interval = interval([self.ql[i], self.qh[i]]) - interval(self.q[i]) # find the interval for joint correction
969  else:
970  joint_correction_interval = interval([-inf, inf])
971 
972  stepsize_interval = stepsize_interval & joint_correction_interval/direction[i]
973 
974  '''
975  print "direction : = ", direction[i]
976  print "stepsize_interval: = ", stepsize_interval
977  print
978  '''
979 
980  return stepsize_interval
981 
982  def optimum_joint_stepsize(self, direction, safety_factor = 0.99):
983  '''
984  returns the optimum feasible step size that minimizes the objective function
985  if the joints are constrained to move in the given "direction".
986  The "safety_factor" specifies how much we can approach to the borders of joint limits
987  '''
988  if not self.larm_reference:
989  qc = self.q[0:11]
990  qm = self.qm[0:11]
991  else:
992  qc = np.append(self.q[11:18] , self.q[7:11])
993  qm = np.append(self.qm[11:18], self.qm[7:11])
994 
995  den = np.dot(direction.T, self.W * direction)
996  num = np.dot(direction.T, self.W * (qc - qm))
997 
998  if not gen.equal(num, 0.0):
999  eta = - num/den
1000  else:
1001  print "Error from PR2.optim_joint_stepsize(): Moving in the given direction does not influence the objective function"
1002  return None
1003 
1004  interval_eta = self.joint_stepsize_interval(direction)
1005 
1006  (eta_l, eta_h) = interval_eta[0]
1007 
1008  if eta < eta_l:
1009  return safety_factor*eta_l
1010  elif eta < eta_h:
1011  return eta
1012  else:
1013  return safety_factor*eta_h
1014 
1015  def steepest_descent_redundancy_correction(self):
1016  '''
1017  Finds the steepest descent direction of the objective function for redundancy vector "phi"
1018  '''
1019  steepest_descent_redundancy_direction = - self.div_phi_ofun()
1020  steepest_descent_joint_direction = np.dot(self.redundancy_jacobian_extended(), steepest_descent_redundancy_direction)
1021 
1022  eta = self.optimum_joint_stepsize(steepest_descent_joint_direction)
1023 
1024 
1025  return eta*steepest_descent_redundancy_direction
1026 
1027  def endeffector_in_target(self):
1028  '''
1029  Returns true is endeffector is in target
1030  '''
1031  self.in_target = vecmat.equal(self.xd, self.end_position()) and vecmat.equal(self.Rd, self.end_orientation())
1032  return self.in_target
1033 
1034  def optimize_config(self, silent = True):
1035  '''
1036  Assuming the endeffector is in target,
1037  this function changes the robot configuration without changing the endeffector pose
1038  until the minimum possible value of objective function is achieved
1039  Note:
1040  This function is a procedure and does not return any thing. Running this function, changes the robot configuration.
1041  If the current tip pose is not in target, this function fails with an error
1042  '''
1043  if (self.xd == None) or (self.Rd == None):
1044  self.set_target(self.end_position(), self.end_orientation())
1045 
1046  assert self.endeffector_in_target(), "optimize_config Error: Endeffector is not in target pose"
1047 
1048  ofun_old = self.objective_function()
1049  k = 1.0
1050 
1051  while k > 0.001:
1052  phi = self.redundant_parameters()
1053  # Find the steepest descent correction for redundancy:
1054  delta_phi = self.steepest_descent_redundancy_correction()
1055  # Correct the redundancy
1056  phi = phi + k*delta_phi
1057  # Run IK to find the joints with the corrected redundancy:
1058  solution = self.IK_config(phi)
1059  if solution == None:
1060  fail = True
1061  else:
1062  # Solution exists: Compute the value of the objective function for the new configuration
1063  ofun_new = self.objective_function_for(solution)
1064  pygen.show("Objective Function Value: ", ofun_new, silent)
1065  if ofun_new > ofun_old - gen.epsilon:
1066  fail = True
1067  else:
1068  # Objective Function Reduced: Try to set the solution config
1069  qd = np.copy(self.q)
1070  qd[0:11] = solution
1071  if not self.set_config(qd):
1072  fail = True
1073  else:
1074  # Solution is feasible :-)
1075  fail = False
1076 
1077  if fail:
1078  k = k/2
1079  else:
1080  k = 1.0
1081 
1082  def extended_config(self, q):
1083  qq = np.copy(self.q)
1084  if not self.larm_reference:
1085  qq[0:11] = q
1086  else:
1087  qq[7:11 ] = q[7:11]
1088  qq[11:18] = q[0:7]
1089  return qq
1090 
1091  def goto_target(self):
1092  '''
1093  Finds the inverse kinematic solution closest to the current configuration
1094  The new joint angles will be set if all the kinematic equations are satisfied.
1095  All kinematic parameters will be updated.
1096  THIS FUNCTION IS NOT COMPLETE AND i NEED TO WORK ON IT MORE ...
1097  '''
1098  arm = self.reference_arm()
1099 
1100  if self.control_mode == "Fixed-Base":
1101  if not arm.goto_target(optimize = True):
1102  print "Error from goto_target: No soluton for the arm in fixed mode"
1103  return False
1104  else:
1105  if not self.larm_reference:
1106  self.q[0:7] = arm.config.q
1107  else:
1108  self.q[11:18] = arm.config.q
1109 
1110  self.set_config(self.q)
1111  return True
1112  elif self.control_mode == "Free-Base":
1113  print "Error from PR2().goto_target: Free-Base mode is not supported yet."
1114  return False
1115  else:
1116  print "Error from PR2().goto_target: Unknown control mode"
1117  return False
1118 
1119  def project_to_js(self, pos_traj, ori_traj = None, phi_start = 0.0, phi_end = None, delta_phi = 0.1, relative = True):
1120  '''
1121  projects the given taskspace pose trajectory into the jointspace
1122  The phase starts from phi_start and added by delta_phi in each step.
1123  at any time, if a solution is not found, the process stops
1124  '''
1125  if phi_end == None:
1126  phi_end = pos_traj.phi_end
1127 
1128  if phi_end > pos_traj.phi_end:
1129  phi_end = pos_traj.phi_end
1130 
1131  if ori_traj == None:
1132  ori_traj = trajlib.Orientation_Trajectory()
1133  ori_traj.current_orientation = self.end_orientation()
1134 
1135  jt = trajlib.Polynomial_Trajectory(dimension = 18)
1136  jt.capacity = 5
1137  jt.add_point(phi = 0.0, pos = np.copy(self.q), vel = np.zeros(18))
1138 
1139  phi = phi_start
1140  pos_traj.set_phi(phi)
1141  ori_traj.set_phi(phi)
1142  if relative:
1143  p0 = self.end_position() - pos_traj.current_position
1144  R0 = np.dot(self.end_orientation(), ori_traj.current_orientation.T)
1145  else:
1146  p0 = np.zeros(3)
1147  R0 = np.eye(3)
1148 
1149  phi = phi + delta_phi
1150  stay = True
1151 
1152  while (phi <= phi_end) and stay:
1153  if phi == phi_end:
1154  stay = False
1155  pos_traj.set_phi(phi)
1156  ori_traj.set_phi(phi)
1157  p = p0 + pos_traj.current_position
1158  R = np.dot(R0, ori_traj.current_orientation)
1159  self.set_target(p, R)
1160  if self.goto_target():
1161  jt.add_point(phi = phi - phi_start, pos = np.copy(self.q))
1162  phi = phi + delta_phi
1163  if phi > phi_end:
1164  phi = phi_end
1165  else:
1166  stay = False
1167 
1168  jt.interpolate()
1169 
1170  return jt
1171 
1172  def project_to_ts(self, js_traj, phi_start = 0.0, phi_end = None, delta_phi = 0.1):
1173  '''
1174  projects the given jointspace trajectory into the taskspace
1175  The phase starts from phi_start and added by delta_phi in each step.
1176  if at any time the joint values are not feasible, the process is stopped.
1177  '''
1178  if phi_end == None:
1179  phi_end = js_traj.phi_end
1180 
1181  ori_traj = trajlib.Orientation_Trajectory_Segment()
1182  ori_traj.capacity = 200
1183  pos_traj = trajlib.Polynomial_Trajectory()
1184  if phi_end > js_traj.phi_end:
1185  phi_end = js_traj.phi_end
1186 
1187  phi = phi_start
1188  js_traj.set_phi(phi)
1189 
1190  while (phi < phi_end) and (self.set_config(js_traj.current_position)):
1191  pos_traj.add_point(phi - phi_start, self.end_position())
1192  # print self.end_position()
1193  ori_traj.add_point(phi - phi_start, self.end_orientation())
1194  phi = phi + delta_phi
1195  if phi > phi_end:
1196  phi = phi_end
1197  js_traj.set_phi(phi)
1198 
1199  pos_traj.add_point(phi - phi_start, self.end_position())
1200  ori_traj.add_point(phi - phi_start, self.end_orientation())
1201  return (pos_traj, ori_traj)
1202 
1203 
def full_config
Dumps the given partial joint values in the given full set of PR2 joints and returns the modified ful...
Rd
A rotation matrix specifying the desired endeffector orientation for the IK problem.
def objective_function_for
Use this function to get the value of the objective function for a given configuration q Why has this...
def larm_end_position
Returns the current position of the endeffector or gripper end for the left arm.
def objective_function
Use this function to find the current value of the objective function.
xd
A numpy vector of size 3 representing the desired position of the Endeffector.
def all_joints_in_range
Use this function to check if all values of the given joint vector are in their feasibility range...
larm_reference
A boolean specifying the reference arm for the endeffector.
def end_position
Returns the current position of the endeffector or gripper end for the reference arm.
def pos_rarm_grip_wrt_tor_shpan
Use this function to get the current position of the right gripper (Arm Endeffector) w...
def set_target
Sets the endeffector target to a desired position and orientation.
Contains properties and methods managing the kinematics of the PR2 robot.
def all_IK_solutions
Finds all the feasible solutions of the Inverse Kinematic problem for given redundant parameter vecto...
def redundant_parameters
Use this function to get the current values of the five redundant parameters of the PR2 robot in free...
def set_config
Sets the robot configuration to the desired given joint values.
def pos_larm_grip_wrt_tor_shpan
Use this function to get the current position of the left gripper (Arm Endeffector) w...
def joint_in_range
Use this function to check if a given value for specific joint is in its feasibility range...
rarm
An instance of class packages.nima.robotics.kinematics.special_geometries.pr2.pr2_arm_kinematics.PR2_ARM() containing the kinematic properties and methods of the right arm.
Contains properties and methods managing the kinematics of the PR2 robot arm.
q
A numpy vector of size 18 containing the current values of the joints.
def pos_larm_grip
Use this function to get the current global position of the left gripper (Arm Endeffector).
qh
A numpy vector of size 7 containing the upper bounds of the arm joints.
ofuncode
Specifies the objective function for redundancy optimization.
def IK_config
Finds the solution of the Inverse Kinematic problem for given redundant parameter vector in free-bas...
def pos_larm_grip_wrt_tor
Use this function to get the current position of the left gripper (Arm Endeffector) w...
larm
An instance of class packages.nima.robotics.kinematics.special_geometries.pr2.pr2_arm_kinematics.PR2_ARM() containing the kinematic properties and methods of the left arm.
def pos_rarm_grip_wrt_tor
Use this function to get the current position of the right gripper (Arm Endeffector) w...
qm
A numpy vector of size 18 containing a set of reference values for the joints.
def pos_rarm_grip
Use this function to get the current global position of the right gripper (Arm Endeffector).
def rarm_end_position
Returns the current position of the endeffector or gripper end for the right arm. ...
ql
A numpy vector of size 7 containing the lower bounds of the arm joints.