【问题标题】:Angle between two Euler Angles or Quaternions, Gimbal Lock两个欧拉角或四元数之间的角度,万向节锁
【发布时间】:2017-05-22 16:27:07
【问题描述】:

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

我正在寻找计算两个向量之间的角度,无论它们是四元数还是欧拉角,特别是在人类主体上。 我基本上想找到旋转轴并计算三个基本组件的角度差(x,y,z,一个人似乎不太可能能够扭曲他们的身体并到达云台锁。

我读过this paper,这似乎是您选择接近旋转的方式(x->y->z 让您到达与x->z->y 相同的点,但就所采取的角度而言是不同的路径)是 Gimbal-锁定开始发挥作用,但建议的 XZ'Y'' 序列似乎完全避免了万向节锁定。

我已经读过四元数更容易让计算机计算出我想继续使用四元数的地方,因为我使用的是 Pi,但我只是不太了解如何从四元数转到您的基本x,y,z 分量。所以我想我的问题是:

  1. 四元数对于人体运动是必需的吗?
  2. 在最后的角度计算之前保持四元数中的数字并在最后一步转换为欧拉角会避免万向节锁定吗?

【问题讨论】:

    标签: math rotation quaternions


    【解决方案1】:

    基本上,您有 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
                )
    

    【讨论】:

    • 谢谢!我做了更多的研究,我认为四元数的恒定值真的让我失望。我一直在努力理解四元数的差异如何导致角度,但我现在意识到计算从一个四元数到另一个四元数的旋转四元数可以简单地转换为它的 3D 角度以找到角度的差异。我读到了欧拉角旋转,但我不知道你做旋转的顺序很重要。我实际上发现一个特定的轮换有最小的奇异情况。
    • 谁能告诉我答案中使用了什么语言?
    • @Mozzis 我这里用的是 F#,基本上是 .NET 版本的 OCAML。
    猜你喜欢
    • 2012-06-21
    • 2013-06-22
    • 1970-01-01
    • 1970-01-01
    • 2019-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多