rotations.cpp

Go to the documentation of this file.
00001 
00016 #include <cmath>
00017 #include <dlrCommon/functional.h>
00018 #include <dlrNumeric/rotations.h>
00019 #include <dlrNumeric/utilities.h>
00020 
00021 namespace {
00022 
00023   const double rotationsEpsilon_ = 1.0E-10;
00024 
00025 }
00026 
00027 namespace dlr {
00028 
00029   namespace numeric {
00030     
00031     Quaternion
00032     angleAxisToQuaternion(double angle, const Vector3D& axis, bool isNormalized)
00033     {
00034       // Deal with the angle.
00035       double angleOverTwo = angle / 2.0;
00036       double cosineValue = std::cos(angleOverTwo);
00037       double sineValue = std::sin(angleOverTwo);
00038 
00039       // If the axis is already unit length, we're done!
00040       if(isNormalized) {
00041         return Quaternion(cosineValue, sineValue * axis.x(),
00042                           sineValue * axis.y(), sineValue * axis.z());
00043       }
00044 
00045       // Axis is not known to be unit length, so we have more work to do.
00046       Vector3D axisCopy = axis;
00047       double axisMagnitude = magnitude(axis);
00048       if(axisMagnitude != 0.0) {
00049         axisCopy /= axisMagnitude;
00050       } else {
00051         // Axis is too small to observe.  Pick an arbitrary axis.
00052         axisCopy.setValue(1.0, 0.0, 0.0);
00053       }
00054       return Quaternion(cosineValue, sineValue * axisCopy.x(),
00055                         sineValue * axisCopy.y(), sineValue * axisCopy.z());
00056     }
00057 
00058   
00059     Vector3D
00060     angleAxisToRollPitchYaw(double angle, const Vector3D& axis,
00061                             bool isNormalized)
00062     {
00063       Quaternion quaternion = angleAxisToQuaternion(angle, axis, isNormalized);
00064       return quaternionToRollPitchYaw(quaternion);
00065     }    
00066 
00067   
00068     Transform3D
00069     angleAxisToTransform3D(double angle, const Vector3D& axis, bool isNormalized)
00070     {
00071       Quaternion quaternion = angleAxisToQuaternion(angle, axis, isNormalized);
00072       return quaternionToTransform3D(quaternion);
00073     }
00074 
00075   
00076     std::pair<double, Vector3D>
00077     quaternionToAngleAxis(const Quaternion& quaternion)
00078     {
00079       // Normalize the quaternion, if necessary.
00080       Quaternion quaternionCopy(quaternion);
00081       quaternionCopy.normalize();
00082 
00083       // Recover the obvious quantities.
00084       double cosineHalfTheta = quaternionCopy.s();
00085       Vector3D sineTimesAxis(
00086         quaternionCopy.i(), quaternionCopy.j(), quaternionCopy.k());
00087       double sineHalfTheta = magnitude(sineTimesAxis);
00088 
00089       // Recover angle and axis.
00090       double angle = 2 * std::atan2(sineHalfTheta, cosineHalfTheta);
00091       if(sineHalfTheta == 0.0) {
00092         return std::make_pair(angle, Vector3D(1.0, 0.0, 0.0));
00093       }
00094       return std::make_pair(angle, sineTimesAxis / sineHalfTheta);
00095     }
00096 
00097   
00098     Vector3D
00099     quaternionToRollPitchYaw(const Quaternion& quaternion)
00100     {
00101       // The quaternion will be normalized inside quaternionToTransform3D().
00102       Transform3D transform3D = quaternionToTransform3D(quaternion);
00103       return transform3DToRollPitchYaw(transform3D);
00104     }
00105 
00106 
00107     Transform3D
00108     quaternionToTransform3D(const Quaternion& quaternion)
00109     {
00110       // Normalize the quaternion, if necessary.
00111       Quaternion quaternionCopy(quaternion);
00112       quaternionCopy.normalize();
00113 
00114       // Some convenience variables.
00115       double ii = 2.0 * quaternionCopy.i() * quaternionCopy.i();
00116       double jj = 2.0 * quaternionCopy.j() * quaternionCopy.j();
00117       double kk = 2.0 * quaternionCopy.k() * quaternionCopy.k();
00118       double si = 2.0 * quaternionCopy.s() * quaternionCopy.i();
00119       double sj = 2.0 * quaternionCopy.s() * quaternionCopy.j();
00120       double sk = 2.0 * quaternionCopy.s() * quaternionCopy.k();
00121       double ij = 2.0 * quaternionCopy.i() * quaternionCopy.j();
00122       double ik = 2.0 * quaternionCopy.i() * quaternionCopy.k();
00123       double jk = 2.0 * quaternionCopy.j() * quaternionCopy.k();
00124 
00125       return Transform3D(1 - jj - kk, ij - sk, ik + sj, 0.0,
00126                          ij + sk, 1 - ii - kk, jk - si, 0.0,
00127                          ik - sj, jk + si, 1 - ii - jj, 0.0,
00128                          0.0, 0.0, 0.0, 1);
00129     }
00130 
00131   
00132     std::pair<double, Vector3D>
00133     rollPitchYawToAngleAxis(const Vector3D& rollPitchYaw)
00134     {
00135       Quaternion quaternion = rollPitchYawToQuaternion(rollPitchYaw);
00136       return quaternionToAngleAxis(quaternion);
00137     }
00138 
00139   
00140     Quaternion
00141     rollPitchYawToQuaternion(const Vector3D& rollPitchYaw)
00142     {
00143       Transform3D transform3D = rollPitchYawToTransform3D(rollPitchYaw);
00144       return transform3DToQuaternion(transform3D);
00145     }
00146   
00147 
00148     Transform3D
00149     rollPitchYawToTransform3D(const Vector3D& rollPitchYaw)
00150     {
00151       // First compute some convenience values.
00152       double cosineRoll = std::cos(rollPitchYaw.x());
00153       double cosinePitch = std::cos(rollPitchYaw.y());
00154       double cosineYaw = std::cos(rollPitchYaw.z());
00155 
00156       double sineRoll = std::sin(rollPitchYaw.x());
00157       double sinePitch = std::sin(rollPitchYaw.y());
00158       double sineYaw = std::sin(rollPitchYaw.z());
00159 
00160       // Each of roll, pitch, yaw, correspond to a rotation about one
00161       // axis.
00162       Transform3D rollTransform(1.0, 0.0, 0.0, 0.0,
00163                                 0.0, cosineRoll, -sineRoll, 0.0,
00164                                 0.0, sineRoll, cosineRoll, 0.0,
00165                                 0.0, 0.0, 0.0, 1.0);
00166       Transform3D pitchTransform(cosinePitch, 0.0, sinePitch, 0.0,
00167                                  0.0, 1.0, 0.0, 0.0,
00168                                  -sinePitch, 0.0, cosinePitch, 0.0,
00169                                  0.0, 0.0, 0.0, 1.0);
00170       Transform3D yawTransform(cosineYaw, -sineYaw, 0.0, 0.0,
00171                                sineYaw, cosineYaw, 0.0, 0.0,
00172                                0.0, 0.0, 1.0, 0.0, 
00173                                0.0, 0.0, 0.0, 1.0);
00174 
00175       // Compose the three rotations to get the result.
00176       return rollTransform * (pitchTransform * yawTransform);
00177     }
00178 
00179   
00180     std::pair<double, Vector3D>
00181     transform3DToAngleAxis(const Transform3D& transform3D)
00182     {
00183       Quaternion quaternion = transform3DToQuaternion(transform3D);
00184       return quaternionToAngleAxis(quaternion);
00185     }
00186 
00187   
00188     // This routine draws from _On Homogeneous Transforms, Quaternions,
00189     // and Computation Efficiency_, by Funda, Taylor, and Paul, IEEE R&A,
00190     // June 1990.
00191     Quaternion
00192     transform3DToQuaternion(const Transform3D& transform3D)
00193     {
00194       // For convenience, we make temporary variables representing the
00195       // elements of transform3D.
00196       double t00 = transform3D.value<0, 0>();
00197       double t01 = transform3D.value<0, 1>();
00198       double t02 = transform3D.value<0, 2>();
00199       double t10 = transform3D.value<1, 0>();
00200       double t11 = transform3D.value<1, 1>();
00201       double t12 = transform3D.value<1, 2>();
00202       double t20 = transform3D.value<2, 0>();
00203       double t21 = transform3D.value<2, 1>();
00204       double t22 = transform3D.value<2, 2>();
00205     
00206       // First compute s.
00207       double sSquaredTimesFour = (1.0 + t00 + t11 + t22);
00208       // Allow for numerical errors.
00209       if(sSquaredTimesFour < 0.0) {
00210         sSquaredTimesFour = 0.0;
00211       }
00212       double sValue = std::sqrt(sSquaredTimesFour) / 2.0;
00213       // Allow for numerical errors.
00214       if(sValue > 1.0) {
00215         sValue = 1.0;
00216       }
00217 
00218       // This vector points in the direction of the axis of rotation, but
00219       // unfortunately goes to zero for theta approaching 0 degrees and
00220       // 180 degrees.
00221       Vector3D axis0(t21 - t12, t02 - t20, t10 - t01);
00222     
00223       // We need to find another parallel vector using the elements of
00224       // transform3D. Start by noting which axis dominates.
00225       int axisOfLargestRotation = 0;
00226       if(t00 > t11) {
00227         if(t00 > t22) {
00228           axisOfLargestRotation = 0;
00229         } else {
00230           axisOfLargestRotation = 2;
00231         }
00232       } else {
00233         if(t11 > t22) {
00234           axisOfLargestRotation = 1;
00235         } else {
00236           axisOfLargestRotation = 2;
00237         }
00238       }
00239 
00240       // Now compute the parallel vector and add it to axis0.
00241       if(axisOfLargestRotation == 0) {
00242         Vector3D axis1(1.0 + t00 - t11 - t22, t10 + t01, t20 + t02);
00243         if(axis0.x() >= 0) {
00244           axis0 += axis1;
00245         } else {
00246           axis0 -= axis1;
00247         }
00248       } else if(axisOfLargestRotation == 1) {
00249         Vector3D axis1(t10 + t01, 1.0 + t11 - t00 - t22, t21 + t12);
00250         if(axis0.y() >= 0) {
00251           axis0 += axis1;
00252         } else {
00253           axis0 -= axis1;
00254         }
00255       } else if(axisOfLargestRotation == 2) {
00256         Vector3D axis1(t20 + t02, t21 + t12, 1.0 + t22 - t00 - t11);
00257         if(axis0.z() >= 0) {
00258           axis0 += axis1;
00259         } else {
00260           axis0 -= axis1;
00261         }
00262       }
00263 
00264       // Now see about normalizing the unit quaternion.
00265       double axisMagnitudeSquared = dot(axis0, axis0);
00266       if(approximatelyEqual(axisMagnitudeSquared, 0.0, rotationsEpsilon_)) {
00267         // Hmm, we still have a very small axis.  Assume this means that
00268         // the input rotation is nearly zero.
00269         if(sValue >= 0) {
00270           return Quaternion(1.0, 0.0, 0.0, 0.0);
00271         } else {
00272           return Quaternion(-1.0, 0.0, 0.0, 0.0);
00273         }
00274       }
00275       axis0 *= std::sqrt((1 - (sValue * sValue)) / axisMagnitudeSquared);
00276 
00277       // Done.
00278       return Quaternion( sValue, axis0.x(), axis0.y(), axis0.z());
00279     }
00280 
00281   
00282     Vector3D
00283     transform3DToRollPitchYaw(const Transform3D& transform3D)
00284     {
00285       // There must be a better way to get this value.
00286       const double piOverTwo = 1.57079632679;    
00287 
00288       // Start by recovering pitch.
00289       double sinePitch = transform3D.value<0, 2>();
00290       double cosinePitch = std::sqrt(
00291         transform3D.value<0, 0>() * transform3D.value<0, 0>()
00292         + transform3D.value<0, 1>() * transform3D.value<0, 1>());
00293       double pitch = std::atan2(sinePitch, cosinePitch);
00294 
00295       // Now recover roll and yaw.
00296       double roll;
00297       double yaw;
00298       if((!approximatelyEqual(pitch, piOverTwo, rotationsEpsilon_))
00299          && (!approximatelyEqual(pitch, -piOverTwo, rotationsEpsilon_))) {
00300         double cosinePitch = std::cos(pitch);
00301         roll = std::atan2(-(transform3D.value<1, 2>() / cosinePitch),
00302                           (transform3D.value<2, 2>() / cosinePitch));
00303         yaw = std::atan2(-(transform3D.value<0, 1>() / cosinePitch),
00304                          (transform3D.value<0, 0>() / cosinePitch));
00305       } else {
00306         roll = std::atan2(transform3D.value<2, 1>(),
00307                           transform3D.value<1, 1>());
00308         yaw = 0.0;
00309       }
00310       return Vector3D(roll, pitch, yaw);
00311     }
00312 
00313   
00314   } // namespace numeric
00315 
00316 } // namespace dlr

Generated on Mon Jul 9 20:34:03 2007 for dlrLibs Utility Libraries by  doxygen 1.5.2