MAGIKS  1.1
Manipulator General Inverse Kinematic Solver
 All Classes Namespaces Files Functions Variables Pages
endeffector.py
Go to the documentation of this file.
1 ## @file: endeffector.py
2 # @brief: This module provides a class containing a set of reference positions and orientations in the taskspace together
3 # with their associated poses.
4 # @author Nima Ramezani Taghiabadi
5 #
6 # PhD Researcher
7 # Faculty of Engineering and Information Technology
8 # University of Technology Sydney (UTS)
9 # Broadway, Ultimo, NSW 2007, Australia
10 # Phone No. : 04 5027 4611
11 # Email(1) : nima.ramezani@gmail.com
12 # Email(2) : Nima.RamezaniTaghiabadi@uts.edu.au
13 # @version 4.0
14 #
15 # Last Revision: 23 August 2015
16 
17 '''
18 Changes from last version:
19  1- a new task cost function added: Liegeois_Midrange_Deviance() used for Jacobian Nullspace Gradient Projection to avoid joint limits as a second-priority cost function
20 '''
21 
22 import numpy, math, copy, sys
23 import link_point as lplib, task_reference as trlib, cost_function as cflib
24 import general_python as genpy
25 
26 from magiks.geometry import manipulator_geometry as mangeolib
27 from magiks.magiks_core import function_library as flib
28 from math_tools.geometry import rotation as rot, geometry as geolib
29 from math_tools.algebra import vectors_and_matrices as vecmat
30 
31 class Endeffector_Settings(object):
32  '''
33  '''
34  def __init__(self):
35 
36  '''
37  If there are more than one endeffector,
38  precision settings can be set individually for each "position reference" (Task Point) and "orientation reference" (Task Frame)
39  You can change each one after the creation of class instance
40  '''
41 
42  self.precision_base_for_position = "Coordinate Difference"
43  self.precision_base_for_rotation = "Axis Angle"
44 
45  self.precision_for_position = 0.02 # coordinate distance in meters
46  self.precision_for_rotation = 2.0 # axis angle in degrees
47 
48 class Endeffector(mangeolib.Manipulator_Geometry):
49  '''
50  This class contains all kinematic properties of the taskspace of a manipulator.
51  These properties are:
52 
53  1- task_point: A list of operational points (Position References)
54  2- reference_orientation: A list of operational link frames (Orientation References)
55  3- The geometric Jacobian
56  4- The error Jacobian
57  5- The pose error vector and its norm
58 
59  Method "update" calculates and update all these properties according to the given joint configuration.
60  '''
61  def __init__(self, config_settings, geo_settings, end_settings):
62  super(Endeffector, self).__init__(copy.deepcopy(config_settings), copy.copy(geo_settings))
63  self.end_settings = copy.copy(end_settings)
64  # Property "task_points" : is a list of instances of class task_point
65  # Each instance is a "task point" and determines a position reference for the endeffector
66  '''
67  The following code defines one task point by default for the link_segment model
68  This defined task point is the origin of the last link of the model (The defined endeffector by default)
69  '''
70  self.task_point = []
71  self.task_frame = []
72  self.task_cost = []
73 
74  last_link_number = geo_settings.nlink - 1
75  last_link_origin = lplib.Link_Point(last_link_number,1,[0,0,0])
76  endeffector_position_reference_by_default = trlib.Task_Point(self.config_settings, [last_link_origin])
77  self.add_taskpoint(endeffector_position_reference_by_default)
78 
79  # Property "task_frame" : is a list of instances of class Task_Frame
80  # Each instance is a "task frame" and determines an orientation reference for the endeffector
81 
82  '''
83  The following code defines one task frame by default for the link_segment model
84  This defined task frame is the orientation of the last link of the model (The defined endeffector by default)
85  '''
86  endeffector_orientation_reference_by_default = trlib.Task_Frame(self.config_settings, last_link_number)
87  self.add_taskframe(endeffector_orientation_reference_by_default)
88 
89  self.task_cost.append(cflib.Cost_Function(input_ref = 'Joint Values'))
90  self.task_cost[0].function = flib.Zghal_Function(xl = numpy.copy(self.ql), xh = numpy.copy(self.qh))
91  self.task_cost[0].abs_grad = numpy.zeros(self.config_settings.DOF)
92  # self.task_cost[0].purpose = 'Joint Damping'
93  '''
94  self.task_cost.append(cflib.Cost_Function(input_ref = 'Joint Values'))
95  self.task_cost[1].function = flib.Liegeois_Midrange_Deviance(xl = numpy.copy(self.ql), xh = numpy.copy(self.qh))
96  # self.task_cost[1].purpose = 'Gradient Projection'
97  '''
98 
99  # mp -- number of position coordinates (3 by default)
100  # mo -- number of orientation coordinates (3 by default)
101  # ("self.mp" elements representing position error come first )
102  # ("self.mo" elements representing orientation error come next)
103 
104  (mp, mo) = self.num_task_constraints()
105 
106  self.clear_pose()
107 
108  def clear_pose(self):
109  for tp in self.task_point:
110  tp.clear()
111 
112  for tf in self.task_frame:
113  tf.clear()
114 
115  # self.current_pose is the pose vector. Three elements for each task_point followed by nine elements for each reference_orientation
116  # represent actual positions and orientations of the endeffector respectively in one pose vector
117  self.current_pose = None
118  # Matrix of Geometric Jacobian (for both positions and orientations)
119  # The first rows represent geometric jacobian for position. Number of rows for position = 3*len(task_points)
120  # The next rows represent geometric jacobian for orientation. Number of rows for orientation = 3*len(task_frame)
121  self.geo_jac = None
122  self.clear_error()
123 
124  def clear_error(self):
125  for tp in self.task_point:
126  tp.clear_error()
127 
128  for tf in self.task_frame:
129  tf.clear_error()
130 
131  # self.current_pose_error is the overall vector containing values of error functions (both position and orientation)
132  self.current_pose_error = None
133  # self.current_error_norm contains the norm of the error vector
134  self.current_error_norm = None
135 
136  # Matrix of Error Jacobian (for both position and orientation errors)
137  # (The first "self.mp" rows represent error jacobian for position)
138  # (The next "self.mo" rows represent error jacobian for orientation)
139  self.err_jac = None
140  # Property "self.all_task_points_in_target" is True if all of the task_points are in their desired positions
142  # Property "self.all_reference_orientations_in_target" is True if all of the reference_orientations are identical to their desired orientations
144  # Property "self.in_target" is True if all of the task_points and task_frames are in their desired positions and orientations
145  self.is_in_target = None
146  # Property self.mu contains the current value of the manipulability index
147  self.mu = None
148 
149  def add_taskpoint(self, task_point):
150  self.task_point.append(task_point)
151  n_tp = len(self.task_point) - 1
152 
153  self.task_point[n_tp].error.settings.precision = self.end_settings.precision_for_position
154  self.task_point[n_tp].error.settings.precision_base = self.end_settings.precision_base_for_position
155 
156  self.clear_pose()
157 
158  def add_taskframe(self, task_frame):
159  self.task_frame.append(task_frame)
160  n_tf = len(self.task_frame) - 1
161  self.task_frame[n_tf].error.settings.precision = self.end_settings.precision_for_rotation
162  self.task_frame[n_tf].error.settings.precision_base = self.end_settings.precision_base_for_rotation
163 
164  def __str__(self):
165  s = ''
166  for i in range(len(self.task_point)):
167  s += " Reference Position " + str(i) + ":" + "\n" + "\n"
168  #s += "-----------------------------" + "\n"
169  s += str(self.task_point[i])
170  s += "\n"
171  for i in range(len(self.task_frame)):
172  s += " Reference Orientation " + str(i) + ":" + "\n" + "\n"
173  #s += "-----------------------------" + "\n"
174  s += str(self.task_frame[i])
175 
176  s += "\n"
177  s += " Pose Error: "
178  s += vecmat.vector_to_str(self.pose_error()) + "\n"
179  s += " Norm of Pose Error: "
180  s += str(self.pose_error_norm()) + "\n"
181  return s
182 
184  cnt = 0 # constraint counter
185 
186  # For task_points:
187  for tp in self.task_point:
188  # Counting total number of constraints
189  cnt = cnt + tp.error.settings.weight.shape[0]
190 
191  self.mp = cnt
192  # For task_frame:
193  for tf in self.task_frame:
194  #Counting total number of constraints
195  cnt = cnt + tf.error.settings.weight.shape[0]
196 
197  self.mo = cnt - self.mp
198  return (self.mp, self.mo)
199 
201 
202  W = numpy.ones(self.config_settings.DOF)
203  for cf in self.task_cost:
204  if cf.purpose == 'Joint Damping':
205  abs_grad = abs(cf.gradient(self))
206  delta = abs_grad - cf.abs_grad
207  for i in range(self.config_settings.DOF):
208  if delta[i] >= 0.0:
209  W[i] += cf.weight*abs_grad[i]
210  cf.abs_grad[i] = abs_grad[i]
211 
212  return numpy.diag(1.0/W)
213 
214  def pose(self):
215  if self.current_pose == None:
216  '''
217  Calculates and updates the actual positions and orientations of all task_points and task_frames according to the given configuration
218  '''
219  #Create pose vector:
220  self.current_pose = numpy.zeros(( 3*len(self.task_point) + 9*len(self.task_frame) ))
221 
222  k = 0 #counter for pose vector
223 
224  H = self.transfer_matrices()
225  # For task_points:
226  for tp in self.task_point:
227  pos = tp.position(H)
228  #Arranging three position coordinates of each task_point in the endeffector pose vector
229  for j in range(0,3):
230  self.current_pose[k] = pos[j]
231  k +=1
232 
233  # For task_frames:
234  for tf in self.task_frame:
235  #Arranging nine orientation coordinates of each reference_orientation (Elements of the Rotation Matrix) in the endeffector pose vector
236  ori = tf.orientation(H).matrix()
237  for i in range(0,3):
238  for j in range(0,3):
239  self.current_pose[k] = ori[i,j]
240  k +=1
241 
242  return self.current_pose
243 
244  def pose_error(self):
245  if self.current_pose_error == None:
246  '''
247  Calculates and updates the current values of position and orientation errors between the actual and target poses of task_points and task_frames
248  Also updates the values of "pose_error" and "error_norm"
249  '''
250  #Creation of the vector of errors
251  cnt = 0
252  p = self.pose() # This should change
253  self.num_task_constraints()
254  self.current_pose_error = numpy.zeros((self.mp + self.mo))
255  #For task_points
257  H = self.transfer_matrices()
258  for tp in self.task_point:
259  ers = "Target has not been set for some of the taskpoints"
260  assert tp.rd != None, genpy.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, ers)
261  # Calculating position error for each task_point
262  self.all_task_points_in_target = self.all_task_points_in_target and tp.error.in_target(tp.position(H), tp.rd)
263  #Arranging the vector of errors for position
264  tp_err = tp.error.value(tp.ra, tp.rd)
265  for k in range(0,len(tp_err)):
266  self.current_pose_error[cnt] = tp_err[k]
267  cnt = cnt + 1
268  #For task_frames
270  for tf in self.task_frame:
271  ers = "Target has not been set for some of the taskframes"
272  assert tf.rd != None, genpy.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, ers)
273  # Calculating orientation error for each task_frame
274  self.all_task_frames_in_target = self.all_task_frames_in_target and tf.error.in_target(tf.orientation(H), tf.rd)
275  #Arranging the vector of errors for orientation"
276  tf_err = tf.error.value(tf.ra, tf.rd)
277  for k in range(0,len(tf_err)):
278  self.current_pose_error[cnt] = tf_err[k]
279  cnt = cnt + 1
280 
282 
283  return self.current_pose_error
284 
285  def pose_error_norm(self):
286  if self.current_error_norm == None:
287  p = self.pose_error()
288  self.current_error_norm = numpy.linalg.norm(self.current_pose_error)
289  return self.current_error_norm
290 
292  if self.geo_jac == None:
293  '''
294  Calculates and updates the geometric jacobian of the endeffector according to the task_points and task_frames
295  '''
296  #Create the geometric jacobian matrix
297  cnt = 0
298  npos = 3*len(self.task_point)
299  nori = 3*len(self.task_frame)
300  self.geo_jac = numpy.zeros((npos + nori, self.config_settings.DOF))
301  #For task_points
302  for i in range(0,len(self.task_point)):
303  tp = self.task_point[i]
304  # Calculating geometric jacobian matrice corresponding to each task_point
305  tp.update_geometric_jacobian(self.analytic_jacobian)
306  #Arranging geometric jacobian for position
307  for k in range(0,3):
308  for j in range(0, self.config_settings.DOF):
309  self.geo_jac[3*i + k, j] = tp.geometric_jacobian.value[k,j]
310  #For task_frames
311  for i in range(0,len(self.task_frame)):
312  tf = self.task_frame[i]
313  #Calculate geometric jacobian matrice corresponding to each reference_orientation"
314  tf.update_geometric_jacobian(self.H)
315  #Arranging geometric jacobian for orientation
316  for k in range(0,3):
317  for j in range(0, self.config_settings.DOF):
318  self.geo_jac[npos + 3*i + k,j] = tf.geometric_jacobian.value[k,j]
319 
320  return self.geo_jac
321 
322  def error_jacobian(self):
323  if self.err_jac == None:
324  '''
325  Calculates and updates the error jacobian of the endeffector according to the actual and target poses of the task_points and task_frames and their corresponding metrics
326  '''
327  cnt = 0
328  #Creation of the error jacobian matrix"
329  self.err_jac = numpy.zeros((self.mp + self.mo, self.config_settings.DOF))
330  H = self.transfer_matrices()
331  #For task_points
332  for tp in self.task_point:
333  ers = "Target has not been set for some of the taskpoints"
334  assert tp.rd != None, genpy.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, ers)
335  # Calculating error jacobian for each task_point
336  tp_ej = tp.error_jacobian(H, self.ajac)
337 
338  tp_er = tp.error.value(tp.position(H), tp.rd)
339  #Arranging error jacobian for position
340  for k in range(0,len(tp_er)):
341  for j in range(0, self.config_settings.DOF):
342  self.err_jac[cnt,j] = self.jmc_c[j]*tp_ej[k,j]
343  cnt = cnt + 1
344  #For task_frames
345  for tf in self.task_frame:
346  ers = "Target has not been set for some of the taskframes"
347  assert tf.rd != None, genpy.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, ers)
348  # Calculating error jacobian for each reference_orientation
349  tf_ej = tf.error_jacobian(self.ajac)
350  tf_er = tf.error.value(tf.orientation(H), tf.rd)
351  #Arranging error jacobian for orientation
352  for k in range(0,len(tf_er)):
353  for j in range(0, self.config_settings.DOF):
354  self.err_jac[cnt,j] = self.jmc_c[j]*tf_ej[k,j]
355  cnt = cnt + 1
356  return self.err_jac
357 
358  def manipulability(self):
359  if self.mu == None:
360  J = self.error_jacobian()
361  self.mu = math.sqrt(abs(numpy.linalg.det(numpy.dot(J, J.T))))
362  return self.mu
363 
364  def in_target(self):
365  if self.is_in_target == None:
366  pe = self.pose_error()
367  return self.is_in_target
368 
369  def pose_to_tuple(self, pose_selection , parametrization):
370  '''
371  return the endeffector target pose as a concatenated tuple
372  '''
373  assert pose_selection in ['actual','desired']
374  pose_tpl = ()
375 
376  H = self.transfer_matrices()
377  for tp in self.task_point:
378  if pose_selection == 'actual':
379  pv = tp.position(H)
380  elif pose_selection == 'desired':
381  pv = tp.rd
382  else:
383  assert False
384 
385  for j in range(3):
386  pose_tpl += pv[j],
387 
388  for tf in self.task_frame:
389  if pose_selection == 'actual':
390  rm = tf.orientation(H)
391  elif pose_selection == 'desired':
392  rm = tf.rd
393  else:
394  assert False
395 
396  if parametrization == 'Rotation Matrix':
397  for j in range(3):
398  for k in range(3):
399  pose_tpl += rm['matrix'][j][k],
400  else:
401  ov = rm[parametrization]
402  for j in range(3):
403  pose_tpl += ov[j],
404 
405  return pose_tpl
406 
407  def tuple_to_pose(self, pose_selection, pose_tuple, parametrization):
408  '''
409  get the pose as a concatenated tuple and change endeffector target
410  (inverse of "self.target_pose_to_tuple")
411  '''
412  assert pose_selection in ['actual','desired']
413  actual = (pose_selection == 'actual')
414 
415  cnt = 0
416  r = numpy.zeros(3)
417  for tp in self.task_point:
418  for j in range(3):
419  r[j] = pose_tuple[cnt]
420  cnt += 1
421  if actual:
422  tp.ra = copy.copy(r)
423  else:
424  tp.set_target(copy.copy(r))
425 
426  for tf in self.task_frame:
427  if parametrization == 'Rotation Matrix':
428  r = numpy.eye(3)
429  for j in range(3):
430  for k in range(3):
431  r[j][k] = pose_tuple[cnt]
432  cnt += 1
433  if actual:
434  tf.ra['matrix'] = r
435  else:
436  tf.set_target(geolib.Orientation_3D(r, ori_velocity = numpy.zeros((3,3))))
437 
438  else:
439 
440  ov = numpy.zeros((3))
441  for j in range(0,3):
442  ov[j] = pose_tuple[cnt]
443  cnt += 1
444 
445  if actual:
446  tf.ra = rotation.rotation_matrix(ov, parametrization)
447  else:
448  tf.rd = rotation.rotation_matrix(ov, parametrization)
449 
450  self.clear_error()
451 
452  def set_config_virtual(self, qvrd):
453  if super(Endeffector, self).set_config_virtual(qvrd):
454  self.clear_pose()
455  return True
456  else:
457  return False
458 
459  def set_config(self, qd):
460  if super(Endeffector, self).set_config(qd):
461  self.clear_pose()
462  return True
463  else:
464  return False
465 
466  def set_target(self, position, orientation):
467  assert len(position) <= len(self.task_point)
468  assert len(orientation) <= len(self.task_frame)
469  cnt = 0
470  for p in position:
471  self.task_point[cnt].set_target(p)
472  cnt += 1
473  cnt = 0
474  for o in orientation:
475  o.set_velocity(numpy.zeros((3,3)), representation = 'matrix')
476  self.task_frame[cnt].set_target(o)
477  cnt += 1
478 
479  self.clear_error()