ROBOOP, A Robotics Object Oriented Package in C++
trajectory.cpp
Go to the documentation of this file.
1 /*
2 Copyright (C) 2002-2004 Etienne Lachance
3 
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
8 
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13 
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 Report problems and direct all questions to:
20 
21 email: etienne.lachance@polymtl.ca or richard.gourdeau@polymtl.ca
22 
23 -------------------------------------------------------------------------------
24 Revision_history:
25 
26 2004/06/02: Etienne Lachance
27  -Added class Trajectory_Select.
28 
29 2004/07/01: Ethan Tira-Thompson
30  -Added support for newmat's use_namespace #define, using ROBOOP namespace.
31 
32 2005/06/10: Etienne Lachance
33  -Missing file warning message.
34 
35 2005/11/06: Etienne Lachance
36  - No need to provide a copy constructor and the assignment operator
37  (operator=) for Spl_Quaternion, Spl_Cubic and Spl_path classes. Instead we
38  use the one provide by the compiler.
39 -------------------------------------------------------------------------------
40 */
41 
47 #include "trajectory.h"
48 
49 #ifdef use_namespace
50 namespace ROBOOP {
51  using namespace NEWMAT;
52 #endif
53 
54 
55 Spl_cubic::Spl_cubic(const Matrix & pts)
70 {
71  int N = pts.Ncols();
72  bad_data = false;
73  nb_path = pts.Nrows()-1;
74 
75  if(!N)
76  {
77  bad_data = true;
78  cerr << "Spl_cubic::Spl_cubic: size of time vector is zero." << endl;
79  return;
80  }
81  if(N <= 3)
82  {
83  bad_data = true;
84  cerr << "Spl_cubic::Spl_cubic: need at least 4 points to produce a cubic spline." << endl;
85  return;
86  }
87  if(!nb_path)
88  {
89  bad_data = true;
90  cerr << "Spl_cubic::Spl_cubic: No data for each points." << endl;
91  return;
92  }
93 
94  ColumnVector delta(N-1), beta(N-1);
95  tk = pts.SubMatrix(1,1,1,N); // time at sampling point
96 
97  for(int i = 1; i <= N-1; i++)
98  {
99  delta(i) = pts(1,i+1) - pts(1,i); // eq 5.58d Angeles
100 
101  if(!delta(i))
102  {
103  bad_data = true;
104  cerr << "Spl_cubic::Spl_cubic: time between input points is zero" << endl;
105  return;
106  }
107  beta(i) = 1/delta(i); // eq 5.58e Angeles
108  }
109 
110  Matrix A(N-2, N-2); A = 0; // eq 5.59 Angeles
111  A(1,1) = 2*(delta(1)+delta(2));
112  A(1,2) = delta(2);
113  for(int j = 2; j <= N-2; j++)
114  {
115  A(j,j-1) = delta(j);
116  A(j,j) = 2*(delta(j)+delta(j+1));
117  if( (j+1) <= A.Ncols())
118  A(j,j+1) = delta(j+1);
119  }
120 
121  Matrix C(N-2, N); C = 0; // eq 5.58c Angeles
122  for(int k = 1; k <= N-2; k++)
123  {
124  C(k,k) = beta(k);
125  C(k,k+1) = -(beta(k)+beta(k+1));
126  C(k,k+2) = beta(k+1);
127  }
128 
129  Matrix _6AiC = 6*A.i()*C; // eq 5.58a Angeles
130 
131  ColumnVector dd_s(N); // second spline derivative at sampling points
132  dd_s(1) = dd_s(N) = 0; // second der is 0 on first and last point of natural splines.
133 
134  Ak = Matrix(nb_path, N-1);
135  Bk = Matrix(nb_path, N-1);
136  Ck = Matrix(nb_path, N-1);
137  Dk = Matrix(nb_path, N-1);
138 
139  for(int ii = 2; ii <= nb_path+1; ii++)
140  {
141  dd_s.SubMatrix(2,N-1,1,1) = _6AiC*pts.SubMatrix(ii,ii,1,N).t();
142 
143  for(int jj = 1; jj < N; jj++)
144  {
145  // eq 5.55a - 5.55d Angeles
146  Ak(ii-1,jj) = 1/(6.0*delta(jj))*(dd_s(jj+1) - dd_s(jj));
147  Bk(ii-1,jj) = 1/2.0*dd_s(jj);
148  Ck(ii-1,jj) = (pts(ii,jj+1)-pts(ii,jj))/delta(jj) -
149  1/6.0*delta(jj)*(dd_s(jj+1) + 2*dd_s(jj));
150  Dk(ii-1,jj) = pts(ii,jj);
151  }
152  }
153 }
154 
155 short Spl_cubic::interpolating(const Real t, ColumnVector & s)
157 {
158  if(bad_data)
159  {
160  cerr << "Spl_cubic::interpolating: data is not good. Problems occur in constructor." << endl;
161  return BAD_DATA;
162  }
163 
164  for(int i = 1; i < tk.Ncols(); i++)
165  if( (t >= tk(i)) && (t < tk(i+1)) )
166  {
167  s = Ak.SubMatrix(1,nb_path,i,i)*pow(t - tk(i), 3) +
168  Bk.SubMatrix(1,nb_path,i,i)*pow(t - tk(i),2) +
169  Ck.SubMatrix(1,nb_path,i,i)*(t - tk(i)) +
170  Dk.SubMatrix(1,nb_path,i,i);
171  return 0;
172  }
173 
174  cerr << "Spl_cubic::interpolating: t is out of range." << endl;
175  return NOT_IN_RANGE;
176 }
177 
178 
179 short Spl_cubic::first_derivative(const Real t, ColumnVector & ds)
181 {
182  if(bad_data)
183  {
184  cerr << "Spl_cubic::first_derivative: data is not good. Problems occur in constructor." << endl;
185  return BAD_DATA;
186  }
187 
188  for(int i = 1; i < tk.Ncols(); i++)
189  if( (t >= tk(i)) && (t < tk(i+1)) )
190  {
191  ds = 3*Ak.SubMatrix(1,nb_path,i,i)*pow(t - tk(i), 2) +
192  2*Bk.SubMatrix(1,nb_path,i,i)*(t - tk(i)) +
193  Ck.SubMatrix(1,nb_path,i,i);
194  return 0;
195  }
196 
197  cerr << "Spl_cubic::first_derivative: t not in range." << endl;
198  return NOT_IN_RANGE;
199 }
200 
201 
202 short Spl_cubic::second_derivative(const Real t, ColumnVector & ds)
204 {
205  if(bad_data)
206  {
207  cerr << "Spl_cubic::second_derivative: data is not good. Problems occur in constructor." << endl;
208  return BAD_DATA;
209  }
210 
211  for(int i = 1; i < tk.Ncols(); i++)
212  if( (t >= tk(i)) && (t < tk(i+1)) )
213  {
214  ds = 6*Ak.SubMatrix(1,nb_path,i,i)*(t - tk(i)) +
215  2*Bk.SubMatrix(1,nb_path,i,i);
216  return 0;
217  }
218 
219  cerr << "Spl_cubic::second_derivative: t not in range." << endl;
220  return NOT_IN_RANGE;
221 }
222 
223 // ----------- E N D - E F F E C T O R P A T H W I T H S P L I N E S ----------
224 
225 Spl_path::Spl_path(const string & filename)
265 {
266  const char *ptr_filename = filename.c_str(); //transform string to *char
267 
268  std::ifstream inpointfile(ptr_filename, std::ios::in);
269  point_map pts_map;
270 
271  if(inpointfile)
272  {
273  double time;
274  int nb_path;
275  string temp;
276  pts_map.clear();
277 
278  getline(inpointfile, temp);
279  while(temp.substr(0,1) == "#") { // # comment
280  getline(inpointfile, temp);
281  }
282 
283  if(temp == "JOINT_SPACE")
284  type = JOINT_SPACE;
285  else if (temp == "CARTESIAN_SPACE")
286  type = CARTESIAN_SPACE;
287  else
288  {
289  cerr << "Spl_path::Spl_path: wrong selection type. Should be joint space or cartesian space." << endl;
290  return;
291  }
292 
293  getline(inpointfile, temp);
294  istringstream inputString1(temp);
295  inputString1 >> nb_path;
296  if(nb_path < 1)
297  {
298  cerr << "Spl_path::Spl_path: number of splines should be >= 1." << endl;
299  return;
300  }
301  ColumnVector p(nb_path);
302 
303 
304  while( !inpointfile.eof() )
305  {
306  getline(inpointfile, temp);
307  istringstream inputString2(temp);
308 
309  if(temp.substr(0,1) == " ")
310  break;
311  inputString2 >> time;
312  final_time = time;
313 
314  getline(inpointfile, temp);
315  istringstream inputString3(temp);
316 
317  for(int i = 1; i <= nb_path; i++)
318  inputString3 >> p(i);
319 
320  pts_map.insert(point_map::value_type(time, p));
321  }
322  }
323  else
324  cerr << "Spl_path::Spl_path: can not open file " << filename.c_str() << endl;
325 
326  // Spl_cubic class take as input a Matrix that contain the data.
327  int nb_pts, nb_path;
328  nb_pts = pts_map.size();
329  point_map::const_iterator iter = pts_map.begin();
330  nb_path = iter->second.Nrows();
331 
332  Matrix pts(nb_path+1, nb_pts); pts = 0;
333  {
334  int i = 1;
335  for(iter = pts_map.begin(); iter != pts_map.end(); ++iter)
336  {
337  pts(1,i) = iter->first;
338  pts.SubMatrix(2, nb_path+1, i, i) = iter->second;
339  i++;
340  }
341  }
342  Spl_cubic::operator=(Spl_cubic(pts));
343 }
344 
345 
350 Spl_path::Spl_path(const Matrix & pts): Spl_cubic(pts)
351 {
352 }
353 
354 short Spl_path::p(const Real t, ColumnVector & p)
356 {
357  if(interpolating(t, p))
358  {
359  cerr << "Spl_path::p_pdot: problem with spline interpolating." << endl;
360  return -4;
361  }
362 
363  return 0;
364 }
365 
366 short Spl_path::p_pdot(const Real t, ColumnVector & p, ColumnVector & pdot)
368 {
369  if(interpolating(t, p))
370  {
371  cerr << "Spl_path::p_pdot: problem with spline interpolating." << endl;
372  return -4;
373  }
374  if(first_derivative(t, pdot))
375  {
376  cerr << "Spl_path::p_pdot: problem with spline first_derivative." << endl;
377  return -4;
378  }
379 
380  return 0;
381 }
382 
383 short Spl_path::p_pdot_pddot(const Real t, ColumnVector & p, ColumnVector & pdot,
384  ColumnVector & pdotdot)
386 {
387  if(interpolating(t, p))
388  {
389  cerr << "Spl_path::p_pdot_pdotdot: problem with spline interpolating." << endl;
390  return -4;
391  }
392  if(first_derivative(t, pdot))
393  {
394  cerr << "Spl_path::p_pdot_pdotdot: problem with spline first_derivative." << endl;
395  return -4;
396  }
397  if(second_derivative(t, pdotdot))
398  {
399  cerr << "Spl_path::p_pdot_pdotdot: problem with spline first_derivative." << endl;
400  return -4;
401  }
402  return 0;
403 }
404 
405 // -------------------- Q U A T E R N I O N S P L I N E S -------------------
406 
407 
408 Spl_Quaternion::Spl_Quaternion(const string & filename)
434 {
435  const char *ptr_filename = filename.c_str(); //transform string to *char
436 
437  std::ifstream inquatfile(ptr_filename, std::ios::in);
438 
439  if(inquatfile)
440  {
441  double time;
442  string temp;
443  Matrix R(3,3);
444  Quaternion q;
445  quat_data.clear();
446 
447  while( !inquatfile.eof() )
448  {
449  getline(inquatfile, temp);
450  while(temp.substr(0,1) == "#") {
451  getline(inquatfile, temp);
452  }
453  istringstream inputString(temp);
454  if(temp.substr(0,1) == " ")
455  break;
456  inputString >> time;
457 
458  getline(inquatfile, temp);
459  if( (temp.substr(0,1) == "r") || (temp.substr(0,1) == "R") )
460  {
461  getline(inquatfile, temp);
462  istringstream inputString(temp);
463  inputString >> R(1,1) >> R(1,2) >> R(1,3) >> R(2,1) >> R(2,2) >>
464  R(2,3) >> R(3,1) >> R(3,2) >> R(3,3);
465  q = Quaternion(R);
466  }
467  else if( (temp.substr(0,1) == "q") || (temp.substr(0,1) == "Q") )
468  {
469  getline(inquatfile, temp);
470  istringstream inputString(temp);
471  inputString >> R(1,1) >> R(1,2) >> R(1,3) >> R(2,1);
472  q = Quaternion(R(1,1), R(1,2), R(1,3), R(2,1));
473  }
474  else if(temp.substr(0,1) == "")
475  {
476  }
477  else
478  {
479  cerr << "Spl_Quaternion::Spl_Quaternion: format of input file "
480  << filename.c_str() << " is incorrect" << endl;
481  }
482 
483  quat_data.insert( quat_map::value_type(time, q));
484  }
485  }
486  else
487  {
488  cerr << "Spl_Quaternion::Spl_Quaternion: can not open file "
489  << filename.c_str() << endl;
490  }
491 }
492 
493 
494 Spl_Quaternion::Spl_Quaternion(const quat_map & quat)
496 {
497  quat_data = quat;
498 }
499 
500 short Spl_Quaternion::quat(const Real t, Quaternion & s)
508 {
509  Real dt=0;
510  Quaternion ds;
511  quat_map::const_iterator iter1 = quat_data.begin(), iter2;
512 
513  if( t == iter1->first)
514  {
515  s = iter1->second;
516  return 0;
517  }
518 
519  // Point to interpolate is between the first and the second point. Use Slerp function
520  // since there is not enough points for Squad. Use dt since Slerp and Squad functions
521  // need t from 0 to 1.
522  iter2 = iter1;
523  iter2++;
524  if( t < iter2->first)
525  {
526  dt = (t - iter1->first)/(iter2->first - iter1->first);
527  s = Slerp(iter1->second, iter2->second, dt);
528  return 0;
529  }
530 
531 
532  // From second element to element N-2.
533  // for(iter1 = ++quat_data.begin(); iter1 != ----quat_data.end(); ++iter1)
534  // for(iter1 = ++iter1; iter1 != ----quat_data.end(); ++iter1)
535  for(iter1 = iter2; iter1 != ----quat_data.end(); ++iter1)
536  {
537  iter2=iter1;
538  iter2++;
539  if( (t >= iter1->first) && (t < iter2->first) )
540  {
541  dt = (t - iter1->first)/(iter2->first - iter1->first);
542 
543  Quaternion qn_ = (--iter1)->second, // q(n-1)
544  qn = (++iter1)->second, // q(n)
545  qn_1 = (++iter1)->second, // q(n+1)
546  qn_2 = (++iter1)->second, // q(n+2)
547  q_tmp;
548  ----iter1;
549 
550  // Intermediate point a(n) and a(n+1)
551  Quaternion an = qn*(((qn.i()*qn_1).Log() + (qn.i()*qn_).Log())/-4).exp(),
552  an_1 = qn_1*(((qn_1.i()*qn_2).Log() + (qn_1.i()*qn).Log())/-4).exp();
553 
554  s = Squad(qn, an, an_1, qn_1, dt);
555  return 0;
556  }
557  }
558 
559  // Interpolation between last two points.
560  iter2 = iter1; iter2++;
561  if( (t >= iter1->first) && (t <= iter2->first) )
562  {
563  dt = (t - iter1->first)/(iter2->first - iter1->first);
564  s = Slerp(iter1->second, iter2->second, dt);
565  return 0;
566  }
567 
568  cerr << "Spl_Quaternion::quat_w: t not in range." << endl;
569  return NOT_IN_RANGE;
570 }
571 
572 short Spl_Quaternion::quat_w(const Real t, Quaternion & s, ColumnVector & w)
574 {
575  Real dt=0;
576  Quaternion ds;
577  quat_map::const_iterator iter1 = quat_data.begin(), iter2;
578 
579  if( t == iter1->first)
580  {
581  s = iter1->second;
582  w = ColumnVector(3); w = 0.0;
583  return 0;
584  }
585 
586  // Point to interpolate is between the first and the second point. Use Slerp function
587  // since there is not enough points for Squad. Use dt since Slerp and Squad functions
588  // need t from 0 to 1.
589  iter2 = iter1;
590  iter2++;
591  if( t < iter2->first)
592  {
593  dt = (t - iter1->first)/(iter2->first - iter1->first);
594  s = Slerp(iter1->second, iter2->second, dt);
595  ds = Slerp_prime(iter1->second, iter2->second, dt);
596  w = Omega(s, ds);
597 
598  return 0;
599  }
600 
601  // From second element to element N-2.
602  // for(iter1 = ++quat_data.begin(); iter1 != ----quat_data.end(); ++iter1)
603  // for(iter1 = ++iter1; iter1 != ----quat_data.end(); ++iter1)
604  for(iter1 = iter2; iter1 != ----quat_data.end(); ++iter1)
605  {
606  iter2=iter1;
607  iter2++;
608  if( (t >= iter1->first) && (t < iter2->first) )
609  {
610  dt = (t - iter1->first)/(iter2->first - iter1->first);
611 
612  Quaternion qn_ = (--iter1)->second, // q(n-1)
613  qn = (++iter1)->second, // q(n)
614  qn_1 = (++iter1)->second, // q(n+1)
615  qn_2 = (++iter1)->second, // q(n+2)
616  q_tmp;
617  ----iter1;
618 
619  // Intermediate point a(n) and a(n+1)
620  Quaternion an = qn*(((qn.i()*qn_1).Log() + (qn.i()*qn_).Log())/-4).exp(),
621  an_1 = qn_1*(((qn_1.i()*qn_2).Log() + (qn_1.i()*qn).Log())/-4).exp();
622 
623  s = Squad(qn, an, an_1, qn_1, dt);
624  ds = Squad_prime(qn, an, an_1, qn_1, dt);
625  w = Omega(s, ds);
626  return 0;
627  }
628  }
629 
630  // Interpolation between last two points.
631  iter2 = iter1; iter2++;
632  if( (t >= iter1->first) && (t <= iter2->first) )
633  {
634  dt = (t - iter1->first)/(iter2->first - iter1->first);
635  s = Slerp(iter1->second, iter2->second, dt);
636  ds = Slerp_prime(iter1->second, iter2->second, dt);
637  w = Omega(s, ds);
638  return 0;
639  }
640 
641  cerr << "Spl_Quaternion::quat_w: t not in range." << endl;
642  return NOT_IN_RANGE;
643 }
644 
645 
646 // -----------------------------------------------------------------------------------------------
647 
648 
651 {
652  type = NONE;
653  quaternion_active = false;
654 }
655 
656 
657 Trajectory_Select::Trajectory_Select(const string & filename)
665 {
666  set_trajectory(filename);
667 }
668 
669 
672 {
673  type = x.type;
674  if(x.quaternion_active)
675  {
677  path_quat = x.path_quat;
678  }
679  else
680  path = x.path;
681 
682  return *this;
683 }
684 
685 
686 void Trajectory_Select::set_trajectory(const string & filename)
688 {
689  const char *ptr_filename = filename.c_str(); //transform string to *char
690 
691  std::ifstream inpointfile(ptr_filename, std::ios::in);
692 
693  if(inpointfile)
694  {
695  string temp;
696 
697  getline(inpointfile, temp);
698  while(temp.substr(0,1) == "#") { // # comment
699  getline(inpointfile, temp);
700  }
701 
702  if( (temp == "JOINT_SPACE") || (temp == "CARTESIAN_SPACE") )
703  {
704  path = Spl_path(filename);
705  type = path.get_type();
706  quaternion_active = false;
707  }
708  else
709  {
710  path_quat = Spl_Quaternion(filename);
711  type = CARTESIAN_SPACE;
712  quaternion_active = true;
713  }
714  }
715  else
716  cerr << "Trajectory_Select::set_trajectory: invalid input file " << filename << endl;
717 }
718 
719 #ifdef use_namespace
720 }
721 #endif
722 
723 
724 
725 
726 
727 
728