两个欧拉角或四元数之间的角度,万向节锁

时间:2017-01-08 07:44:04

标签: math rotation quaternions

我有惯性测量单位传感器,可以输出四元数或欧拉角的数据。作为生物力学家,欧拉角对我来说更有意义,但我也理解四元数,但我从未真正研究它们。我确实有数学背景,所以我没有完全迷失,我理解欧拉角的万向节锁效应。

我正在计算两个向量之间的角度,无论它们是四元数还是欧拉角,特别是在人类主体上。 我基本上想要找到旋转轴并计算三个基本组件(xyz中的角度差异,似乎非常不可能一个人能够扭曲他们的身体并达到万向节锁定。

我看过this paper,看起来你选择接近旋转的方式(x->y->z会让你到达与x->z->y相同的点,但在角度方面却是不同的路径采取)是万向节锁定发挥作用的地方,但拟议的XZ'Y'序列似乎完全避免了万向节锁定。

我已经读过,四元数对于计算机来说更容易计算,因为我使用的是Pi,所以我想继续使用四元数,但我还不完全了解如何从四元数到基数x,y,z组件。所以我想我的问题是:

  1. 人体运动需要四元数吗?
  2. 在四元数中保持数字直到最后一个角度计算并在最后一步转换为欧拉角避免万向节锁?

1 个答案:

答案 0 :(得分:0)

基本上,您有2个主要选项可供使用,例如:有骨架。

  • 使用4x4矩阵(允许旋转和平移)
  • 使用(单位)四元数进行旋转和偏移以进行翻译。

如果你看一个函数的典型实现,它采用2个向量并返回四元数,给它们之间的旋转,你会发现它不仅仅是一个简单的公式。边缘案件正在被识别和处理。

let rotFromVectors (v1 : vec3) (v2 : vec3) : quat =
    let PI = System.Math.PI
    let PI_BY_TWO = PI / 2.0
    let TWO_PI = 2.0 * PI
    let ZERO_ROTATION = quat(0.0f,0.0f,0.0f,1.0f)
    let aabb = sqrt (float (vec3.dot(v1, v1)) * float (vec3.dot(v2,v2)))
    if aabb <> 0.0
    then
        let ab = float (vec3.dot(v1,v2)) / aabb
        let c = 
            vec3
                ( float32 ((float v1.y * float v2.z - float v1.z * float v2.y) / aabb)
                , float32 ((float v1.z * float v2.x - float v1.x * float v2.z) / aabb)
                , float32 ((float v1.x * float v2.y - float v1.y * float v2.x) / aabb)
                )
        let cc = float (vec3.dot(c, c))
        if cc <> 0.0
        then
            let s =
                match ab > -sin (PI_BY_TWO) with //0.707107f
                | true -> 1.0 + ab
                | false -> cc / (1.0 + sqrt (1.0-cc))
            let m = sqrt (cc + s * s)
            quat(float32 (float c.x / m), float32 (float c.y / m), float32 (float c.z / m), float32(s / m))
        else
            if ab > 0.0
            then 
                ZERO_ROTATION
            else
                let m = sqrt (v1.x * v1.x + v1.y * v1.y)
                if(m <> 0.0f)
                then
                    quat(v1.y / m, (-v1.x) / m, 0.0f, 0.0f)
                else
                    quat(1.0f,0.0f,0.0f,0.0f)
    else
        ZERO_ROTATION

其中quat是四元数的类型,vec3是上述代码中3D矢量的类型。

通过四元数旋转矢量的代码与数学建议一样简单:

let rotateVector (alpha : quat) (v:vec3) : vec3 =
    let s = vec3.length v
    quat.inverse alpha * (vecToPureQuat v) * alpha |> pureQuatToVec |> fun v' -> v' * s

最后并非最不重要的是(某些......欧拉角 - 实际上有24个不同版本的欧拉角,12个具有固定角度旋转,12个具有连续旋转)之间的转换函数使用半角方法。

let eulerToRot (v:vec3) : quat =
    let d = 0.5F
    let t0 = cos (v.z * d)
    let t1 = sin (v.z * d)
    let t2 = cos (v.y * d)
    let t3 = sin (v.y * d)
    let t4 = cos (v.x * d)
    let t5 = sin (v.x * d)
    quat
        (   t0 * t3 * t4 - t1 * t2 * t5
        ,   t0 * t2 * t5 + t1 * t3 * t4
        ,   t1 * t2 * t4 - t0 * t3 * t5
        ,   t0 * t2 * t4 + t1 * t3 * t5
        )
    |> quat.normalize

let rotToEuler (q:quat) : vec3 =
    let ysqr = q.y * q.y
    // roll (x-axis rotation)
    let t0 = +2.0f * (q.w * q.x + q.y * q.z)
    let t1 = +1.0f - 2.0f * (q.x * q.x + ysqr)
    let roll = atan2 t0 t1

    // pitch (y-axis rotation)
    let t2 = 
        let t2' = +2.0f * (q.w * q.y - q.z * q.x)
        match t2' with
        | _ when t2' > 1.0f -> 1.0f
        | _ when t2' < -1.0f -> -1.0f
        | _ -> t2'
    let pitch = asin t2

    // yaw (z-axis rotation)
    let t3 = +2.0f * (q.w * q.z + q.x *q.y)
    let t4 = +1.0f - 2.0f * (ysqr + q.z * q.z)
    let yaw = atan2 t3 t4
    vec3(roll,pitch,yaw)

最后要知道的是,将矢量转换为(纯)四元数对于rotateVector函数来说非常方便。

let vecToPureQuat (v:vec3) : quat =
    quat(v.x,v.y,v.z,0.0f)

let pureQuatToVec (q:quat) : vec3 =
    vec3(q.x,q.y,q.z)

所以,回答你的主要问题:四元数是否必要?不。您也可以使用4x4矩阵。

如果它认为你有用,你可以从一个到另一个:

    let offsetAndRotToMat (offset:vec3) (q:quat) : mat4 =
        let ux = v3 1 0 0
        let uy = v3 0 1 0
        let uz = v3 0 0 1
        let rx = rotateVector q ux 
        let ry = rotateVector q uy 
        let rz = rotateVector q uz 
        mat4
            (
                rx.x, rx.y, rx.z, 0.0f,
                ry.x, ry.y, ry.z, 0.0f,
                rz.x, rz.y, rz.z, 0.0f,
                offset.x,offset.y,offset.z,1.0f
            )