2017-01-08 39 views
0

我有慣性測量單位傳感器,可以輸出四元數或歐拉角數據。作爲一名生物機械師,歐拉角對我來說更有意義,但我也對四元數有所瞭解,但我從未真正研究它們。我確實有一個數學背景,所以我並沒有完全迷失,並且我明白歐拉角中的Gimbal鎖定效應。兩個歐拉角或四元數之間的角度,萬向鎖

我在計算兩個矢量之間的角度,無論它們是四元數還是歐拉角,特別是在人體上。 我基本上想要找到旋轉軸並計算三個基本組件(x,y,z中的角度差異,並且看起來人們不可能扭曲身體並達到Gimbal-lock 。

我讀過this paper,它好像你選擇接近旋轉的方式(x->y->z讓你在相同的點x->z->y但在角度方面不同的路徑採取的)就是萬向鎖進場時,但提議的XZ'Y'序列似乎完全避免了Gimbal-lock。

我讀過四元數對於計算機來說更容易計算哪個是我想要繼續使用四元數的地方,因爲我使用的是Pi,但是我不完全理解如何從四元數到你的基本的x,y,z分量。所以我想我的問題是:

  1. 四元數是人類運動所必需的嗎?
  2. 會保留四元數,直到最終角度計算並在最後一步轉換爲歐拉角避免Gimbal鎖?

回答

0

基本上,你有兩個主要的選擇,例如:與骨架。

  • 使用4×4矩陣(其允許旋轉和平移)
  • 使用(單位)四元數爲平移旋轉和偏移。

如果你看一個函數的典型實現,它帶有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在上面的代碼中的三維矢量的類型的類型。

由四元數旋轉向量的代碼只是那樣簡單數學提示:

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 
      ) 
+0

謝謝!我做了更多的研究,並且我認爲四元數的不變價值只是真的讓我失望。我正在努力瞭解四元數的差異會如何導致角度,但我現在認識到,計算從一個四元數到另一個四元數的旋轉四元數可以簡單地轉換爲其三維角度來找出角度的差異。 我讀到歐拉角旋轉,我不知道你做的旋轉順序的重要性。實際上我發現那裏有一個特定的輪換,其中最小的單例。 – Andrew