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

Generated on Wed Nov 25 00:42:42 2009 for dlrUtilities Utility Library by  doxygen 1.5.8