worldsim/include/wquaternion.h

00001 /********************************************************************************
00002  *  WorldSim -- library for robot simulations                                   *
00003  *  Copyright (C) 2008-2011 Gianluca Massera <emmegian@yahoo.it>                *
00004  *                                                                              *
00005  *  This program is free software; you can redistribute it and/or modify        *
00006  *  it under the terms of the GNU General Public License as published by        *
00007  *  the Free Software Foundation; either version 2 of the License, or           *
00008  *  (at your option) any later version.                                         *
00009  *                                                                              *
00010  *  This program is distributed in the hope that it will be useful,             *
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of              *
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
00013  *  GNU General Public License for more details.                                *
00014  *                                                                              *
00015  *  You should have received a copy of the GNU General Public License           *
00016  *  along with this program; if not, write to the Free Software                 *
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  *
00018  ********************************************************************************/
00019 
00020 #ifndef WQUATERNION_H
00021 #define WQUATERNION_H
00022 
00023 #include "worldsimconfig.h"
00024 #include "wvector.h"
00025 
00026 namespace farsa {
00027 
00028 class wMatrix;
00029 
00041 class FARSA_WSIM_TEMPLATE wQuaternion {
00042 public:
00044     wQuaternion(); 
00046     wQuaternion( const wMatrix &matrix);
00048     wQuaternion( real q0, real q1, real q2, real q3 );
00050     wQuaternion( const wVector &unit_Axis, real Angle = 0.0 );
00051     
00052     void scale( real scale ); 
00053     void normalize(); 
00054     real dotProduct( const wQuaternion &QB ) const;
00055     wQuaternion inverse() const; 
00056 
00057     wVector rotateVector( const wVector& vector) const;
00058     wVector unrotateVector( const wVector& vector ) const;
00059 
00060     wVector getEuleroAngles() const;
00061     wQuaternion slerp( const wQuaternion &q1, real t ) const;
00062     wVector calcAverageOmega( const wQuaternion &q1, real dt ) const;
00063 
00064     wQuaternion operator*( const wQuaternion &B ) const;
00065     wQuaternion operator+( const wQuaternion &B ) const; 
00066     wQuaternion operator-( const wQuaternion &B ) const; 
00067 
00068     real q0;
00069     real q1;
00070     real q2;
00071     real q3;
00072 };
00073 
00074 } // end namespace farsa
00075 
00076 //--- this is included here because wMatrix and wQuaternion has mutual dependencies
00077 #include "wmatrix.h"
00078 
00079 namespace farsa {
00080 
00081 inline wQuaternion::wQuaternion() {
00082     q0 = 1.0;
00083     q1 = 0.0;
00084     q2 = 0.0;
00085     q3 = 0.0;
00086 }
00087 
00088 inline wQuaternion::wQuaternion( const wMatrix &matrix ) {
00089     enum QUAT_INDEX {
00090         X_INDEX=0,
00091         Y_INDEX=1,
00092         Z_INDEX=2
00093     };
00094     static QUAT_INDEX QIndex [] = {Y_INDEX, Z_INDEX, X_INDEX};
00095 
00096     real *ptr;
00097     real trace;
00098     QUAT_INDEX i;
00099     QUAT_INDEX j;
00100     QUAT_INDEX k;
00101 
00102     trace = matrix[0][0] + matrix[1][1] + matrix[2][2];
00103 
00104     if( trace > 0.0 ) {
00105         trace = sqrt( trace + 1.0 );
00106         q0 = 0.5*trace;
00107         trace = 0.5/trace;
00108         q1 = (matrix[1][2] - matrix[2][1]) * trace;
00109         q2 = (matrix[2][0] - matrix[0][2]) * trace;
00110         q3 = (matrix[0][1] - matrix[1][0]) * trace;
00111     } else {
00112         i = X_INDEX;
00113         if( matrix[Y_INDEX][Y_INDEX] > matrix[X_INDEX][X_INDEX] ) {
00114             i = Y_INDEX;
00115         }
00116         if( matrix[Z_INDEX][Z_INDEX] > matrix[i][i] ) {
00117             i = Z_INDEX;
00118         }
00119         j = QIndex[i];
00120         k = QIndex[j];
00121 
00122         trace = 1.0 + matrix[i][i] - matrix[j][j] - matrix[k][k];
00123         trace = sqrt(trace);
00124 
00125         ptr = &q1;
00126         ptr[i] = 0.5*trace;
00127         trace = 0.5/trace;
00128         q0 = (matrix[j][k] - matrix[k][j]) * trace;
00129         ptr[j] = (matrix[i][j] + matrix[j][i]) * trace;
00130         ptr[k] = (matrix[i][k] + matrix[k][i]) * trace;
00131     }
00132 }
00133 
00134 inline wQuaternion::wQuaternion(real Q0, real Q1, real Q2, real Q3) {
00135     q0 = Q0;
00136     q1 = Q1;
00137     q2 = Q2;
00138     q3 = Q3;
00139     // INVARIANT: fabs( dotProduct(*this) - 1.0) < 1.0e-4f;
00140 }
00141 
00142 inline wQuaternion::wQuaternion( const wVector &unitAxis, real angle ) {
00143     angle *= 0.5;
00144     q0 = cos( angle );
00145     real sinAng = sin( angle );
00146 
00147     q1 = unitAxis.x * sinAng;
00148     q2 = unitAxis.y * sinAng;
00149     q3 = unitAxis.z * sinAng;
00150 }
00151 
00152 inline void wQuaternion::scale(real scale) {
00153     q0 *= scale;
00154     q1 *= scale;
00155     q2 *= scale;
00156     q3 *= scale;
00157 }
00158 
00159 inline void wQuaternion::normalize() {
00160     scale( 1.0/sqrt( dotProduct(*this)) );
00161 }
00162 
00163 inline real wQuaternion::dotProduct( const wQuaternion &QB ) const {
00164     return q0*QB.q0 + q1*QB.q1 + q2*QB.q2 + q3*QB.q3;
00165 }
00166 
00167 inline wQuaternion wQuaternion::inverse() const {
00168     return wQuaternion(q0, -q1, -q2, -q3);
00169 }
00170 
00171 inline wVector wQuaternion::rotateVector( const wVector& vector ) const {
00172     wMatrix matrix( *this, wVector( 0.0f, 0.0f, 0.0f, 1.0f ) );
00173     return matrix.rotateVector( vector );
00174 }
00175 
00176 inline wVector wQuaternion::unrotateVector( const wVector& vector ) const {
00177     wMatrix matrix( *this, wVector( 0.0f, 0.0f, 0.0f, 1.0f ) );
00178     return matrix.unrotateVector( vector );
00179 }
00180 
00181 inline wQuaternion wQuaternion::operator+(const wQuaternion &B) const {
00182     return wQuaternion( q0+B.q0, q1+B.q1, q2+B.q2, q3+B.q3);
00183 }
00184 
00185 inline wQuaternion wQuaternion::operator-( const wQuaternion &B ) const {
00186     return wQuaternion( q0-B.q0, q1-B.q1, q2-B.q2, q3-B.q3);
00187 }
00188 
00189 inline wQuaternion wQuaternion::operator*( const wQuaternion &B ) const {
00190     return wQuaternion( B.q0*q0 - B.q1*q1 - B.q2*q2 - B.q3*q3, 
00191                         B.q1*q0 + B.q0*q1 - B.q3*q2 + B.q2*q3, 
00192                         B.q2*q0 + B.q3*q1 + B.q0*q2 - B.q1*q3, 
00193                         B.q3*q0 - B.q2*q1 + B.q1*q2 + B.q0*q3 ); 
00194 }
00195 
00196 inline wVector wQuaternion::getEuleroAngles() const {
00197     real val;
00198     real pitch;
00199     real yaw;
00200     real roll;
00201 
00202     val = 2.0f*( q2*q0 - q3*q1 );
00203     if( val >= 0.99995f ) {
00204         pitch = 2.0f*atan2( q1, q0 );
00205         yaw = 0.5f*PI_GRECO;
00206         roll = 0.0f;
00207     } else if( val <= -0.99995f ) {
00208         pitch = 2.0f*atan2( q1, q0 );
00209         yaw = -0.5f*PI_GRECO;
00210         roll = 0.0f;
00211     } else {
00212         yaw = sin (val);
00213         pitch = atan2( 2.0f*( q1*q0 + q3*q2 ), 1.0f-2.0f*( q1*q1 + q2*q2) );
00214         roll = atan2( 2.0f*( q3*q0 + q1*q2 ), 1.0f-2.0f*( q2*q2 + q3*q3) );
00215     }
00216     return wVector(pitch, yaw, roll);
00217 }
00218 
00219 inline wVector wQuaternion::calcAverageOmega( const wQuaternion &QB, real dt ) const {
00220     wQuaternion dq( inverse()*QB );
00221     wVector omegaDir( dq.q1, dq.q2, dq.q3 );
00222     real dirMag2 = omegaDir % omegaDir;
00223     if( dirMag2 < 1.0e-10 ) {
00224         return wVector( 0.0, 0.0, 0.0, 0.0 );
00225     }
00226     real dirMagInv = 1.0/sqrt( dirMag2 );
00227     real dirMag = dirMag2 * dirMagInv;
00228     real omegaMag = 2.0*atan2( dirMag, dq.q0 )/dt;
00229 
00230     return omegaDir.scale( dirMagInv * omegaMag );
00231 }
00232 
00233 inline wQuaternion wQuaternion::slerp( const wQuaternion &QB, real t ) const {
00234     real dot;
00235     real ang;
00236     real Sclp;
00237     real Sclq;
00238     real den;
00239     real sinAng;
00240     wQuaternion Q;
00241 
00242     dot = dotProduct( QB );
00243 
00244     if( (dot+1.0) > 1.0e-5f ) {
00245         if( dot < 0.995f ) {
00246             ang = cos( dot );
00247             sinAng = sin( ang );
00248             den = 1.0/sinAng;
00249             Sclp = sin( (1.0-t) * ang ) * den;
00250             Sclq = sin( t*ang ) * den;
00251         } else  {
00252             Sclp = 1.0-t;
00253             Sclq = t;
00254         }
00255         Q.q0 = q0*Sclp + QB.q0*Sclq;
00256         Q.q1 = q1*Sclp + QB.q1*Sclq;
00257         Q.q2 = q2*Sclp + QB.q2*Sclq;
00258         Q.q3 = q3*Sclp + QB.q3*Sclq;
00259     } else {
00260         Q.q0 =  q3;
00261         Q.q1 = -q2;
00262         Q.q2 =  q1;
00263         Q.q3 =  q0;
00264 
00265         Sclp = sin(1.0-t) * PI_GRECO * 0.5;
00266         Sclq = sin( t * PI_GRECO * 0.5 );
00267 
00268         Q.q0 = q0*Sclp + Q.q0*Sclq;
00269         Q.q1 = q1*Sclp + Q.q1*Sclq;
00270         Q.q2 = q2*Sclp + Q.q2*Sclq;
00271         Q.q3 = q3*Sclp + Q.q3*Sclq;
00272     }
00273 
00274     dot = Q.dotProduct( Q );
00275     if( dot < (1.0-1.0e-4f) ) {
00276         dot = 1.0/sqrt( dot );
00277         Q.q0 *= dot;
00278         Q.q1 *= dot;
00279         Q.q2 *= dot;
00280         Q.q3 *= dot;
00281     }
00282     return Q;
00283 }
00284 
00285 } // end namespace farsa
00286 
00287 #endif