2
\$\begingroup\$

I have two quaternions, q1 and q2. If my object's current rotation is at q1, but I need to get it to q2, what would be the process to figure out how much rotation I apply to x, y, and z in order to get there?

In this example, x=right, y=up, z=forward

I've tried getting the delta between the two quaternions and unwrapping that into Eulers and moving the x, y, z the amount of the unwraped Vector3f, but that does not seem to give the correct values. Current implementation in pseudo code:

var dQuat = Quaternion.Inverse(prevRotation) * target.rotation;
dChange = dQuat.eulerAngles;

This approach gives me only positive changes. If I rotate forward on x, I get positive lower numbers and if I rotate backwards on x, I get larger positive numbers.

I'm looking for, in order to get to q2 from q1, rotate n degrees along your x, y, z...

Thanks in advance for any help!

\$\endgroup\$
  • \$\begingroup\$ When you rotate "backwards" on any axis are the values greater than 180? Backwards being where you would expect negative numbers. \$\endgroup\$ – Connor Hollis Oct 8 '15 at 17:38
  • 1
    \$\begingroup\$ why do you need the euler? \$\endgroup\$ – ratchet freak Oct 8 '15 at 18:46
  • \$\begingroup\$ @ConnorHollis correct. Normally in the 300+ range \$\endgroup\$ – nathansizemore Oct 8 '15 at 19:26
  • \$\begingroup\$ @ratchetfreak because I'm physically moving motors on an x, y, and/or z axis. \$\endgroup\$ – nathansizemore Oct 8 '15 at 19:27
  • 1
    \$\begingroup\$ @nathansizemore if the value is over 180 just subtract 360 and see if that's what you want. That should (if I am getting what you are describing correctly) give you a positive 0 to 180 range and a negative 0 to -180 range. \$\endgroup\$ – Connor Hollis Oct 8 '15 at 20:03
1
\$\begingroup\$

Sounds like you are working with step motors which will take a rotation along a fixed axis. Not an answer to your question but an alternative I have used successfully: Work with spherical coordinates (pitch and yaw) to match your two motors on the x and y axis? Keep your direction heading as a vector (pointing in the direction you are gazing). BTW, a quaternion is a gaze vector + rotation around the gaze vector. If you do not have a step motor for the roll around the gaze, you may ignore that anyways. You have +z as forward, so you are using left handed math presumably. I have done the same and display the results using DirectX which uses left handed math. Below is sample code to calculate pitch and yaw from a gaze vector. This gives two Eugler angles around two fixed axis 0 to 360 degrees. Perform this once on your current gaze vector and again on the "to be gaze" vector, and subtract them. Dive by the degree of each motor's step, and that and its sign will give you the proper motor rotation. If you do not have position feedback, then calculate your final gaze vector using your degrees per step intervals so it stay in sync with your motor's true position after every rotation and just use the code snippet below to calculate the "to be gaze" vector.

void CalculatePitchYaw(
    XMFLOAT3 &vectorInOut, // Input: vector direction in x, y, z; Output: x & z snapped to axis near zero & normalized.
    float &pitchRadiansOut, // Output: Pitch 0 to XM_2PI
    float &yawRadiansOut, // Output: Yaw 0 to XM_2PI
    const bool rhs) // set to false if using left hand system vector math 
{
XMVECTOR dir = XMVector3Normalize(XMLoadFloat3(&vectorInOut)); // unit vector for direction only
XMFLOAT3 vec;
XMStoreFloat3(&vec, dir);

const float nearZero = 0.0005; // 1/12500.f increments of a circle (radians); // not too small as we are dividing and using atan2f.  If more accuracy is needed, drop this tolerance and use atan2 (doubles) instead.

// Calculate yaw & pitch
// yaw (bearing in XZ plane)
if (fabsf(vec.x) <= nearZero) // near zero x-axis
{
    vec.x = 0.0f;  // treat as only along the z-axis
    if ((vec.z > 0.0f && rhs) || (vec.z < 0.0f && !rhs)) yawRadiansOut = XM_PI; // 180 deg
    else yawRadiansOut = 0; // 0 deg
}
else if (fabsf(vec.z) <= nearZero) // near zero z-axis
{
    vec.z = 0.0f; // treat as only along the x-axis
    if (vec.x > 0.0f) yawRadiansOut = XM_PIDIV2; // 90 deg ; right of z-axis
    else yawRadiansOut = XM_2PI - XM_PIDIV2; // 270 deg ; left of z-axis
}
else yawRadiansOut = atan2f(vec.x, vec.z);// rotation about the y-axis; neither near x nor z axis

// pitch (bearing in YZ plane)
vec.z = fabsf(vec.z);
vec.x = fabsf(vec.x);
if (vec.x > vec.z) vec.z = vec.x;

pitchRadiansOut = atan2f(-vec.y, vec.z); // rotation about the x-axis

vectorInOut = vec;

while (yawRadiansOut < 0.0f) yawRadiansOut += XM_2PI;
while (pitchRadiansOut < 0.0f) pitchRadiansOut += XM_2PI;
}
\$\endgroup\$
1
\$\begingroup\$

If I understand the question correctly, here's an approach that might work:

  1. Choose an arbitrary vector, e.g. (0, 0, 1)
  2. Multiply it by the first quaternion (q1) to get v1
  3. Multiply it also by the second quaternion (q2) to get v2
  4. Now calculate a new quaternion (q3) from v1 and v2. See code from the GLM library.
  5. Convert q3 to Euler angles and you have your answer. See code from the GLM library.
\$\endgroup\$
  • \$\begingroup\$ You'll need to do this with two vectors, to control for twist around the axis of the first vector. \$\endgroup\$ – DMGregory Oct 20 '15 at 12:44
  • \$\begingroup\$ Come to think of it, there's another problem here. Euler angles don't just sum like one might expect. Because the axes aren't always orthogonal, the effect of a given incremental rotation depends on the orientation you started in (the extreme case being gimbal lock, where two axes are parallel) So computing the Euler angles of the Quaternion difference won't get you the incremental Euler rotations to apply to get to the end quaternion from the start quaternion. To do that, we'd need to take into account the Euler angles of the starting configuration in some way. \$\endgroup\$ – DMGregory Oct 20 '15 at 12:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.