ROBOOP, A Robotics Object Oriented Package in C++
quaternion.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/01/19: Etienne Lachance
27  -Removed function Exp and Ln.
28  -Added function power and Log.
29  -Fixed bugs in Slerp, Slerp_prime, Squad and Squad_prime.
30 
31 2003/05/23: Etienne Lachance
32  -Added the following member function -=, +=, *=, /=, Exp, Ln, dot, d_dt, E
33  -Added functions Integ_Trap_quat, Slerp, Slerp_prime, Squad, Squad_prime,
34 
35 2004/05/14: Etienne Lachance
36  -Replaced vec_x_prod by CrossProduct.
37 
38 2004/05/21: Etienne Lachance
39  -Added comments that can be used with Doxygen.
40 
41 2004/07/01: Etienne Lachance
42  -Replaced vec_dot_prod by DotProdut of Newmat library.
43 
44 2004/07/01: Ethan Tira-Thompson
45  -Added support for newmat's use_namespace #define, using ROBOOP namespace
46  -Fixed problem in constructor using float as Real type
47 
48 2005/11/06: Etienne Lachance
49  - No need to provide a copy constructor and the assignment operator
50  (operator=) for Quaternion class. Instead we use the one provide by the
51  compiler.
52 
53 2005/11/13: Etienne Lachance
54  - operator* and operator/ are now non-member functions when one of the
55  operand is a real. With these modifications we support q2 = c * q1 and
56  q2 = q1 * c
57 -------------------------------------------------------------------------------
58 */
59 
60 
66 #include "quaternion.h"
67 
68 #ifdef use_namespace
69 namespace ROBOOP {
70  using namespace NEWMAT;
71 #endif
72 
73 
76 {
77  s_ = 1.0;
78  v_ = ColumnVector(3);
79  v_ = 0.0;
80 }
81 
82 Quaternion::Quaternion(const Real angle, const ColumnVector & axis)
84 {
85  if(axis.Nrows() != 3)
86  {
87  cerr << "Quaternion::Quaternion, size of axis != 3" << endl;
88  exit(1);
89  }
90 
91  // make sure axis is a unit vector
92  Real norm_axis = sqrt(DotProduct(axis, axis));
93 
94  if(norm_axis != 1)
95  {
96  cerr << "Quaternion::Quaternion(angle, axis), axis is not unit" << endl;
97  cerr << "Make the axis unit." << endl;
98  v_ = sin(angle/2) * axis/norm_axis;
99  }
100  else
101  v_ = sin(angle/2) * axis;
102 
103  s_ = cos(angle/2);
104 }
105 
106 Quaternion::Quaternion(const Real s_in, const Real v1, const Real v2,
107  const Real v3)
109 {
110  s_ = s_in;
111  v_ = ColumnVector(3);
112  v_(1) = v1;
113  v_(2) = v2;
114  v_(3) = v3;
115 }
116 
117 Quaternion::Quaternion(const Matrix & R)
158 {
159  if( ((R.Nrows() == 3) && (R.Ncols() == 3)) ||
160  ((R.Nrows() == 4) && (R.Ncols() == 4)) )
161  {
162  Real tmp = fabs(R(1,1) + R(2,2) + R(3,3) + 1);
163  s_ = 0.5*sqrt(tmp);
164  if(v_.Nrows() != 3)
165  v_ = ColumnVector(3);
166 
167  if(s_ > EPSILON)
168  {
169  v_(1) = (R(3,2)-R(2,3))/(4*s_);
170  v_(2) = (R(1,3)-R(3,1))/(4*s_);
171  v_(3) = (R(2,1)-R(1,2))/(4*s_);
172  }
173  else
174  {
175  // |w| <= 1/2
176  static int s_iNext[3] = { 2, 3, 1 };
177  int i = 1;
178  if ( R(2,2) > R(1,1) )
179  i = 2;
180  if ( R(3,3) > R(2,2) )
181  i = 3;
182  int j = s_iNext[i-1];
183  int k = s_iNext[j-1];
184 
185  Real fRoot = sqrt(R(i,i)-R(j,j)-R(k,k) + 1.0);
186 
187  Real *tmp[3] = { &v_(1), &v_(2), &v_(3) };
188  *tmp[i-1] = 0.5*fRoot;
189  fRoot = 0.5/fRoot;
190  s_ = (R(k,j)-R(j,k))*fRoot;
191  *tmp[j-1] = (R(j,i)+R(i,j))*fRoot;
192  *tmp[k-1] = (R(k,i)+R(i,k))*fRoot;
193  }
194 
195  }
196  else
197  cerr << "Quaternion::Quaternion: matrix input is not 3x3 or 4x4" << endl;
198 }
199 
212 {
213  Quaternion q;
214  q.s_ = s_ + rhs.s_;
215  q.v_ = v_ + rhs.v_;
216 
217  return q;
218 }
219 
232 {
233  Quaternion q;
234  q.s_ = s_ - rhs.s_;
235  q.v_ = v_ - rhs.v_;
236 
237  return q;
238 }
239 
255 {
256  Quaternion q;
257  q.s_ = s_ * rhs.s_ - DotProduct(v_, rhs.v_);
258  q.v_ = s_ * rhs.v_ + rhs.s_ * v_ + CrossProduct(v_, rhs.v_);
259 
260  return q;
261 }
262 
263 
266 {
267  return *this*rhs.i();
268 }
269 
270 
271 void Quaternion::set_v(const ColumnVector & v)
273 {
274  if(v.Nrows() == 3)
275  v_ = v;
276  else
277  cerr << "Quaternion::set_v: input has a wrong size." << endl;
278 }
279 
287 {
288  Quaternion q;
289  q.s_ = s_;
290  q.v_ = -1*v_;
291 
292  return q;
293 }
294 
295 Real Quaternion::norm()const
304 {
305  return( sqrt(s_*s_ + DotProduct(v_, v_)) );
306 }
307 
310 {
311  Real tmp = norm();
312  if(tmp > EPSILON)
313  {
314  s_ = s_/tmp;
315  v_ = v_/tmp;
316  }
317  return *this;
318 }
319 
329 {
330  return conjugate()/norm();
331 }
332 
341 {
342  Quaternion q;
343  Real theta = sqrt(DotProduct(v_,v_)),
344  sin_theta = sin(theta);
345 
346  q.s_ = cos(theta);
347  if ( fabs(sin_theta) > EPSILON)
348  q.v_ = v_*sin_theta/theta;
349  else
350  q.v_ = v_;
351 
352  return q;
353 }
354 
355 Quaternion Quaternion::power(const Real t) const
356 {
357  Quaternion q = (Log()*t).exp();
358 
359  return q;
360 }
361 
371 {
372  Quaternion q;
373  q.s_ = 0;
374  Real theta = acos(s_),
375  sin_theta = sin(theta);
376 
377  if ( fabs(sin_theta) > EPSILON)
378  q.v_ = v_/sin_theta*theta;
379  else
380  q.v_ = v_;
381 
382  return q;
383 }
384 
385 Quaternion Quaternion::dot(const ColumnVector & w, const short sign)const
412 {
413  Quaternion q;
414  Matrix tmp;
415 
416  tmp = -0.5*v_.t()*w;
417  q.s_ = tmp(1,1);
418  q.v_ = 0.5*E(sign)*w;
419 
420  return q;
421 }
422 
423 ReturnMatrix Quaternion::E(const short sign)const
429 {
430  Matrix E(3,3), I(3,3);
431  I << threebythreeident;
432 
433  if(sign == BODY_FRAME)
434  E = s_*I + x_prod_matrix(v_);
435  else
436  E = s_*I - x_prod_matrix(v_);
437 
438  E.Release();
439  return E;
440 }
441 
442 Real Quaternion::dot_prod(const Quaternion & q)const
451 {
452  return (s_*q.s_ + v_(1)*q.v_(1) + v_(2)*q.v_(2) + v_(3)*q.v_(3));
453 }
454 
455 ReturnMatrix Quaternion::R()const
487 {
488  Matrix R(3,3);
489  R << threebythreeident;
490  R = (1 - 2*DotProduct(v_, v_))*R + 2*v_*v_.t() + 2*s_*x_prod_matrix(v_);
491 
492  R.Release();
493  return R;
494 }
495 
496 ReturnMatrix Quaternion::T()const
502 {
503  Matrix T(4,4);
504  T << fourbyfourident;
505  T.SubMatrix(1,3,1,3) = (1 - 2*DotProduct(v_, v_))*T.SubMatrix(1,3,1,3)
506  + 2*v_*v_.t() + 2*s_*x_prod_matrix(v_);
507  T.Release();
508  return T;
509 }
510 
511 // -------------------------------------------------------------------------------------
512 
513 Quaternion operator*(const Real c, const Quaternion & q)
523 {
524  Quaternion out;
525  out.set_s(q.s() * c);
526  out.set_v(q.v() * c);
527  return out;
528 }
529 
530 Quaternion operator*(const Quaternion & q, const Real c)
534 {
535  return operator*(c, q);
536 }
537 
538 
539 Quaternion operator/(const Real c, const Quaternion & q)
545 {
546  Quaternion out;
547  out.set_s(q.s() / c);
548  out.set_v(q.v() / c);
549  return out;
550 }
551 
552 Quaternion operator/(const Quaternion & q, const Real c)
553 {
554  return operator/(c, q);
555 }
556 
557 ReturnMatrix Omega(const Quaternion & q, const Quaternion & q_dot)
563 {
564  Matrix A, B, M;
565  UpperTriangularMatrix U;
566  ColumnVector w(3);
567  A = 0.5*q.E(BASE_FRAME);
568  B = q_dot.v();
569  if(A.Determinant())
570  {
571  QRZ(A,U); //QR decomposition
572  QRZ(A,B,M);
573  w = U.i()*M;
574  }
575  else
576  w = 0;
577 
578  w.Release();
579  return w;
580 }
581 
582 short Integ_quat(Quaternion & dquat_present, Quaternion & dquat_past,
583  Quaternion & quat, const Real dt)
585 {
586  if (dt < 0)
587  {
588  cerr << "Integ_Trap(quat1, quat2, dt): dt < 0. dt is set to 0." << endl;
589  return -1;
590  }
591 
592  // Quaternion algebraic constraint
593  // Real Klambda = 0.5*(1 - quat.norm_sqr());
594 
595  dquat_present.set_s(dquat_present.s() );//+ Klambda*quat.s());
596  dquat_present.set_v(dquat_present.v() ); //+ Klambda*quat.v());
597 
598  quat.set_s(quat.s() + Integ_Trap_quat_s(dquat_present, dquat_past, dt));
599  quat.set_v(quat.v() + Integ_Trap_quat_v(dquat_present, dquat_past, dt));
600 
601  dquat_past.set_s(dquat_present.s());
602  dquat_past.set_v(dquat_present.v());
603 
604  quat.unit();
605 
606  return 0;
607 }
608 
609 Real Integ_Trap_quat_s(const Quaternion & present, Quaternion & past,
610  const Real dt)
612 {
613  Real integ = 0.5*(present.s()+past.s())*dt;
614  past.set_s(present.s());
615  return integ;
616 }
617 
618 ReturnMatrix Integ_Trap_quat_v(const Quaternion & present, Quaternion & past,
619  const Real dt)
621 {
622  ColumnVector integ = 0.5*(present.v()+past.v())*dt;
623  past.set_v(present.v());
624  integ.Release();
625  return integ;
626 }
627 
628 Quaternion Slerp(const Quaternion & q0, const Quaternion & q1, const Real t)
679 {
680  if( (t < 0) || (t > 1) )
681  cerr << "Slerp(q0, q1, t): t < 0 or t > 1. t is set to 0." << endl;
682 
683  if(q0.dot_prod(q1) >= 0)
684  return q0*((q0.i()*q1).power(t));
685  else
686  return q0*((q0.i()*-1*q1).power(t));
687 }
688 
690  const Real t)
712 {
713  if( (t < 0) || (t > 1) )
714  cerr << "Slerp_prime(q0, q1, t): t < 0 or t > 1. t is set to 0." << endl;
715 
716  if(q0.dot_prod(q1) >= 0)
717  return Slerp(q0, q1, t)*(q0.i()*q1).Log();
718  else
719  return Slerp(q0, q1, t)*(q0.i()*-1*q1).Log();
720 }
721 
722 Quaternion Squad(const Quaternion & p, const Quaternion & a, const Quaternion & b,
723  const Quaternion & q, const Real t)
741 {
742  if( (t < 0) || (t > 1) )
743  cerr << "Squad(p,a,b,q, t): t < 0 or t > 1. t is set to 0." << endl;
744 
745  return Slerp(Slerp(p,q,t),Slerp(a,b,t),2*t*(1-t));
746 }
747 
748 Quaternion Squad_prime(const Quaternion & p, const Quaternion & a, const Quaternion & b,
749  const Quaternion & q, const Real t)
790 {
791  if( (t < 0) || (t > 1) )
792  cerr << "Squad_prime(p,a,b,q, t): t < 0 or t > 1. t is set to 0." << endl;
793 
794  Quaternion q_squad,
795  U = Slerp(p, q, t),
796  V = Slerp(a, b, t),
797  W = U.i()*V,
798  U_prime = U*(p.i()*q).Log(),
799  V_prime = V*(a.i()*b).Log(),
800  W_prime = U.i()*V_prime - U.power(-2)*U_prime*V;
801 
802  q_squad = U*( W.power(2*t*(1-t))*W.Log()*(2-4*t) + W.power(2*t*(1-t)-1)*W_prime*2*t*(1-t) )
803  + U_prime*( W.power(2*t*(1-t)) );
804 
805  return q_squad;
806 }
807 
808 #ifdef use_namespace
809 }
810 #endif
811 
812 
813 
814 
815 
816