MAGIKS  1.1
Manipulator General Inverse Kinematic Solver
 All Classes Namespaces Files Functions Variables Pages
trajectory.py
Go to the documentation of this file.
1 # HEADER
2 
3 ## @file trajectory.py
4 # @brief This module provides a class containing a trajectory in the multi-dimensional space.
5 # The path is generated by a number of key points containing poses, twists
6 # with their corresponding phase values ($f \phi $f)
7 # @author Nima Ramezani Taghiabadi
8 #
9 # PhD Researcher
10 # Faculty of Engineering and Information Technology
11 # University of Technology Sydney (UTS)
12 # Broadway, Ultimo, NSW 2007, Australia
13 # Phone No. : 04 5027 4611
14 # Email(1) : nima.ramezani@gmail.com
15 # Email(2) : Nima.RamezaniTaghiabadi@uts.edu.au
16 # @version 7.1
17 #
18 # Last Revision: 21 August 2015
19 
20 # BODY
21 
22 '''
23 Changes from previous version:
24  1- jerk added
25  2- smoothed add_point feature added based on finite difference coefficients
26  3- add_point respecs limits for position, velocity, acceleration and jerk
27  4- function draw_points() added.
28  5- function plot_list() added
29 '''
30 
31 import copy, math, sys, numpy as np
32 
33 import general_python as genpy
34 from math_tools import general_math as gen
35 from math_tools.algebra import polynomials as pl, vectors_and_matrices as vm
36 from math_tools.geometry import geometry as geo, trigonometry as trig, geometry_2d as geo2d
37 
38 all_figures = ['Position', 'Velocity', 'Acceleration', 'Jerk']
39 all_ltypes = ['-','r--', 'bs', 'g^', 'bo', 'k']
40 all_colors = ['black','blue', 'cyan','green', 'red', 'purple', 'orange', 'gray', 'yellow', 'magenta',
41  'firebrick', 'k',
42  '#F0E442', '#8A2BE2', '#bcbcbc', '#8b8b8b', '#b3de69', '#0072B2', '#eeeeee',
43  '#6ACC65', '#ffed6f', '#FF9F9A', '#6d904f', '#FFFEA3', '0.70'
44  '#FFB5B8', '#bc82bd', '#ccebc4', '#77BEDB', '#009E73', '#97F0AA', '#D65F5F',
45  '.8' , '#003FFF', '#92C6FF', '#fdb462', '#FBC15E', '#8172B2', '#B47CC7',
46  '#E5E5E5', '#CC79A7', '#E24A33', '#006374', '#EEEEEE', '#8EBA42', '0.40',
47  'w' , '#fa8174', '#55A868', '#B0E0E6', '#C44E52', '#64B5CD', '#4C72B0',
48  '#D0BBFF', '#f0f0f0', '#cbcbcb', '#C4AD66', '#03ED3A', '#467821', '#00FFCC',
49  '#FFC400', '#30a2da', '#988ED5', '#e5ae38', 'darkgoldenrod', '#CCB974',
50  '#348ABD', '0.8' , '#feffb3', '#bfbbd9', '#555555', '#8dd3c7', '#777777',
51  '0.6' , '#D55E00', '#fc4f30', '#7600A1', '#00D7FF', '#4878CF', '#017517',
52  '#001C7F', '#B8860B', '#EAEAF2', '#E8000B', '#afeeee', '#81b1d2', '#7A68A6',
53  '#8C0900', '.15' , '#A60628', '#56B4E9']
54 # all_colors = [str(i*0.1) for i in range(10)]
55 '''
56 r-- : dashed line
57 bs : blue squares
58 g^ : green triangles
59 '''
60 
61 # Backward Finite Difference Coefficients for Velocity
62 FDC_v = {1:[1.0, - 1.0],
63  2:[1.5, - 2.0, 0.5],
64  3:[11.0/6, - 3.0, 1.5, - 1.0/3],
65  4:[25.0/12, - 4.0, 3.0, - 4.0/3, 0.25 ],
66  5:[137.0/60, - 5.0, 5.0, - 10.0/3, 1.25, - 0.2 ],
67  6:[49.0/20, - 6.0, 7.5, - 20.0/3, 3.75, - 1.2, 1.0/6 ]}
68 
69 FDC_a = {1:[1.0, - 2.0, 1.0],
70  2:[2.0, - 5.0, 4.0, - 1.0 ],
71  3:[35.0/12, - 26.0/3, 9.5, - 14.0/3, 11.0/12],
72  4:[15.0/4, - 77.0/6, 107.0/6, - 13.0, 61.0/12, - 5.0/6 ],
73  5:[203.0/45, - 87.0/5, 117.0/4, - 254.0/9, 33.0/2, - 27.0/5 , 137.0/180],
74  6:[469.0/90, - 22.3, 879.0/20, - 949.0/18, 41.0, - 20.1, 1019.0/180.0, -0.7]}
75 
76 def feasible_position(Xd, X0, V0, X_min, X_max, v_max, a_max, dt, smooth = False):
77  dim = len(Xd)
78 
79  v_max = v_max*math.sqrt(dim)
80  a_max = a_max*math.sqrt(dim)
81 
82  r = a_max*dt
83  v0 = np.linalg.norm(V0)
84  assert v0 < v_max + r , "Inconsistent "
85  V = (Xd - X0)/dt
86  sgn = 1.0
87  if smooth:
88  V = 0.5*(V + V0)
89  v = np.linalg.norm(V)
90  if v > v_max:
91  V = V*v_max/v
92  v = v_max
93 
94  vv0 = np.dot(V,V0)
95 
96  if v0*v0 + v*v - 2*vv0 > r*r: # end of vector V is not inside the circle: desired velocity is not feasible
97  theta = trig.arccos(vv0/(v*v0))
98  O = geo2d.Point_2D([- v0, 0.0])
99  cir = geo2d.Circle_2D(R = r)
100  line_V = geo2d.Line_2D(O, theta, representation = 'point-angle')
101  sol = cir.intersection(line_V)
102  if sol == []:
103  A = (V - V0)/dt
104  a = np.linalg.norm(A)
105  while (v > v_max) or (a > a_max):
106  if a > a_max :
107  A = A*a_max/a
108  a = a_max
109  V = A*dt + V0
110  v = np.linalg.norm(V)
111  if v > v_max :
112  V = V*v_max/v
113  v = v_max
114  A = (V - V0)/dt
115  a = np.linalg.norm(A)
116  X = X0 + V*dt
117 
118  assert np.linalg.norm(A) < a_max + 0.001
119  assert np.linalg.norm(V) < v_max + 0.001
120  return (X, V, A)
121 
122  '''
123  CH = cir.center.perpend_to(line_V)
124  H = line_V.intersection(CH)
125  v_opt = O.dist(H)
126  sgn = gen.sign(vv0)
127  '''
128  elif len(sol) == 1:
129  v_opt = O.dist(sol[0])
130  sgn = gen.sign(vv0)
131  else:
132  o1 = O.dist(sol[0])
133  o2 = O.dist(sol[1])
134  if v0 > r:
135  if vv0 > 0:
136  d1 = abs(o1 - v)
137  d2 = abs(o2 - v)
138  sgn = 1.0
139  else:
140  d1 = o1
141  d2 = o2
142  sgn = - 1.0
143 
144  if d1 < d2:
145  v_opt = o1
146  else:
147  v_opt = o2
148  else:
149  sgn = 1.0
150  if gen.equal(o1*o1 + v0*v0 - 2*o1*v0*math.cos(theta) - r*r, 0.0):
151  v_opt = o1
152  else:
153  v_opt = o2
154 
155  V = V*sgn*v_opt/v
156  # v = np.linalg.norm(V)
157 
158  V = V*min(1.0, vm.feasible_stepsize(direction = V*dt, x = X0, x_min = X_min, x_max = X_max))
159 
160  X = X0 + V*dt
161 
162  A = (V - V0)/dt
163  '''
164  assert sum(X > X_max) == 0
165  assert sum(X < X_min) == 0
166 
167  assert np.linalg.norm(A) < a_max + 0.001
168  assert np.linalg.norm(V) < v_max + 0.001
169  '''
170  return (X, V, A)
171 
172 '''
173 
174 def feasible_position(Xd, X0, V0, X_min, X_max, v_max, a_max, dt):
175  r = a_max*dt
176  v0 = np.linalg.norm(V0)
177  assert v0 < v_max + r , "Inconsistent "
178  V = (Xd - X0)/dt
179  sgn = 1.0
180  # V = 0.5*(V + V0)
181  v = np.linalg.norm(V)
182  if v > v_max:
183  V = V*v_max/v
184  v = v_max
185 
186  vv0 = np.dot(V,V0)
187 
188  if v0*v0 + v*v - 2*vv0 > r*r: # end of vector V is not inside the circle: desired velocity is not feasible
189  print "****************"
190  print "Not inside: "
191  theta = trig.arccos(vv0/(v*v0))
192  O = geo2d.Point_2D([- v0, 0.0])
193  cir = geo2d.Circle_2D(R = r)
194  line_V = geo2d.Line_2D(O, theta, representation = 'point-angle')
195  sol = cir.intersection(line_V)
196  if sol == []:
197  print "No Intersection: "
198  CH = cir.center.perpend_to(line_V)
199  H = line_V.intersection(CH)
200  sgn = gen.sign(vv0)
201  elif len(sol) == 1:
202  print "Tangent: "
203  H = sol[0]
204  sgn = gen.sign(vv0)
205  else:
206  print "Two Intersections: "
207  if O.dist(cir.center) > r:
208  print "O outside circle"
209  if vv0 > 0:
210  print "same direction"
211  d1 = abs(O.dist(sol[0]) - v)
212  d2 = abs(O.dist(sol[1]) - v)
213  sgn = 1.0
214  print 'O.dist(sol[0]) = ', O.dist(sol[0])
215  print 'O.dist(sol[1]) = ', O.dist(sol[1])
216  else:
217  print "opposite direction"
218  d1 = O.dist(sol[0])
219  d2 = O.dist(sol[1])
220  sgn = - 1.0
221  print "I must be zero: ", d1*d1 + v0*v0 + 2*d1*v0*math.cos(theta) - r*r
222  print "I must be zero: ", d2*d2 + v0*v0 + 2*d2*v0*math.cos(theta) - r*r
223 
224  if d1 < d2:
225  H = sol[0]
226  print "d1 selected"
227  else:
228  H = sol[1]
229  print "d2 selected"
230 
231  else:
232  print "O inside circle"
233  sgn = 1.0
234  d1 = O.dist(sol[0])
235  d2 = O.dist(sol[1])
236  if gen.equal(d1*d1 + v0*v0 - 2*d1*v0*math.cos(theta) - r*r, 0.0):
237  H = sol[0]
238  print "d1 selected"
239  print "I must be zero: ", d2*d2 + v0*v0 + 2*d2*v0*math.cos(theta) - r*r
240  print "I must be amax: ", np.linalg.norm( (V*d1/v - V0) /dt)
241  else:
242  H = sol[1]
243  print "d2 selected"
244  print "I must be zero: ", d1*d1 + v0*v0 + 2*d1*v0*math.cos(theta) - r*r
245  print "I must be amax: ", np.linalg.norm( (V*d2/v - V0) /dt)
246 
247  if vv0 > 0:
248  print "same direction"
249  else:
250  print "opposite direction"
251 
252  assert cir.possess(sol[0])
253  assert cir.possess(sol[1])
254 
255  print "d1 = : ", d1
256  print "d2 = : ", d2
257 
258 
259  print
260  A = (V - V0)/dt
261  a = np.linalg.norm(A)
262  print "v_0 = ", v0
263  print "v_d = ", v
264  print "a_d = ", a
265  v_opt = O.dist(H)
266  V = V*sgn*v_opt/v
267  v = np.linalg.norm(V)
268 
269  print "v_opt = ", v
270  print "v_max = ", v_max
271  A = (V - V0)/dt
272  a = np.linalg.norm(A)
273  print
274  print "a = ", a
275  print "a_max = ", a_max
276 
277  # kh = min(1.0, vm.feasible_stepsize(direction = Xd - X0, x = X0, x_min = X_min, x_max = X_max))
278 
279  # V = kh*V
280 
281  X = X0 + V*dt
282 
283  A = (V - V0)/dt
284 
285  if np.dot(V, Xd - X0) > 0.0:
286  print "Belakhare hamjahat shodand: sgn = ", sgn
287  else:
288  print "Akharesh hamjahat nashodand dadash: sgn = ", sgn
289 
290  return (X, V, A)
291 
292 def feasible_position(xd, x0, v0, x_min, x_max, v_max, a_max, dt):
293  d = xd - x0
294  Ld = np.linalg.norm(d)
295  if gen.equal(Ld, 0.0):
296  return (xd, d, - v0/dt)
297 
298  ss = Ld/dt
299  u = d/Ld
300  kh = min(ss, vm.feasible_stepsize(direction = u, x = x0, x_min = x_min, x_max = x_max))
301 
302  vh = v_max
303  vl = - v_max
304 
305  betta = sum(u*v0)
306  gamma = sum(v0*v0)
307  delta = betta*betta + a_max*a_max*dt*dt - gamma
308  if delta < 0:
309  al = betta
310  ah = betta
311  else:
312  sqrt_delta = math.sqrt(delta)
313  al = betta - sqrt_delta
314  ah = betta + sqrt_delta
315 
316  sh = min(ah, vh, kh)
317  sl = max(al, vl)
318 
319  if sl > sh:
320  sh = min(vh, kh)
321  """
322  if ah < vl:
323  sh = vl
324  """
325  if ss > sh:
326  ss = sh
327 
328  v = u*ss
329  x = v*dt + x0
330  a = (v - v0)/dt
331 
332  La = np.linalg.norm(a)
333  Lv = np.linalg.norm(v)
334 
335  if La > a_max + 0.0001:
336  print La, a_max, 'al: ', al, 'ah: ', ah,'sl: ', sl,'sh: ', sh, 'vh: ', vh, 'delta: ', delta
337  if Lv > v_max + 0.0001:
338  print Lv, v_max
339 
340  assert Lv <= v_max + 0.0001
341  return (x,v,a)
342 
343 def feasible_position(xd, x0, v0, x_min, x_max, v_max, a_max, dt):
344  dr = xd - x0
345  vh = dt*a_max + v0
346  vl = - dt*a_max + v0
347  xh = vh*dt + x0
348  xl = vl*dt + x0
349  xh = np.minimum(x_max, xh)
350  xl = np.maximum(x_min, xl)
351 
352 
353 
354  if sum(xl > xh) != 0:
355  print "THIS SHOULD NOT HAPPEN *************************************************************************"
356  print xl
357  print xh
358 
359  kh = min(1.0, vm.feasible_stepsize(direction = dr, x = x0, x_min = xl, x_max = xh))
360 
361  x = x0 + kh*dr
362  v = (x - x0)/dt
363  a = (v - v0)/dt
364 
365  if kh < 1.0:
366  print
367  print "kh = ", kh
368  print "xl = ", xl
369  print "xh = ", xh
370  print
371  print "xd = ", xd
372  print "x = ", x
373 
374  return (x,v,a)
375 '''
376 
377 def jerkbound_position_estimate(xd, x0, v0, a0, j_max, dt):
378  x = xd
379  v = (xd - x0)/dt
380  a = (v - v0)/dt
381  j = (a - a0)/dt
382 
383  Lj = np.linalg.norm(j)
384  if Lj > j_max:
385  # print
386  # print "Lj = ", Lj
387  j = j*j_max*(1.0 + math.log(Lj/j_max))/Lj
388  a = j*dt + a0
389  v = a*dt + v0
390  x = v*dt + x0
391  # print "Lj = ", np.linalg.norm(j)
392  # print "La = ", np.linalg.norm(a)
393  # print "Lv = ", np.linalg.norm(v)
394 
395  return (x, v, a)
396 
397 def accbound_position_estimate(xd, x0, v0, a_max, dt):
398  x = xd
399  v = (xd - x0)/dt
400  a = (v - v0)/dt
401 
402  Lv = np.linalg.norm(v)
403  La = np.linalg.norm(a)
404  if La > a_max:
405  a = a*a_max*(1.0 + math.log(La/a_max))/La
406  v = a*dt + v0
407  x = v*dt + x0
408  # print "La = ", np.linalg.norm(a)
409  # print "Lv = ", np.linalg.norm(v)
410 
411  return (x, v, a)
412 
413 def velbound_position_estimate(xd, x0, v0, v_max, dt):
414  x = xd
415  v = (xd - x0)/dt
416 
417  Lv = np.linalg.norm(v)
418  if Lv > v_max:
419  # print
420  # print "Lv = ", Lv
421  v = v*v_max*(1.0 + math.log(Lv/v_max))/Lv
422  # print "Lv = ", np.linalg.norm(v)
423  x = v*dt + x0
424 
425  a = (v - v0)/dt
426  # print "La = ", np.linalg.norm(a)
427 
428  return (x, v, a)
429 
430 def finite_difference_estimate(x, x_min, x_max, v_max, a_max, dt):
431 
432  genpy.check_type(x, [np.ndarray, list, tuple], __name__, '', sys._getframe().f_code.co_name, 'x', array_length = [2,3,4,5,6,7,8], shape_length = 2)
433  if a_max == None:
434  a_max = np.inf
435  if v_max == None:
436  v_max = np.inf
437 
438  n = len(x)
439  x_t = np.copy(x[0])
440  x_changed = False
441  if n == 2:
442  cv = FDC_v[n - 1]
443  ca = cv
444  v0 = 0.0
445  else:
446  cv = FDC_v[n - 1]
447  ca = FDC_a[n - 2]
448  v0 = np.dot(FDC_v[n - 2], x[1:n])/dt
449 
450  dt2 = dt*dt
451  gain = (1.0/math.sqrt(x.shape[1]))
452 
453  # If smooth is activated, find the smoothed value of x[0] here
454 
455  a_t = np.dot(ca, x)/dt2
456  v_t = np.dot(cv, x)/dt
457  kh = min(1.0, vm.feasible_stepsize(direction = x[0] - x[1], x = x[1], x_min = x_min, x_max = x_max))
458 
459  La = np.linalg.norm(a_t)*gain
460  Lv = np.linalg.norm(v_t)*gain
461  sa = - np.dot(ca[1:n], x[1:n])
462  sv = - np.dot(cv[1:n], x[1:n])
463 
464  # while (La > a_max) or (Lv > v_max) or (kh < 1.0):
465  if La > a_max: # clamping the vector if higher than maximum
466  a_t = a_t*a_max*(1.0 + math.log(La/a_max))/La
467  x[0] = (sa + dt2*a_t)/ca[0]
468  v_t = np.dot(cv, x)/dt
469  Lv = np.linalg.norm(v_t)*gain
470  La = np.linalg.norm(a_t)*gain
471  kh = min(1.0, vm.feasible_stepsize(direction = x[0] - x[1], x = x[1], x_min = x_min, x_max = x_max))
472  print "acc changed: ", La, Lv, kh
473 
474  '''
475  if Lv > v_max: # clamping the vector if higher than maximum
476  v_t = v_t*v_max*(1.0 + math.log(Lv/v_max))/Lv
477  x[0] = (sv + dt*v_t)/cv[0]
478  a_t = np.dot(ca, x)/dt2
479  La = np.linalg.norm(a_t)*gain
480  Lv = np.linalg.norm(v_t)*gain
481  kh = min(1.0, vm.feasible_stepsize(direction = x[0] - x[1], x = x[1], x_min = x_min, x_max = x_max))
482  print
483  print "vel changed: ", La, Lv, kh
484  '''
485  if kh < 1.0:
486  x[0] = x[1] + kh*(x[0] - x[1])
487  v_t = np.dot(cv, x)/dt
488  a_t = np.dot(ca, x)/dt2
489  La = np.linalg.norm(a_t)*gain
490  Lv = v_max
491  kh = 1.0
492  # print "pos changed: ", La, Lv, kh
493 
494  for j in range(x.shape[1]):
495  assert (x_t[j] < x_max[j] + 0.0001) and (x_t[j] > x_min[j] - 0.0001), "j = " + str(j)
496 
497  return (x_t, v_t, a_t)
498 
499 
500 def plot_list(pathlist, ncol = 1, show_limits = False, show_plot = True):
501  if len(pathlist) == 0:
502  return None
503  dim = pathlist[0].dim
504  for path in pathlist:
505  assert dim == path.dim, "\n Error: All paths must have the same dimensions \n"
506 
507  import matplotlib.pyplot as plt
508 
509  plt.figure(1)
510  nrow = dim/ncol
511 
512  for i in range(nrow):
513  for j in range(ncol):
514  cn = ncol*i+j
515  plt.subplot(nrow,ncol,cn + 1)
516  for path in pathlist:
517  path.draw_points(plt, axis = cn)
518  if show_limits:
519  if path.plot_settings.figure == 'Position':
520  path.axhline(y = path.pos_max[cn])
521  path.axhline(y = path.pos_min[cn])
522  elif path.plot_settings.figure == 'Velocity':
523  path.axhline(y = path.vel_max)
524  path.axhline(y = path.vel_min)
525  elif path.plot_settings.figure == 'Acceleration':
526  path.axhline(y = path.acc_max)
527  path.axhline(y = path.acc_min)
528  else:
529  assert False, genpy.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, path.plot_settings.figure + " is not a valid value for argument figure")
530 
531  plt.xlabel('phi')
532  plt.ylabel(path.plot_settings.ylabel(cn))
533 
534  if show_plot:
535  plt.show()
536 
538 
539  def __init__(self, dim):
540  self.figure = 'Position'
541  self.ylabel_suffix = ''
542  self.ylabel_prefix = None
543  self.grid = False
544  self.show_range = False
545  self.axis_label = ['Axis ' + str(i) for i in range(dim)]
546  self.plot_color = [all_colors[i] for i in range(dim)]
547 
548  def ylabel(self, axis):
549  if axis == None:
550  dl = ''
551  else:
552  assert axis < len(self.axis_label), genpy.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, str(axis) + " is not a valid value for argument axis: Must be lower than dimension")
553  dl = self.axis_label[axis]
554 
555  if self.ylabel_prefix == None:
556  s = self.figure + " of "
557  else:
558  s = self.ylabel_prefix
559 
560  if self.ylabel_suffix != None:
561  s += dl + self.ylabel_suffix
562  return s
563 
564 ## This class, introduces a structure for a key point in the multi-dimensional space.
565 # Key points are used to generate a trajectory.
566 # A key point contains a phase value specifying the point phase (time),
567 # and three numpy vectors specifying the desired position, velocity and acceleration at that point.
568 # The values of some or all elements of velocity, acceleration and position can be None
569 # but for each key point, at least one position, velocity or acceleration value must be specified per each element.
570 class Key_Point(object):
571 
572  ## Class Constructor
573  # @param phi The phase value (\f$ \phi \f$) of the key point
574  # @param pos The desired position vector at the key point
575  # @param vel The desired velocity vector at the key point
576  # @param acc The desired acceleration vector at the key point
577  def __init__(self, phi, pos, vel = None, acc = None):
578 
579  ## An integer indicating the dimension of space in which the kepy point is defined.
580  # This number specifies the number of elements of position, velocity and acceleration vectors
581  self.dim = len(pos)
582 
583  # A real float indicating the phase value of the key point
584  self.phi = phi
585 
586  # A numpy vector indicating the position of the key point
587  self.pos = pos
588 
589  # A numpy vector indicating the velocity of the key point
590  self.vel = vel
591 
592  # A numpy vector indicating the acceleration of the key point
593  self.acc = acc
594 
595  ## This function is the string representation of the key point
596  # @param None
597  # @return A string representing all the information about the key point
598  def __str__( self ):
599  s = "Point Dimension: " + str(self.dim) + '\n'
600  s += "Phase : " + str(self.phi) + '\n'
601  s += "Position : " + str(self.pos) + '\n'
602  s += "Velocity : " + str(self.vel) + '\n'
603  s += "Acceleration: " + str(self.acc) + '\n'
604  return s
605 
606  ## Use this function to get the current value of position, velocity or acceleration in a desired dimension
607  # @param field_name A string, must be selected from
608  # set: ['Position', 'Velocity', 'Acceleration'
609  # specifying which vector is desired.
610  # @param axis A non-negative integer specifying which element of the vector should be returned.
611  # (Must not be greater than the space dimension)
612  # @return A float containing the value of the element specified by argument \b axis from the vector specified by argument \b field_name
613  def value(self, field_name = 'Position', axis = 0):
614  assert (axis <= self.dim), genpy.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, "Argument axis must not esxeed the space dimension")
615  assert (axis >= 0), genpy.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, "Argument axis can not have a negative value")
616 
617  if field_name == 'Position':
618  return self.pos[axis]
619  elif field_name == 'Velocity':
620  return self.vel[axis]
621  elif field_name == 'Acceleration':
622  return self.acc[axis]
623  else:
624  assert False, genpy.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, field_name + " is not not a valid value for argument field_name")
625 
626 ## This class contains properties and methods for a <em> Trajectory Segment </em>
627 # A trajectory is established of one segment or a number of segments connected together creating a path in the space.
628 # Each segment, contains a number of instances of class Key_Point().
629 class Path(object):
630 
631  ## Class Constructor
632  # @param dimension A positive integer specifying the dimension of the space in which the trajectory segment is defined
633  def __init__(self, dimension = 3, capacity = 3):
634  # By default, initially the trajectory is a constant position at [0,0,0] (not changing)
635  # You should add points to it to make your trajectory
636 
637  self.current_phi = 0.0
638  ## A float indicating the phase of the last key point (Maximum value for phi)
639  self.phi_end = 0.0
640  ## An integer indicating the number of points
641  self.npoint = 0
642  ## An integer indicating how many key points the path can hold
643  self.capacity = capacity
644 
645  self.dim = dimension
646  self.current_position = np.zeros(self.dim)
647  self.current_velocity = np.zeros(self.dim)
648  self.current_acceleration = np.zeros(self.dim)
649 
650  self.plot_settings = Path_Plot_Settings(dim = dimension)
651 
652  self.point = []
653 
654  ## String representation of the instance (trajectory segment)
655  # @param None
656  # @return A string containing the main information about the trajectory segment
657  def __str__( self ):
658  s = "Phase Length : " + str(self.phi_end) + '\n'
659  s += "Number of Points : " + str(len(self.point)) + '\n'
660  s += "Segment Starting Point: " + '\n'
661  s += str(self.point[0]) + '\n'
662  s += "Segment End Point: " + '\n'
663  s += str(self.point[len(self.point) - 1]) + '\n'
664  return s
665 
666  ## Use this function to append a key point to the end of the trajectory segment
667  # @param phi The phase value (\f$ \phi \f$) of the key point to be added
668  # @param pos The desired position vector at the key point to be added
669  # @param vel The desired velocity vector at the key point to be added
670  # @param acc The desired acceleration vector at the key point to be added
671  # @return None
672  def add_point(self, phi, pos, vel = None, acc = None):
673  n = len(self.point)
674  assert n < self.capacity, genpy.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, "The path capacity is full! Can not take more points than its capacity")
675 
676  if n > 0:
677  assert phi >= self.point[n-1].phi, genpy.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, "Given phi is less than the last point's phase")
678 
679  nn = np.array([None for j in range(self.dim)])
680  pos = genpy.check_type(pos, [np.ndarray], __name__, self.__class__.__name__, sys._getframe().f_code.co_name, 'pos', array_length = self.dim, default = np.copy(nn))
681  vel = genpy.check_type(vel, [np.ndarray], __name__, self.__class__.__name__, sys._getframe().f_code.co_name, 'vel', array_length = self.dim, default = np.copy(nn))
682  acc = genpy.check_type(acc, [np.ndarray], __name__, self.__class__.__name__, sys._getframe().f_code.co_name, 'acc', array_length = self.dim, default = np.copy(nn))
683 
684  self.point.append(Key_Point(phi, pos, vel, acc))
685  self.phi_end = phi
686  self.npoint += 1
687 
688  ## Computes the euclidean distances of each key point from the next and return the result in a vector.
689  # The segment must have at least two key points, otherwise an error is printed and None is returned.
690  # @param None
691  # @return A numpy vector containing the distance between the points
692  def points_dist(self):
693  n_pnt = len(self.point)
694  if n_pnt > 1:
695  dist = np.zeros(n_pnt - 1)
696  for i in range(n_pnt - 1):
697  dist[i] = np.linalg.norm(self.point[i+1].pos - self.point[i].pos)
698  return dist
699  else:
700  assert False, genpy.err_str(__name__, self.__class__.__name__, 'points_dist', 'The segment must have at least two key points')
701 
702  def closest_keypoint_number(self, phi = None):
703  if phi == None:
704  phi = self.current_phi
705  npnt = len(self.point) # Number of key points
706  i = 0
707  while (self.point[i].phi < self.current_phi) and (i < npnt - 1):
708  i += 1
709  return i
710 
711  def get_keypoint(self, point_number):
712  assert point_number < self.npoint, "Argument point_number must be smaller than self.npoint"
713  return self.point[point_number]
714 
715  ## Sets the current phase value
716  # @param phi A float specifying the desired phase value. The given value must not exceed the phase of the last key point.
717  # @return None
718  def set_phase(self, phi):
719  assert phi <= self.phi_end, genpy.err_str(__name__, self.__class__.__name__, 'set_phi', 'Given phi (' + str(phi) + ') is greater than the phase of the last key point (' + str(self.phi_end) + ')')
720  assert len(self.point) > 1, genpy.err_str(__name__, self.__class__.__name__, 'value', 'Can not change the phase when there are less than two key points!')
721  self.current_phi = phi
722  i = self.closest_keypoint_number()
723  k = (self.current_phi - self.point[i-1].phi)/(self.point[i].phi - self.point[i-1].phi)
724  for j in range(self.dim):
725  if (self.point[i].pos[j] != None) and (self.point[i-1].pos[j] != None):
726  self.current_position[j] = self.point[i-1].pos[j] + k*(self.point[i].pos[j] - self.point[i-1].pos[j])
727  else:
728  self.current_position[j] = None
729 
730  if (self.point[i].vel[j] != None) and (self.point[i-1].vel[j] != None):
731  self.current_velocity[j] = self.point[i-1].vel[j] + k*(self.point[i].vel[j] - self.point[i-1].vel[j])
732  else:
733  if (self.point[i].pos[j] != None) and (self.point[i-1].pos[j] != None):
734  self.current_velocity[j] = (self.point[i].pos[j] - self.point[i-1].pos[j])/(self.point[i].phi - self.point[i-1].phi)
735  else:
736  self.current_velocity[j] = None
737 
738  if (self.point[i].acc[j] != None) and (self.point[i-1].acc[j] != None):
739  self.current_acceleration[j] = self.point[i-1].acc[j] + k*(self.point[i].acc[j] - self.point[i-1].acc[j])
740  else:
741  if (self.point[i].vel[j] != None) and (self.point[i-1].vel[j] != None):
742  self.current_acceleration[j] = (self.point[i].vel[j] - self.point[i-1].vel[j])/(self.point[i].vel[j] - self.point[i-1].vel[j])
743  else:
744  self.current_acceleration[j] = None
745  return self.current_position
746 
747  def __getitem__(self, phi):
748  genpy.check_range(phi, 0.0, self.phi_end, __name__, self.__class__.__name__, sys._getframe().f_code.co_name, 'phi')
749  self.set_phase(phi)
750  return self.current_position
751 
752  ## Sets the current phase value
753  # @param phi A float specifying the desired phase value. The given value must not exceed the phase of the last key point.
754  # @return None
755  def set_phi(self, phi):
756  '''
757  check phi to be valid. Many changes must be delivered.
758  1- current_position must be a function returning property pos
759  '''
760  self.set_phase(phi)
761 
762  def map_phi(self, phi_start = 0, phi_end = 1.0):
763  '''
764  Maps the current trajectory phase interval to the given interval (phi_start, phi_end)
765  and adjusts all keypoint phases, velocities and accelerations accordingly
766  The segment will be interpolated after that
767  Also the current_phi will be changed to the mapped value
768  '''
769  self.phi_start = self.point[0].phi
770  delta_phi = self.phi_end - self.phi_start
771  delta_the = phi_end - phi_start
772  r = delta_the/delta_phi
773  for pnt in self.point:
774  x = (pnt.phi - self.phi_start)/delta_phi # x must be between 0 and 1
775  the = phi_start + x*delta_the
776  pnt.phi = the
777  for j in range(self.dim):
778  if pnt.vel[j] != None:
779  pnt.vel[j] = pnt.vel[j]/r
780  if pnt.acc[j] != None:
781  pnt.acc[j] = pnt.acc[j]/(r*r)
782 
783  x = (self.current_phi - self.phi_start)/delta_phi # x must be between 0 and 1
784  self.current_phi = phi_start + x*delta_the
785 
786  self.phi_start = phi_start
787  self.phi_end = phi_end
788 
789  def current_value(self, field_name= 'Position', axis = 0):
790 
791  if field_name == 'Position':
792  return self.current_position[axis]
793  elif field_name == 'Velocity':
794  return self.current_velocity[axis]
795  elif field_name == 'Acceleration':
796  return self.current_acceleration[axis]
797  else:
798  assert False, genpy.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, field_name + " is not a valid value for field_name")
799 
800  def plot(self, axis = 0, n = 100, show_points = False):
801  import matplotlib.pyplot as plt
802 
803  s = self.plot_settings.ylabel(axis)
804  x = np.append(np.arange(0.0, self.phi_end, self.phi_end/n), self.phi_end)
805  y = []
806  for t in x:
807  self.set_phi(t)
808  y.append(self.current_value(field_name = self.plot_settings.figure, axis = axis))
809  if show_points:
810  px = []
811  py = []
812  for pnt in self.point:
813  px.append(pnt.phi)
814  py.append(pnt.value(field_name = self.plot_settings.figure, axis = axis))
815 
816  plt.plot(x, y, px, py, 'o')
817  else:
818  plt.plot(x, y)
819 
820  plt.ylabel(s)
821  plt.xlabel('phi')
822  plt.show()
823 
824  '''
825  def plot_all(self, axis = [0,1], n = 100, y_text = "", figure = 'Position', show_points = False):
826  if y_text == "":
827  s = figure + " of Axis " + str(axis)
828  else:
829  s = figure + " of " + y_text
830 
831  x = np.append(np.arange(0.0, self.phi_end, self.phi_end/n), self.phi_end)
832  for m in axis:
833  y = []
834  for t in x:
835  self.set_phi(t)
836  y.append(self.current_value(field_name = figure, axis = axis))
837  if show_points:
838  px = []
839  py = []
840  for pnt in self.point:
841  px.append(pnt.phi)
842  py.append(pnt.value(field_name = figure, axis = axis))
843 
844  plt.plot(x, y, px, py, 'o')
845  else:
846  plt.plot(x, y)
847 
848  plt.ylabel(s)
849  plt.xlabel('phi')
850  plt.show()
851  '''
852 
853  def scatter_plot(self, figure = 'Position', axis_x = 0, axis_y = 1, n = 100, y_text = "", show_points = False):
854 
855  t = np.append(np.arange(0.0, self.phi_end, self.phi_end/n), self.phi_end)
856  x = []
857  y = []
858  for ph in t:
859  self.set_phi(ph)
860  x.append(self.current_value(field_name = figure, axis = axis_x))
861  y.append(self.current_value(field_name = figure, axis = axis_y))
862 
863  if show_points:
864  px = []
865  py = []
866  for pnt in self.point:
867  px.append(pnt.value(field_name = figure, axis = axis_x))
868  py.append(pnt.value(field_name = figure, axis = axis_y))
869 
870  plt.plot(x, y, px, py, 'o')
871  else:
872  plt.plot(x, y)
873 
874  plt.ylabel('Y')
875  plt.xlabel('X')
876  plt.show()
877 
879  ## Class Constrauctor
880  def __init__(self, dimension = 3, capacity = 3):
881  super(Path_Polynomial, self).__init__(dimension = dimension, capacity = capacity)
882  self.traj = [pl.Polynomial() for j in range(self.dim)]
883 
884  def interpolate(self):
885  '''
886  specifies the coefficients of the trajectory_tbc_path which passes through a number of poses
887  At least one position and one phi is required.
888  phi[0] must be zero.
889  '''
890  n = len(self.point)
891  if n > 1:
892  pnt = [[] for j in range(self.dim)]
893 
894  for i in range(n):
895  for j in range(self.dim):
896  pnt[j].append(pl.Point(t = self.point[i].phi, x = self.point[i].pos[j], v = self.point[i].vel[j], a = self.point[i].acc[j]))
897  for j in range(self.dim):
898  self.traj[j].interpolate_smart(pnt[j])
899 
900  self.phi_end = self.point[n-1].phi
901  return True
902  else:
903  assert False, genpy.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, "No key points defined")
904 
905  def map_phi(self, phi_start = 0, phi_end = 1.0):
906  super(Path_Polynomial, self).map_phi(phi_start = phi_start, phi_end = phi_end)
907  self.interpolate()
908 
909  ## Sets the current phase value
910  # @param phi A float specifying the desired phase value. The given value must not exceed the phase of the last key point.
911  # @return None
912  def set_phi(self, phi):
913  '''
914  check phi to be valid. Many changes must be delivered.
915  1- current_position must be a function returning property pos
916  '''
917  phi = min(phi, self.phi_end)
918  # assert phi <= self.phi_end, genpy.err_str(__name__, self.__class__.__name__, 'set_phi', 'Given phi (' + str(phi) + ') is greater than the phase of the last key point (' + str(self.phi_end) + ')')
919  assert len(self.point) > 1, genpy.err_str(__name__, self.__class__.__name__, 'value', 'Can not change the phase when there are less than two key points!')
920  self.current_phi = phi
921 
922  self.current_position = np.zeros(self.dim)
923  self.current_velocity = np.zeros(self.dim)
924  self.current_acceleration = np.zeros(self.dim)
925  for j in range(self.dim):
926  self.current_position[j] = self.traj[j].position( t = phi )
927  self.current_velocity[j] = self.traj[j].velocity( t = phi )
928  self.current_acceleration[j] = self.traj[j].acceleration( t = phi )
929 
931  def __init__(self, representation = 'vector', parametrization = 'identity', capacity = 2):
932  super(Orientation_Path, self).__init__(dimension = 3, capacity = capacity)
933  self.current_orientation = geo.Orientation_3D(ori = self.current_position, ori_velocity = self.current_velocity, ori_acceleration = self.current_acceleration, representation = representation, parametrization = parametrization)
934 
935  def set_phi(self, phi):
936  super(Orientation_Path, self).set_phi(phi = phi)
937  self.current_orientation[self.current_orientation.representation] = self.current_position
938  self.current_orientation.set_velocity(value = self.current_velocity)
939  self.current_orientation.set_acceleration(value = self.current_acceleration)
940 
941  def add_point(self, phi, ori):
942  rpn = self.current_orientation.representation
943  ori.parametrization = self.current_orientation.parametrization
944  pos = ori[rpn]
945  vel = ori[rpn + '_velocity']
946  acc = ori[rpn + '_acceleration']
947  super(Orientation_Path, self).add_point(phi, pos, vel, acc)
948 
950  def __init__(self, representation = 'vector', parametrization = 'identity'):
951  super(Orientation_Path_Polynomial, self).__init__(representation = representation, parametrization = parametrization)
952 
953 class Trajectory(object):
954 
955  def __init__(self, dimension = 3, capacity = 3):
956  self.dim = dimension
957 
958  self.plot_settings = Path_Plot_Settings(dim = dimension)
959 
960  ## Specifies the default segment capacity.
961  # When a new segment is added, it will have the default capacity unless specified differently.
962  self.capacity = capacity
963 
964  self.current_position = np.zeros(self.dim)
965  self.current_velocity = np.zeros(self.dim)
966  self.current_acceleration = np.zeros(self.dim)
967 
968  self.segment = []
969  self.seg_start = []
970  self.seg_start_pntn = [] # Segment start point number
971  self.phi_end = 0.0
972  self.npoints = 0
973  self.current_phi = 0.0
974 
975  self.accuracy_level = 5
976  self.pos_min = vm.rep(- np.inf, self.dim)
977  self.pos_max = vm.rep( np.inf, self.dim)
978  self.vel_max = np.inf
979  self.acc_max = np.inf
980  self.jrk_max = np.inf
981 
982  def __str__( self ):
983  s = "Trajectory Phase Length : " + str(self.phi_end) + '\n'
984  s += "Number of Segments : " + str(len(self.segment)) + '\n'
985  for i in range(len(self.segment)):
986  s += "Segment Number " + str(i) + " starting at phi = " + str(self.seg_start[i]) + ': \n'
987  s += str(self.segment[i])
988  s += "****************************************** \n"
989  return s
990 
991  def locate_point(self, point_number):
992  assert point_number < self.npoints, "point number must be smaller than total number of points"
993  i = 1
994  nseg = len(self.segment)
995  while (self.seg_start_pntn[i] <= point_number) and (i < nseg - 1):
996  i += 1
997 
998  if i == nseg - 1:
999  sn = i
1000  else:
1001  sn = i - 1
1002  pn = point_number - self.seg_start_pntn[sn]
1003  # if pn = 0, then the point is a boundary point
1004  return (sn, pn)
1005 
1006  def get_keypoint(self, point_number):
1007  (sn, pn) = self.locate_point(point_number)
1008  return self.segment[sn].point[pn]
1009 
1010  def current_value(self, field_name= 'Position', axis = 0):
1011 
1012  if field_name == 'Position':
1013  return self.current_position[axis]
1014  elif field_name == 'Velocity':
1015  return self.current_velocity[axis]
1016  elif field_name == 'Acceleration':
1017  return self.current_acceleration[axis]
1018  else:
1019  assert False, genpy.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, field_name + " is not a valid value for field_name")
1020 
1021  def add_segment(self, new_seg):
1022 
1023  assert len(new_seg.point) > 0
1024  if len(self.segment) == 0: # First segment
1025  self.npoints = len(new_seg.point)
1026  else:
1027  self.npoints += len(new_seg.point) - 1
1028  # If not the first segment, check to make sure the first point of new segment is identical to the last point of previous segment
1029 
1030  self.segment.append(new_seg)
1031  self.seg_start.append(self.phi_end)
1032  self.phi_end += new_seg.phi_end
1033 
1034  self.seg_start_pntn.append(self.npoints - 1)
1035 
1036  def new_segment(self, capacity = None):
1037  capacity = genpy.check_type(capacity, [int], __name__, self.__class__.__name__, sys._getframe().f_code.co_name, 'capacity', default = self.capacity)
1038  # nn = np.array([None for j in range(self.dim)])
1039  lsi = len(self.segment) - 1
1040  seg = Path(dimension = self.dim, capacity = capacity)
1041  assert len(self.segment[lsi].point) > 1, genpy.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, 'Can not create a new segment. The last segment needs at least two points.')
1042  lslp = self.segment[lsi].point[len(self.segment[lsi].point) - 1] # last_seg_last_point
1043  seg.add_point(0.0, lslp.pos, lslp.vel, lslp.acc)
1044  self.add_segment(seg)
1045 
1046  def points_dist(self):
1047  dist = np.array([])
1048  for seg in self.segment:
1049  dist = np.append(dist, seg.points_dist())
1050  return dist
1051 
1052  def adjust_phase_by_distance(self, gain = 1.0):
1053  n_seg = len(self.segment)
1054  self.seg_start[0] = 0.0
1055  self.phi_start = 0.0
1056  for i in range(n_seg - 1):
1057  d = gain*sum(self.segment[i].points_dist())
1058  self.segment[i].map_phi(phi_end = d)
1059  self.seg_start[i + 1] = self.seg_start[i] + self.segment[i].phi_end
1060 
1061  d = gain*sum(self.segment[n_seg - 1].points_dist())
1062  self.segment[n_seg - 1].map_phi(phi_end = d)
1063  self.phi_end = self.seg_start[n_seg - 1] + self.segment[n_seg - 1].phi_end
1064 
1065  def closest_segment_number(self, phi):
1066  if phi > self.phi_end:
1067  phi = self.phi_end
1068 
1069  self.current_phi = phi
1070 
1071  lsi = len(self.segment) - 1 # last segment index
1072 
1073  i = 0
1074  while (phi > self.seg_start[i] + self.segment[i].phi_end) and (i <= lsi):
1075  i += 1
1076 
1077  return i
1078 
1079  def set_phi(self, phi):
1080  i = self.closest_segment_number(phi)
1081  self.segment[i].set_phi(phi - self.seg_start[i])
1082  self.current_position = self.segment[i].current_position
1083  self.current_velocity = self.segment[i].current_velocity
1084  self.current_acceleration = self.segment[i].current_acceleration
1085 
1086  def set_phase(self, phi):
1087  i = self.closest_segment_number(phi)
1088  self.segment[i].set_phase(phi - self.seg_start[i])
1089  self.current_position = self.segment[i].current_position
1090  self.current_velocity = self.segment[i].current_velocity
1091  self.current_acceleration = self.segment[i].current_acceleration
1092 
1093  def position(self, phi):
1094  genpy.check_range(phi, 0.0, self.phi_end, __name__, self.__class__.__name__, sys._getframe().f_code.co_name, 'phi')
1095  self.set_phase(phi)
1096  return self.current_position
1097 
1098  def positions(self, phi, n, d_phi ):
1099  P = np.zeros((n, self.dim))
1100  for i in range(n):
1101  self.set_phase(phi + i*d_phi)
1102  P[n - i - 1] = np.copy(self.current_position)
1103  return P
1104 
1105  def add_position(self, phi, pos, smooth = False):
1106  if gen.equal(self.phi_end, 0.0):
1107  self.add_point(phi, pos, vel = np.zeros(self.dim), acc = np.zeros(self.dim))
1108  return None
1109 
1110  assert phi > self.phi_end
1111 
1112  lsi = len(self.segment) - 1
1113  lpi = len(self.segment[lsi].point) - 1
1114  p0 = self.segment[lsi].point[lpi].pos
1115  v0 = self.segment[lsi].point[lpi].vel
1116  a0 = self.segment[lsi].point[lpi].acc
1117  dt = phi - self.phi_end
1118 
1119  (P, V, A) = feasible_position(pos, p0, v0, self.pos_min, self.pos_max, self.vel_max, self.acc_max, dt, smooth = smooth)
1120 
1121  self.add_point(phi, pos = P, vel = V, acc = A)
1122 
1123  def add_velocity(self, phi, vel):
1124  assert not self.npoints == 0, "An initial position must exist"
1125 
1126  assert phi > self.phi_end
1127 
1128  lsi = len(self.segment) - 1
1129  lpi = len(self.segment[lsi].point) - 1
1130  p0 = self.segment[lsi].point[lpi].pos
1131  v0 = self.segment[lsi].point[lpi].vel
1132  a0 = self.segment[lsi].point[lpi].acc
1133  dt = phi - self.phi_end
1134 
1135  (P, V, A) = feasible_position(v0 + vel*dt, p0, v0, self.pos_min, self.pos_max, self.vel_max, self.acc_max, dt)
1136 
1137  self.add_point(phi, pos = P, vel = V, acc = A)
1138 
1139  def last_position(self):
1140  lsi = len(self.segment) - 1
1141  assert lsi >= 0, "Empty Trajectory!"
1142  lpi = len(self.segment[lsi].point) - 1
1143  return self.segment[lsi].point[lpi].pos
1144 
1145  def last_velocity(self):
1146  lsi = len(self.segment) - 1
1147  assert lsi >= 0, "Empty Trajectory!"
1148  lpi = len(self.segment[lsi].point) - 1
1149  return self.segment[lsi].point[lpi].vel
1150 
1151  '''
1152  def add_position(self, phi, pos):
1153  if gen.equal(self.phi_end, 0.0):
1154  self.add_point(phi, pos, vel = np.zeros(self.dim), acc = np.zeros(self.dim))
1155  return None
1156 
1157  assert phi > self.phi_end
1158  dt = phi - self.phi_end
1159  al = self.accuracy_level
1160  # if accuracy_level is m we need m+1 position points to compute velocity, m+2 points to compute acceleration
1161 
1162  j = 0
1163  while (j < al) and (self.phi_end < (al - j)*dt):
1164  j = j + 1
1165 
1166  al = al - j
1167 
1168  x = self.positions(phi = self.phi_end - al*dt, n = al + 1, d_phi = dt)
1169  x = np.append([pos], x, axis = 0)
1170  (P, V, A) = finite_difference_estimate(x, self.pos_min, self.pos_max, self.vel_max, self.acc_max, dt)
1171  self.add_point(phi, pos = P, vel = V, acc = A)
1172  '''
1173  ## Use this function to append a key point to the end of the trajectory with desired position, velocity and acceleration
1174  # The given point will be added to the end of the last segment of the trajectory, unless the segment capacity if full
1175  # (number of segment points equals the capacity of that segment).
1176  # In this case, a new segment will be added and the given point is added to the new segment.
1177  # The capacity of the added segment is specified by property self.capacity
1178  # @param phi The phase value (\f$ \phi \f$) of the key point to be added.
1179  # This argument should be greater than the phase of the last added point specified by the property self.phi_end
1180  # @param pos The desired position vector at the key point to be added
1181  # @param vel The desired velocity vector at the key point to be added
1182  # @param acc The desired acceleration vector at the key point to be added
1183  # @return None
1184  def add_point(self, phi, pos, vel = None, acc = None):
1185  genpy.check_type(phi, [float, np.float64], __name__, self.__class__.__name__,sys._getframe().f_code.co_name, 'phi', default = None)
1186 
1187  lsi = len(self.segment) - 1
1188 
1189  if lsi < 0:
1190  assert gen.equal(phi, 0.0), genpy.err_str(__name__, self.__class__.__name__,sys._getframe().f_code.co_name, "Given phi is " + str(phi) + " which should be zero for the first point")
1191  seg = Path(dimension = self.dim)
1192  seg.add_point(0.0, pos, vel, acc)
1193  self.add_segment(seg)
1194  else:
1195  assert (phi > self.phi_end) and (not gen.equal(phi, self.phi_end)), genpy.err_str(__name__, self.__class__.__name__,sys._getframe().f_code.co_name, "Given phi is " + str(phi) + " which should be greater than the last point's phase " + str(self.phi_end))
1196  if len(self.segment[lsi].point) < self.segment[lsi].capacity:
1197  phi0 = self.seg_start[lsi]
1198  self.segment[lsi].add_point(phi - phi0, pos, vel, acc)
1199  self.npoints += 1
1200  else:
1201  self.new_segment()
1202  self.segment[lsi+1].add_point(phi-self.phi_end, pos, vel, acc)
1203  self.npoints += 1
1204 
1205  lsi = len(self.segment) - 1
1206  self.phi_end = self.seg_start[lsi] + self.segment[lsi].phi_end
1207 
1208  def clear_velocities(self):
1209  nn = np.array([None for i in range(self.dim)])
1210  for seg in self.segment:
1211  for pnt in seg.point:
1212  pnt.vel = np.copy(nn)
1213 
1215  nn = np.array([None for i in range(self.dim)])
1216  for seg in self.segment:
1217  for pnt in seg.point:
1218  pnt.acc = np.copy(nn)
1219 
1220  ## private
1221  def add_vector(self, delta_phi, delta_pos, vel = None, acc = None):
1222  assert delta_phi > 0
1223  phi = self.phi_end + delta_phi
1224  lsi = len(self.segment) - 1
1225  lslp = self.segment[lsi].point[len(self.segment[lsi].point) - 1] # last_seg_last_point
1226 
1227  pos = lslp.pos + delta_pos
1228  self.add_point(phi, pos, vel, acc)
1229 
1230  def map_phi(self, phi_start = 0, phi_end = 1.0):
1231  '''
1232  Maps the current trajectory phase interval to the given interval (phi_start, phi_end)
1233  and adjusts all keypoint phases, velocities and accelerations accordingly
1234  All the segments will be interpolated after that
1235  Also the current_phi will be changed to the mapped value
1236  '''
1237  self.phi_start = self.seg_start[0]
1238  n_seg = len(self.segment)
1239  delta_phi = self.phi_end - self.phi_start
1240  delta_the = phi_end - phi_start
1241  r = delta_the/delta_phi
1242  for i in range(n_seg):
1243  x = (self.seg_start[i] - self.phi_start)/delta_phi # x must be between 0 and 1
1244  the = phi_start + x*delta_the
1245  self.segment[i].map_phi(0.0, self.segment[i].phi_end*r)
1246  self.seg_start[i] = the
1247 
1248  x = (self.current_phi - self.phi_start)/delta_phi # x must be between 0 and 1
1249  self.current_phi = phi_start + x*delta_the
1250 
1251  self.phi_start = phi_start
1252  self.phi_end = phi_end
1253 
1254  def get_range(self, axis, figure):
1255  if figure == 'Position':
1256  miny = self.pos_min[axis]
1257  maxy = self.pos_max[axis]
1258  elif figure == 'Velocity':
1259  miny = - self.vel_max
1260  maxy = self.vel_max
1261  elif figure == 'Acceleration':
1262  miny = - self.acc_max
1263  maxy = self.acc_max
1264  else:
1265  assert False, "Unknown figure"
1266 
1267  return (miny, maxy)
1268 
1269  def get_plot(self, plt = None, axis = 0, figure = 'Position'):
1270  if plt == None:
1271  import matplotlib.pyplot as plt
1272 
1273  s = self.plot_settings.ylabel(axis)
1274 
1275  fig, ax = plt.subplots()
1276  self.draw_points(ax, axis = axis)
1277 
1278  if self.plot_settings.show_range:
1279  (miny, maxy) = self.get_range(axis, figure)
1280  ax.annotate('Upper Bound: ' + '{:04.3f}'.format(maxy),
1281  xy = (self.phi_end, maxy), xycoords = 'data',
1282  xytext = (- 150, -17), textcoords = 'offset points',
1283  horizontalalignment='left', verticalalignment='bottom',
1284  color = self.plot_settings.plot_color[axis])
1285  ax.annotate('Lower Bound: ' + '{:04.3f}'.format(miny),
1286  xy = (0.0, miny), xycoords = 'data',
1287  xytext = (150, 17), textcoords = 'offset points',
1288  horizontalalignment='right', verticalalignment='top',
1289  # arrowprops=dict(facecolor='black', shrink=0.05),
1290  color = self.plot_settings.plot_color[axis])
1291 
1292  plt.axhline(linewidth = 4, color = self.plot_settings.plot_color[axis], y = miny)
1293  plt.axhline(linewidth = 4, color = self.plot_settings.plot_color[axis], y = maxy)
1294  plt.axhspan(miny, maxy, facecolor = self.plot_settings.plot_color[axis], alpha = 0.2)
1295 
1296  plt.ylabel(s)
1297  plt.xlabel('phi')
1298  return plt
1299 
1300  def plot(self, axis = 0, figure = 'Position'):
1301  plt = self.get_plot(axis = axis, figure = figure)
1302  plt.show()
1303 
1304  def plot_all(self, legend = True):
1305  import matplotlib.pyplot as plt
1306 
1307  s = self.plot_settings.ylabel(axis = None)
1308 
1309  fig, ax = plt.subplots()
1310  for j in range(self.dim):
1311  self.draw_points(ax, axis = j)
1312  '''
1313  if self.plot_settings.grid:
1314  plt.grid(True)
1315  '''
1316  plt.xlabel('phi')
1317  plt.ylabel(s)
1318 
1319  if legend:
1320  ax.legend([self.plot_settings.axis_label[i] for i in range(self.dim)], loc='upper right')
1321  plt.show()
1322 
1323  def __getitem__(self, axis):
1324  # Returns the axis values as a vector,
1325  # if axis = -1 then it returns the phase (phi) as a vector
1326  # This function can be used for plotting
1327  assert axis < self.dim, genpy.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, str(axis) + " is not a valid value for argument axis: Must be lower than dimension")
1328  px = []
1329  if axis == -1:
1330  for i in range(len(self.segment)):
1331  for pnt in self.segment[i].point:
1332  px.append(self.seg_start[i] + pnt.phi)
1333  else:
1334  for i in range(len(self.segment)):
1335  for pnt in self.segment[i].point:
1336  px.append(pnt.value(field_name = self.plot_settings.figure, axis = axis))
1337 
1338  return px
1339 
1340  def draw_points(self, ax, axis = 0, ltype = '-'):
1341 
1342  ax.plot(self[-1], self[axis], ltype, color = self.plot_settings.plot_color[axis])
1343 
1344  def csv_str(self, n = 100, header = True):
1345 
1346  x = np.append(np.arange(0.0, self.phi_end, self.phi_end/n), self.phi_end)
1347  if header:
1348  dic = {'Position':'x', 'Velocity':'v', 'Acceleration':'a'}
1349  s = 'phi'
1350  for i in range(self.dim):
1351  s += ',' + dic[self.plot_settings.figure] + str(i)
1352  s += '\n'
1353  else:
1354  s = ''
1355  for t in x:
1356  s += str(t)
1357  self.set_phi(t)
1358  for j in range(self.dim):
1359  if self.plot_settings.figure == 'Position':
1360  s += ',' + str(self.current_position[j])
1361  elif self.plot_settings.figure == 'Velocity':
1362  s += ',' + str(self.current_velocity[j])
1363  elif self.plot_settings.figure == 'Acceleration':
1364  s += ',' + str(self.current_acceleration[j])
1365  s += '\n'
1366 
1367  return s
1368 
1369  def write_csv(self, filename, n = 100, path = '', header = True):
1370  FILE_HANDLE = open(filename, "w")
1371  FILE_HANDLE.write(self.csv_str(n = n , header = header))
1372 
1373  def matrix(self, n = 100, figures = ['Position']):
1374  # genpy.check_valid(figures, all_figures, __name__, self.__class__.__name__, sys._getframe().f_code.co_name, figures)
1375  T = np.append(np.arange(0.0, self.phi_end, self.phi_end/n), self.phi_end)
1376  nfig = len(figures)
1377  nrow = len(T)
1378  M = np.zeros((nrow, nfig*self.dim + 1))
1379  i = 0
1380  # print len(T)
1381  for t in T:
1382  # print i, '-',
1383  j = 0
1384  M[i, j] = t
1385  j += 1
1386  self.set_phi(t)
1387  if 'Position' in figures:
1388  M[i, j:(j + self.dim)] = self.current_position
1389  j += self.dim
1390  if 'Velocity' in figures:
1391  M[i, j:(j + self.dim)] = self.current_velocity
1392  j += self.dim
1393  if 'Acceleration' in figures:
1394  M[i, j:(j + self.dim)] = self.current_acceleration
1395  j += self.dim
1396  i += 1
1397  return M
1398 
1399  def plot2d(self, figure = 'Position', axis_x = 0, axis_y = 1, n = 100, show_points = False):
1400 
1401  t = np.append(np.arange(0.0, self.phi_end, self.phi_end/n), self.phi_end)
1402  x = []
1403  y = []
1404  for ph in t:
1405  self.set_phi(ph)
1406  x.append(self.current_value(field_name = self.plot_settings.figure, axis = axis_x))
1407  y.append(self.current_value(field_name = self.plot_settings.figure, axis = axis_y))
1408 
1409  if show_points:
1410  px = []
1411  py = []
1412  for i in range(len(self.segment)):
1413  for pnt in self.segment[i].point:
1414  px.append(pnt.value(field_name = self.plot_settings.figure, axis = axis_x))
1415  py.append(pnt.value(field_name = self.plot_settings.figure, axis = axis_y))
1416 
1417  plt.plot(x, y, px, py, 'o')
1418  else:
1419  plt.plot(x, y)
1420 
1421  plt.ylabel('Y')
1422  plt.xlabel('X')
1423  plt.show()
1424 
1425  def plot3d(self, figure = 'Position', axis_x = 0, axis_y = 1, axis_z = 2, n = 100, label = "", show_points = False):
1426  import matplotlib as mpl
1427  from mpl_toolkits.mplot3d import Axes3D
1428 
1429  t = np.append(np.arange(0.0, self.phi_end, self.phi_end/n), self.phi_end)
1430  mpl.rcParams['legend.fontsize'] = 10
1431  fig = plt.figure()
1432  ax = fig.gca(projection='3d')
1433 
1434  x = []
1435  y = []
1436  z = []
1437  for ph in t:
1438  self.set_phi(ph)
1439  x.append(self.current_value(field_name = self.plot_settings.figure, axis = axis_x))
1440  y.append(self.current_value(field_name = self.plot_settings.figure, axis = axis_y))
1441  z.append(self.current_value(field_name = self.plot_settings.figure, axis = axis_z))
1442 
1443  ax.plot(x, y, z, label = label)
1444  ax.legend()
1445  plt.show()
1446 
1448  def __init__(self, representation = 'vector', parametrization = 'identity'):
1449  super(Orientation_Trajectory, self).__init__()
1450  self.current_orientation = geo.Orientation_3D(self.current_position, ori_velocity = self.current_velocity, ori_acceleration = self.current_acceleration, representation = representation, parametrization = parametrization)
1451 
1452  def set_phi(self, phi):
1453  super(Orientation_Trajectory, self).set_phi(phi = phi)
1454  self.current_orientation[self.current_orientation.representation] = self.current_position
1455  self.current_orientation.set_velocity(value = self.current_velocity)
1456  self.current_orientation.set_acceleration(value = self.current_acceleration)
1457 
1458  def add_point(self, phi, ori):
1459  rpn = self.current_orientation.representation
1460  ori.parametrization = self.current_orientation.parametrization
1461  pos = ori[rpn]
1462  vel = ori[rpn + '_velocity']
1463  acc = ori[rpn + '_acceleration']
1464  super(Orientation_Trajectory, self).add_point(phi, pos, vel, acc)
1465 
1467  def __init__(self, dimension = 3, capacity = 3):
1468  super(Trajectory_Polynomial, self).__init__(dimension = dimension, capacity = capacity)
1469 
1470  def new_segment(self, capacity = None):
1471  capacity = genpy.check_type(capacity, [int], __name__, self.__class__.__name__, sys._getframe().f_code.co_name, 'capacity', default = self.capacity)
1472  # nn = np.array([None for j in range(self.dim)])
1473  lsi = len(self.segment) - 1
1474  assert len(self.segment[lsi].point) > 1, genpy.err_str(__name__, self.__class__.__name__, sys._getframe().f_code.co_name, 'Can not create a new segment. The last segment needs at least two points.')
1475  seg = Path_Polynomial(dimension = self.dim, capacity = capacity)
1476  lslp = self.segment[lsi].point[len(self.segment[lsi].point) - 1] # last_seg_last_point
1477  seg.add_point(0.0, lslp.pos, lslp.vel, lslp.acc)
1478  self.add_segment(seg)
1479 
1480  def interpolate(self):
1481  for seg in self.segment:
1482  seg.interpolate()
1483 
1484  def add_point(self, phi, pos, vel = None, acc = None):
1485  genpy.check_type(phi, [float, np.float64], __name__, self.__class__.__name__,sys._getframe().f_code.co_name, 'phi', default = None)
1486 
1487  lsi = len(self.segment) - 1
1488 
1489  if lsi < 0:
1490  assert gen.equal(phi, 0.0), genpy.err_str(__name__, self.__class__.__name__,sys._getframe().f_code.co_name, "Given phi is " + str(phi) + " which should be zero for the first point")
1491  seg = Path_Polynomial(dimension = self.dim)
1492  seg.add_point(0.0, pos, vel, acc)
1493  self.add_segment(seg)
1494  else:
1495  assert (phi > self.phi_end) and (not gen.equal(phi, self.phi_end)), genpy.err_str(__name__, self.__class__.__name__,sys._getframe().f_code.co_name, "Given phi is " + str(phi) + " which should be greater than the last point's phase " + str(self.phi_end))
1496  if len(self.segment[lsi].point) < self.segment[lsi].capacity:
1497  phi0 = self.seg_start[lsi]
1498  self.segment[lsi].add_point(phi - phi0, pos, vel, acc)
1499  self.npoints += 1
1500  else:
1501  self.new_segment()
1502  self.segment[lsi+1].add_point(phi-self.phi_end, pos, vel, acc)
1503  self.npoints += 1
1504 
1505  lsi = len(self.segment) - 1
1506  self.phi_end = self.seg_start[lsi] + self.segment[lsi].phi_end
1507 
1508  def draw_polynomial(self, ax, axis = 0, figure = None, n = 1000, ltype = '-'):
1509  if figure == None:
1510  figure = self.plot_settings.figure
1511  x = np.append(np.arange(0.0, self.phi_end, self.phi_end/n), self.phi_end)
1512  y = []
1513  for t in x:
1514  self.set_phi(t)
1515  y.append(self.current_value(field_name = figure, axis = axis))
1516 
1517  ax.plot(x, y, ltype)
1518 
1519  def plot(self, axis = 0, figure = None, n = 100, show_points = False):
1520  if figure == None:
1521  figure = self.plot_settings.figure
1522  import matplotlib.pyplot as plt
1523  s = self.plot_settings.ylabel(axis)
1524  fig, ax = plt.subplots()
1525  self.draw_polynomial(ax, axis = axis, n = n)
1526 
1527  if show_points:
1528  self.draw_points(ax, axis = axis, ltype = 'o')
1529 
1530  if self.plot_settings.show_range:
1531 
1532  (miny, maxy) = self.get_range(axis, figure)
1533 
1534  ax.annotate('Upper Bound: ' + '{:04.3f}'.format(maxy),
1535  xy = (self.phi_end, maxy), xycoords = 'data',
1536  xytext = (- 150, -17), textcoords = 'offset points',
1537  horizontalalignment='left', verticalalignment='bottom',
1538  color = self.plot_settings.plot_color[axis])
1539  ax.annotate('Lower Bound: ' + '{:04.3f}'.format(miny),
1540  xy = (0.0, miny), xycoords = 'data',
1541  xytext = (150, 17), textcoords = 'offset points',
1542  horizontalalignment='right', verticalalignment='top',
1543  # arrowprops=dict(facecolor='black', shrink=0.05),
1544  color = self.plot_settings.plot_color[axis])
1545 
1546  plt.axhline(linewidth = 4, color = self.plot_settings.plot_color[axis], y = miny)
1547  plt.axhline(linewidth = 4, color = self.plot_settings.plot_color[axis], y = maxy)
1548  plt.axhspan(miny, maxy, facecolor = self.plot_settings.plot_color[axis], alpha = 0.2)
1549 
1550  plt.ylabel(s)
1551  plt.xlabel('phi')
1552  plt.show()
1553 
1555  self.interpolate()
1556  lsi = len(self.segment) - 1
1557 
1558  for i in range(lsi + 1):
1559  lp = self.segment[i].point[len(self.segment[i].point) - 1] # last point
1560  ip1 = i + 1
1561  if ip1 > lsi:
1562  ip1 = 0
1563  for j in range(self.dim):
1564  self.segment[i].set_phi(lp.phi)
1565  self.segment[ip1].set_phi(0.0)
1566  if lp.vel[j] == None:
1567  if self.segment[ip1].point[0].vel[j] == None:
1568  v = 0.5*(self.segment[i].current_velocity[j] + self.segment[ip1].current_velocity[j])
1569  lp.vel[j] = v
1570  self.segment[ip1].point[0].vel[j] = v
1571  else:
1572  lp.vel[j] = self.segment[ip1].point[0].vel[j]
1573  elif self.segment[ip1].point[0].vel[j] == None:
1574  self.segment[ip1].point[0].vel[j] = lp.vel[j]
1575  elif not gen.equal(lp.vel[j], self.segment[ip1].point[0].vel[j]):
1576  v = 0.5*(lp.vel[j] + self.segment[ip1].point[0].vel[j])
1577  lp.vel[j] = v
1578  self.segment[ip1].point[0].vel[j] = v
1579  else:
1580  # Already Consistent! Do nothing
1581  assert True
1582 
1583  self.interpolate()
1584 
1586  self.interpolate()
1587  lsi = len(self.segment) - 1
1588  self.segment[0].point[0].acc = np.zeros(self.dim)
1589  for i in range(lsi + 1):
1590  lp = self.segment[i].point[len(self.segment[i].point) - 1] # last point of segment i
1591  ip1 = i + 1
1592  if ip1 > lsi:
1593  ip1 = 0
1594  for j in range(self.dim):
1595  self.segment[i].set_phi(lp.phi)
1596  self.segment[ip1].set_phi(0.0)
1597  if lp.acc[j] == None:
1598  if self.segment[ip1].point[0].acc[j] == None:
1599  a = 0.5*(self.segment[i].current_acceleration[j] + self.segment[ip1].current_acceleration[j])
1600  lp.acc[j] = a
1601  self.segment[ip1].point[0].acc[j] = a
1602  else:
1603  lp.acc[j] = self.segment[ip1].point[0].acc[j]
1604  elif self.segment[ip1].point[0].acc[j] == None:
1605  self.segment[ip1].point[0].acc[j] = lp.acc[j]
1606  elif not gen.equal(lp.acc[j], self.segment[ip1].point[0].acc[j]):
1607  a = 0.5*(lp.acc[j] + self.segment[ip1].point[0].acc[j])
1608  lp.acc[j] = a
1609  self.segment[ip1].point[0].acc[j] = a
1610  else:
1611  # Already Consistent! Do nothing
1612  assert True
1613 
1614  self.interpolate()
1615 
1617  def __init__(self, representation = 'vector', parametrization = 'identity'):
1618  super(Orientation_Trajectory_Polynomial, self).__init__(representation = representation, parametrization = parametrization)
def __init__
Class Constructor.
Definition: trajectory.py:633
def add_point
Use this function to append a key point to the end of the trajectory with desired position...
Definition: trajectory.py:1184
dim
An integer indicating the dimension of space in which the kepy point is defined.
Definition: trajectory.py:581
def set_phi
Sets the current phase value.
Definition: trajectory.py:912
npoint
An integer indicating the number of points.
Definition: trajectory.py:641
This class, introduces a structure for a key point in the multi-dimensional space.
Definition: trajectory.py:570
def add_point
Use this function to append a key point to the end of the trajectory segment.
Definition: trajectory.py:672
capacity
Specifies the default segment capacity.
Definition: trajectory.py:962
def __str__
String representation of the instance (trajectory segment)
Definition: trajectory.py:657
def points_dist
Computes the euclidean distances of each key point from the next and return the result in a vector...
Definition: trajectory.py:692
phi_end
A float indicating the phase of the last key point (Maximum value for phi)
Definition: trajectory.py:639
This class contains properties and methods for a Trajectory Segment A trajectory is established of ...
Definition: trajectory.py:629
def value
Use this function to get the current value of position, velocity or acceleration in a desired dimensi...
Definition: trajectory.py:613
capacity
An integer indicating how many key points the path can hold.
Definition: trajectory.py:643
def __str__
This function is the string representation of the key point.
Definition: trajectory.py:598
def set_phi
Sets the current phase value.
Definition: trajectory.py:755
def set_phase
Sets the current phase value.
Definition: trajectory.py:718