四元数绕轴旋转的分量

时间:2010-09-10 11:34:14

标签: rotation quaternions

我无法找到有关此主题的任何有用信息。基本上我想找到四元数旋转的分量,即围绕给定轴(不一定是X,Y或Z - 任意单位矢量)。有点像将四元数投影到矢量上。因此,如果我要求围绕与四元数轴平行的某个轴进行旋转,我将得到相同的四元数。如果我要求围绕与四元数轴正交的轴旋转,我会得到一个标识四元数。中间......嗯,这就是我想知道如何解决的问题:)。

5 个答案:

答案 0 :(得分:27)

这个问题有一个优雅的解决方案,特别适合四元数。它被称为"摆动扭曲分解":

在伪代码中

/**
   Decompose the rotation on to 2 parts.
   1. Twist - rotation around the "direction" vector
   2. Swing - rotation around axis that is perpendicular to "direction" vector
   The rotation can be composed back by 
   rotation = swing * twist

   has singularity in case of swing_rotation close to 180 degrees rotation.
   if the input quaternion is of non-unit length, the outputs are non-unit as well
   otherwise, outputs are both unit
*/
inline void swing_twist_decomposition( const xxquaternion& rotation,
                                       const vector3&      direction,
                                       xxquaternion&       swing,
                                       xxquaternion&       twist)
{
    vector3 ra( rotation.x, rotation.y, rotation.z ); // rotation axis
    vector3 p = projection( ra, direction ); // return projection v1 on to v2  (parallel component)
    twist.set( p.x, p.y, p.z, rotation.w );
    twist.normalize();
    swing = rotation * twist.conjugated();
}

此代码的长答案和推导可以在这里找到 http://www.euclideanspace.com/maths/geometry/rotations/for/decomposition/

答案 1 :(得分:16)

我花了一天时间试图为动画编辑器找到完全相同的东西;我是这样做的:

  1. 获取想要找到旋转的轴,并找到它的正交向量。
  2. 使用四元数旋转此新矢量。
  3. 将此旋转矢量投影到法线
  4. 的平面上
  5. 此投影矢量和原始正交的点积的acos是您的角度。

    public static float FindQuaternionTwist(Quaternion q, Vector3 axis)
    {
        axis.Normalize();
    
        // Get the plane the axis is a normal of
        Vector3 orthonormal1, orthonormal2;
        ExMath.FindOrthonormals(axis, out orthonormal1, out orthonormal2);
    
        Vector3 transformed = Vector3.Transform(orthonormal1, q);
    
        // Project transformed vector onto plane
        Vector3 flattened = transformed - (Vector3.Dot(transformed, axis) * axis);
        flattened.Normalize();
    
        // Get angle between original vector and projected transform to get angle around normal
        float a = (float)Math.Acos((double)Vector3.Dot(orthonormal1, flattened));
    
        return a;
    }
    
  6. 以下是找到正交矩阵的代码,但如果您只想要上述方法的代码,那么您可以做得更好:

    private static Matrix OrthoX = Matrix.CreateRotationX(MathHelper.ToRadians(90));
    private static Matrix OrthoY = Matrix.CreateRotationY(MathHelper.ToRadians(90));
    
    public static void FindOrthonormals(Vector3 normal, out Vector3 orthonormal1, out Vector3 orthonormal2)
    {
        Vector3 w = Vector3.Transform(normal, OrthoX);
        float dot = Vector3.Dot(normal, w);
        if (Math.Abs(dot) > 0.6)
        {
            w = Vector3.Transform(normal, OrthoY);
        }
        w.Normalize();
    
        orthonormal1 = Vector3.Cross(normal, w);
        orthonormal1.Normalize();
        orthonormal2 = Vector3.Cross(normal, orthonormal1);
        orthonormal2.Normalize();
    }
    

    虽然以上工作原理,但您可能会发现它的行为并不像您期望的那样。 例如,如果四元数将矢量旋转90度。大约X和90度。在Y周围你会发现,如果你在Z周围分解旋转它将是90度。同样。如果你想象一个矢量进行这些旋转,那么这是完全合理的,但根据你的应用,它可能不是所希望的行为。 对于我的应用 - 约束骨骼关节 - 我最终得到了一个混合系统。矩阵/ Quats一直使用,但是当涉及到约束关节的方法时,我在内部使用了欧拉角,每次都将旋转四元组分解为围绕X,Y,Z的旋转。

    祝你好运,希望有所帮助。

答案 2 :(得分:1)

minorlogic的答案指出摇摆扭曲分解是迄今为止最好的答案,但它缺少重要的一步。问题(使用minorlogic的伪代码符号)是,如果rap的点积为负,则需要取反twist的所有四个分量,以便得到旋转轴指向与direction相同的方向。否则,如果您尝试测量旋转角度(忽略轴),则会混淆正确旋转和正确旋转的 reverse ,这取决于旋转轴是否呼叫projection(ra, direction)时方向发生了翻转。请注意,projection(ra, direction)将计算点积,因此您应该重复使用它,而不要计算两次。

这是我自己的摆动扭曲投影版本(在某些情况下使用不同的变量名,而不是minorlogic的变量名),并且进行了点积校正。代码用于JOML JDK库,例如v.mul(a, new Vector3d())计算a * v,并将其存储在新的向量中,然后将其返回。

/**
 * Use the swing-twist decomposition to get the component of a rotation
 * around the given axis.
 *
 * N.B. assumes direction is normalized (to save work in calculating projection).
 * 
 * @param rotation  The rotation.
 * @param direction The axis.
 * @return The component of rotation about the axis.
 */
private static Quaterniond getRotationComponentAboutAxis(
            Quaterniond rotation, Vector3d direction) {
    Vector3d rotationAxis = new Vector3d(rotation.x, rotation.y, rotation.z);
    double dotProd = direction.dot(rotationAxis);
    // Shortcut calculation of `projection` requires `direction` to be normalized
    Vector3d projection = direction.mul(dotProd, new Vector3d());
    Quaterniond twist = new Quaterniond(
            projection.x, projection.y, projection.z, rotation.w).normalize();
    if (dotProd < 0.0) {
        // Ensure `twist` points towards `direction`
        twist.x = -twist.x;
        twist.y = -twist.y;
        twist.z = -twist.z;
        twist.w = -twist.w;
        // Rotation angle `twist.angle()` is now reliable
    }
    return twist;
}

答案 3 :(得分:0)

我尝试实现sebf的答案,看起来不错,除了在步骤1中选择向量的选择:

  
      
  1. 取你想要找到旋转的轴,找到一个   正交向量。
  2.   

不足以获得可重复的结果。我已经在纸上开发了这个,我建议采用以下行动方案来选择与“想要找到旋转轴的轴”正交的矢量,即观察轴。存在与观察轴正交的平面。您必须将四元数的旋转轴投影到此平面上。使用此结果向量作为与观察轴正交的向量将得到良好的结果。

感谢sebf让我选择了正确的课程。

答案 4 :(得分:0)

Unity3d的代码

// We have some given data
Quaternion rotation = ...;
Vector3 directionAxis = ...;

// Transform quaternion to angle-axis form
rotation.ToAngleAxis(out float angle, out Vector3 rotationAxis);

// Projection magnitude is what we found - a component of a quaternion rotation around an axis to some direction axis
float proj = Vector3.Project(rotationAxis.normalized, directionAxis.normalized).magnitude;