四元數的“前世”與“今生”

  大家好,這是我的第一篇文章。今後自己的博客文章會不定期進行更新,大部分主題將會是我對近幾年GDC (遊戲開發者大會)上的數學專題與編程專題的學習心得(總結)~
  這一篇文章目的是向大家深入介紹四元數,主要的參考文獻是2013年GDCUnderstandingQuaternions 。和很多博客不同的是,自己最後會嘗試利用一些羣論的知識去理解四元數,學習過相關知識的同學可以看看,相互探討一下。文章篇幅可能有些長,可以直接在目錄裏面選擇自己需要了解的進行閱讀。

內容大綱

  • 背景
  • 複數
  • 四元數
  • 羣論觀點下的四元數
  • 四元數在Unity 中的應用

目標(將會回答以下幾個問題)

  • 爲什麼是四個元?
  • i,j,k 是什麼?
  • 爲什麼旋轉的公式是qpq1
  • 爲什麼是θ/2
  • 我們應該如何理解4D4D 可視化)?

  那麼話不多說,先來進入第一部分。

背景

  • 旋轉定義
  • 旋轉矩陣
  • 歐拉角
  • 羅德里格旋轉公式

旋轉定義

  通常將物體繞一個點或者一個軸轉動一定角度的操作稱爲旋轉。不妨先考慮2D 的情形:


這裏寫圖片描述
旋轉示意圖

  那麼自然而言便會引申出旋轉角的概念:

 
這裏寫圖片描述
旋轉角示意圖

  顯然,旋轉角串聯起了旋轉前後的兩個圖形。而這種“串聯”關係,通常採用矩陣的語言進行描述。

旋轉矩陣

  在線性代數裏,我們知道:
- 一個矩陣代表一種變換(並不一定是旋轉變換)。
- 一個旋轉矩陣乘以一個向量(左乘或者右乘)將使得該向量被旋轉。

  其中,2維的旋轉矩陣形式如下:

(cosθsinθsinθ   cosθ)

  通過觀察上面的旋轉角示意圖,這樣一個矩陣形式是容易理解的。通過對座標系作逆時針θ 角度的旋轉以後,x 軸上的單位向量(1,0)變成(cosθ,sinθ) ,而y 軸上的單位向量(0,1)變成(sinθ,cosθ) ,把它們列排即得二維的旋轉矩陣。
  對於3維的旋轉矩陣而言,其矩陣形式是與2維的旋轉矩陣類似的:均通過將座標軸上的單位向量經旋轉後的像作爲其旋轉矩陣的某一列。以繞x 軸逆時針旋轉θ 角度(右手座標系)爲例,其旋轉矩陣形式如下:
10   00 cosθsinθ0 sinθcosθ

  並且,旋轉矩陣有一個非常完美的性質:正交矩陣,即對於旋轉矩陣M 而言,有:
- M1=MT
- MTM=E
- |M|=1
  在算旋轉矩陣的逆矩陣的時候,可以直接求其轉置矩陣,省下許多計算量。其次,其行列式爲1意味着在經過旋轉變換以後,圖形的大小和形狀都不會發生改變(比方說一個圓繞其圓心旋轉任意角度,其半徑都不會改變)。關於旋轉矩陣是正交矩陣的證明可以參見這篇文章:旋轉矩陣(Rotate Matrix)的性質分析
  在正交矩陣的前提下,我們可以得到旋轉矩陣一個有趣的性質:旋轉矩陣對應特徵值1的特徵向量爲其旋轉軸。因爲旋轉矩陣爲正交矩陣,故其特徵值爲±1 ,當有不少於1個特徵值爲1時,說明物體不僅僅是在繞x 軸(或y,z 軸)旋轉。

歐拉角

  歐拉這樣一位劃時代的數學大師提出的歐拉角,是一種十分簡單的表示旋轉的有力工具。
  來看看wikipedia 對歐拉角的定義:

令原始座標系的三個座標軸分別爲x,y,z ,旋轉後的座標系的三個座標軸爲X,Y,ZN 軸是xOy 平面與XOY 平面的交線(也可以利用外積將N 軸定義爲N=z×Z )。這樣一來,三個歐拉角可定義如下:
- α (或φ ) 是x 軸和N 軸的夾角;
- β (或θ ) 是z 軸和Z 軸的夾角;
- γ (或ψ ) 是N 軸和X 軸的夾角;


這裏寫圖片描述
歐拉角示意圖

  也就是說,歐拉角將繞一個過原點的旋轉軸旋轉θ 分解成了“三步曲”(此時xyz 座標系跟隨旋轉):
1. 繞z 軸旋轉α ,使得x 軸與N 軸重合;
2. 繞x 軸旋轉β ,使得z 軸與Z 軸重合;
3. 繞z 軸旋轉γ ,此時xyz 座標系將與XYZ 座標系重合。

  這樣的旋轉順序被稱爲是“zxz ”順序的,實際上並不一定非要按照這個順序進行旋轉,這個我們等等會再提及一下,此處先以“zxz ”順序爲標準繼續進行介紹。
  看到這裏,也許你已經想罵人了:嘿,說好的“歐拉角很簡單呢?”。別急,可能這樣將座標系旋轉來旋轉去確實會令人有些頭大(想象不出來如何旋轉的同學,可以參看歐拉角-wikipedia 百科裏的動圖演示)。接下來,我們會證明上述的旋轉過程是與下述旋轉過程(此時xyz 座標系不跟隨旋轉,注意與上述旋轉過程的區別)等價的:
1. 繞z 軸旋轉γ
2. 繞x 軸旋轉β
3. 繞z 軸旋轉α

  爲了證明上述兩種旋轉過程等價(即最終的旋轉結果相同),可以利用歐拉角對應的旋轉矩陣進行證明,證明過程如下:(參考自歐拉角與萬向節死鎖
  記
- 繞z 軸旋轉α ,使得x 軸與N 軸重合對應的旋轉矩陣爲Mz1N
- 繞x 軸旋轉β ,使得z 軸與Z 軸重合對應的旋轉矩陣爲MxN
- 繞z 軸旋轉γ ,此時xyz 座標系將與XYZ 座標系重合對應的旋轉矩陣爲Mz2N ;
- 繞z 軸旋轉γ 對應的旋轉矩陣爲Mz2
- 繞x 軸旋轉β 對應的旋轉矩陣爲Mx
- 繞z 軸旋轉α 對應的旋轉矩陣爲Mz1

  那麼問題便轉化爲求證:Mz1NMxNMz2N=Mz2MxMz1
證明:
  顯然,Mz1N=Mz1 ,因爲此時的旋轉還不受座標系改變的影響。
  而對於MxN ,要得到繞x 軸旋轉β ,使得z 軸與Z 軸重合的效果,可以先繞z 軸旋轉α (此時座標系跟隨旋轉),然後繞x 軸旋轉β ,最後再繞z 軸旋轉α (腦海裏面想象不出來的話可以拿些物體比劃比劃,我就是這麼過來的=。=)。用旋轉矩陣的語言描述出來即爲:
  

MxN=M1z1NMxMz1N.

  同理可得:
  
Mz2N=(Mz1NMxN)1Mz2(Mz1NMxN).

  綜上所述有:
Mz1NMxNMz2N=Mz1NMxN(Mz1NMxN)1Mz2(Mz1NMxN)=(Mz1NMxN)(Mz1NMxN)1Mz2(Mz1NMxN)=Mz2(Mz1NMxN)=Mz2(Mz1NM1z1NMxMz1N)=Mz2MxMz1N=Mz2MxMz1

  因此,實際上在使用歐拉角表示旋轉時,可以直接將其理解爲先繞z 軸旋轉γ ,再繞x 軸旋轉β ,最後繞z 軸旋轉α 。這樣不涉及座標系的旋轉,在理解和運用上便會簡單得多。
  需要注意的是:
1. 歐拉角的旋轉順序並不一定都是“zxz ”順序,你也可以根據自己需要定義爲“xyz ”,“zyx ”等等,上述性質是不會改變的。只要保證前後定義的旋轉順序一樣即可,否則可能會出現無法預料的錯誤,畢竟不同旋轉順序下旋轉效果還是有可能大不相同的。
2. 細心的同學可能會發現,在旋轉矩陣中,最先進行的旋轉操作對應的旋轉矩陣在最右側與向量相乘,旋轉矩陣按照旋轉的次序從右向左排列;而在歐拉角中,最先進行的旋轉操作(此處說的是座標系跟隨旋轉的情形)對應的旋轉矩陣在最左側與向量相乘。看到一篇文章,三維旋轉:旋轉矩陣,歐拉角,四元數,對於這個問題是解釋得不錯的:

   對於前者(旋轉矩陣),我們始終是以絕對參考系爲參照來的,對於後者(歐拉角),我們每一次旋轉的刻畫都是基於剛體的座標系。比如,在歐拉角中的第2步,繞x 軸旋轉β ,這裏的x 軸實際上是N 軸了(而不是藍色的x 軸)。
  爲什麼旋轉參考系的不同會導致旋轉矩陣次序的差異呢?細想一下便知,旋轉矩陣左乘疊加用以描述三維變換效果的疊加,這本身就是基於絕對座標系的,所以旋轉矩陣一節沒有疑問;而對於歐拉角一節的這種旋轉方式,這樣考慮:
1. 如果有一個“影子座標系3”與原座標系重合,然後首先進行了第3步(繞z 軸旋轉γ );
2. 然後有一個“影子座標系2”也與原座標系重合,然後與“影子座標系3”一起(視作同一個剛體)進行了第二步;
3. 最後一個“影子座標系1”,與前兩個座標系一起進行了第一步。
  此時,考察“影子座標系”1和2,他們就分別落在了歐拉角旋轉的兩個“快照”上,而“影子座標系3”就落在旋轉後的位置上(紅色的)。  
  而在上述過程中,“影子座標系3”就是相對於絕對座標系依次進行了第三步,第二步,和第一步。所以歐拉角的旋轉矩陣寫成那樣,也是行得通的。

  說了這麼多,先停下來總結一下旋轉矩陣和歐拉角:
旋轉矩陣
- 優點:學過線性代數的都能很容易地理解;
- 缺點:和其它表示旋轉的方式相比,其空間消耗較大,一個三維的旋轉矩陣需要存儲9個元素,而且在作矩陣乘法時也會消耗較多的時間。
- 歐拉角
- 優點:表示簡單,僅需要存儲三個值,(α,β,γ)
- 缺點:在編程實現上,通常還是將其轉化爲旋轉矩陣進行計算,其空間複雜度和時間複雜度沒有太大的變化;且在使用過程當中可能會出現著名的萬向節鎖現象,直觀理解可以參考這個視頻,歐拉旋轉
  這個視頻比較形象地講解了何謂萬向節鎖,萬向節鎖大體說來便是物體繞某個座標軸旋轉了90度,使得某兩個座標軸平面重合,從而丟失了一個維度。在這種情況下,無論接下來作什麼旋轉都無法將物體旋轉到某個角度,除非打破原先定義的旋轉順序或者同時旋轉三個座標軸。這樣一個問題的產生也使得歐拉角在球面平滑插值上“力不從心”。
  這樣看來,也許四元數的出現正是一個timing 吧……

羅德里格旋轉公式

  提到歐拉角,就不得不提及羅德里格旋轉公式。羅德里格旋轉公式的出現將歐拉角從較複雜的旋轉矩陣計算“解放”了出來。該旋轉公式是這樣的:

給定旋轉軸r⃗  ,旋轉角θ 以及一個點p⃗  ,則p⃗  繞旋轉軸r⃗  旋轉θ 後的點的計算公式爲:

R(r⃗ ,θ,p⃗ )=p⃗ cosθ+(r⃗ ×p⃗ )sinθ+r⃗ (r⃗ p⃗ )(1cosθ).

  該公式是由法國數學家Benjamin Olinde Rodrigues 提出的,當然這位數學家最著名的工作並非這個旋轉公式,而是勒讓德多項式……好吧,有點扯遠了……關於羅德里格旋轉公式的證明在此先簡略,有興趣的同學直接參看羅德里格旋轉公式的維基百科即可。(Rodriguesrotation formula

  羅德里格旋轉公式的核心思想是向量分解:不妨先假設三維空間中待旋轉的點(向量)爲p⃗  ,將其分解爲兩個分向量的和,其中一個向量平行於旋轉軸,另外一個向量與旋轉軸正交。這樣,我們便可以使用二維平面上的旋轉方法操作p⃗  正交於旋轉軸的分向量(如下圖所示):



羅德里格旋轉公式示意圖

  如此一來,我們直接在一個2維平面上旋轉p⃗  正交於旋轉軸的分向量之後,再把它與p⃗  平行於旋轉軸的分向量相加即得最終的結果。某種程度上,我們將3維的旋轉操作轉化爲了2維的旋轉操作,達到“降維”的效果。
  有趣的是,羅德里格旋轉公式通常還有另外一種形式(先劇透一下,這與後面要介紹的四元數息息相關~):

a2+b2+c2+d2=1 ,其中a=cos(θ/2) ,再設r=(b,c,d)=sin(θ/2)r⃗  ,那麼羅德里格旋轉公式還可以寫成:

R(a,r,p⃗ )=2a(r×p⃗ )+2(r×(r×p⃗ ))+p⃗ .

證明:將a=cos(θ/2),r=(b,c,d)=sin(θ/2)r⃗  代入即可,

2a(r×p⃗ )=2cos(θ/2)(sin(θ/2)r⃗ ×p⃗ )=(r⃗ ×p⃗ )sinθ,2(r×(r×p⃗ ))+p⃗ =2(r(rp⃗ ))p⃗ r2)+p⃗ ()=2(sin(θ/2))2r⃗ (r⃗ p⃗ )2(1(cos(θ/2))2)p⃗ +p⃗ ()=r⃗ (r⃗ p⃗ )(1cosθ)+p⃗ cosθ.

  因此,R(r⃗ ,θ,p⃗ )=R(a,r,p⃗ ) ,說明羅德里格旋轉公式的兩種表示形式是等價的。
  羅德里格旋轉公式第二種表示形式提出了可以用於創建3維旋轉的4個參數。也許對於已經學過四元數的同學,看到這裏應該會有似曾相識的感覺吧……別急,咱們繼續往下看~(^▽^)

複數

  呼呼呼……終於進入第二部分嚕,談及四元數不可避免地需要提及複數。我個人始終認爲,兩者之間的聯繫是密不可分的。
  複數最初來自於求解一些特定的二次方程,比如x2+1=0 ,其解爲x=1 ,不妨給它一個特定的名字,就讓x=i 吧。
  複數便是這樣一類數字:

a+bi,a,bR,i=1,

  其中,a 爲實部,b 爲虛部,當忽略虛部b 時,就是我們常見的實數了。
  通常爲了便於理解,我們會用二維平面上的一點來表示一個複數,此時兩個座標軸分別爲實數軸和虛數軸:

這裏寫圖片描述

  四元數的乘法運算法則爲:

(a+bi)(c+di)=(acbd)+(bc+ad)i,

  也可以寫成:   
(a,b)(c,d)=(acbd,bc+ad).

  因此,一個複數也只是一個帶有特殊乘法運算的2維向量罷了。
  同時,我們可以得到:
(x+yi)(cosθ+isinθ)=(xcosθysinθ)+i(xsinθ+ycosθ).

  經過旋轉矩陣一節的“薰陶”以後,相信對這個形式已經不陌生了。沒錯,這就是2維旋轉的複數形式!!!

四元數

  • 四元數的誕生
  • 四元數的性質
  • 四元數與三維旋轉的“千絲萬縷”
  • 四元數與四維旋轉
  • 四元數的可視化
  • 四元數的插值

  第二部分嘩地一下就結束了(大霧),主要是因爲上過高中的應該都學過複數,因此許多性質也就不再贅述。第二部分僅列出理解四元數需要的一些“前置技能”而已,現終於進入核心章節——四元數了(撒花(^▽^))。

四元數的誕生

  愛爾蘭數學家漢密爾頓可以說是“四元數之父”了。他在回憶錄當中大概是這樣描述四元數的誕生的:有一天他在吃早餐時,他的兒子問他:“爸爸,你可以讓兩個三維向量相乘得到另外一個三維向量嗎?”他遺憾地回答:“不,我只能將兩個三維向量相加減得到另外一個三維向量。”

(a0+b0i+c0j)(a1+b1i+c1j)=(a0a1b0b1c0c1)+(a0b1+b0a1)i+(a0c1+c0a1)j+b0c1ij+c0b1ji.

  那麼這裏就會出現問題了,ij,ji 應該等於多少?顯然這裏的乘法運算不能直接定義爲向量內積,因爲兩個向量作內積的結果是一個實數(降維了……)。此外,哈密爾頓還不希望該乘法運算不滿足交換律,且兩個非零向量作乘法運算以後不能產生零向量(即沒有零因子, 想起無零因子環???)當時哈密爾頓無法定義出滿足上述條件的乘法運算。顯然,在這裏叉積也是無法奏效的,因爲兩個平行向量的叉積爲零向量,而且當時的時代也還沒有叉積這個概念……
  終於在1843年,當漢密爾頓在都柏林的布魯姆橋下沿着皇家運河散步時,他突然意識到僅僅三個維度是無法定義出滿足上述條件的乘法運算,必須增加一個維度才行!四元數就此誕生!他非常興奮地將他的成果雕刻在橋上,大致內容如下:
  任意一個四元數可以表示成ω+xi+yj+zk,ω,x,y,zR 的形式,其中
i2=j2=k2=ijk=1,ij=k,jk=i,ki=j,ji=k,kj=i,ik=j,

  用乘法表即可表示成:

這裏寫圖片描述

  注意,這裏的乘法運算不再滿足交換律。如果你熟悉叉積,這可能看起來很熟悉。實際上,叉積正是來自四元數。
  此外,對於上述定義的乘法運算,每一個非零向量都有其逆元。後面還會提到,這意味着當我們希望撤銷某一個旋轉時,我們只要取該旋轉操作對應的四元數的逆元即可,這是十分方便的。
  講到這裏,我們現在已經可以回答前兩個問題了(可能有同學已經忘了最初的問題是什麼,這裏再貼一下):

  • 爲什麼是四個元?
  • i,j,k 是什麼?
  • 爲什麼旋轉的公式是qpq1
  • 爲什麼是θ/2
  • 我們應該如何理解4D4D 可視化)?

 
對於問題1:爲什麼是四個元?
  因爲僅僅三個維度是無法定義出滿足條件的乘法運算的:
1. 兩個向量作乘法運算的結果仍舊是一個向量;
2. 該乘法運算不滿足交換律;
3. 兩個非零向量作乘法運算以後不能產生零向量。
對於問題2:i,j,k 是什麼?
  存在於四維空間的三個座標軸。

四元數的性質 

  某種程度上,四元數是由複數擴展而來的:

a+biω+xi+yj+zk

  它可以表示四維平面上的一點(ω,x,y,z) ,或者可以寫成(ω,v),v=(x,y,z) ,這是四元數很常用的一種表示形式,大家可以記一記。
  四元數的加減和普通的向量加減是類似的,因此這裏不再贅述。我們講講四元數的乘法。
  取兩個四元數q0=(ω0,v0),q1=(ω1,v1) ,定義四元數之間的乘法運算如下:
q1q0=(ω1ω0v1v0,ω1v0+ω0v1+v1×v0).

  需要注意的是,因爲包含叉積運算的緣故,故四元數之間的乘法運算一般不滿足交換律:q1q0q0q1
  這樣,我們容易知道,對於四元數之間的乘法運算和任意四元數q ,其單位元是e=(1,0,0,0) ,(eq=qe=q )。當q 非零元(0,0,0,0)時,q1 存在,使得qq1=q1q=e 成立。

四元數與旋轉的“千絲萬縷”

  與複數類似,單位四元數(記住,這類四元數的模是1!!!)可以表示一個旋轉,後面會進行證明。
  對於3維旋轉,令
\omega=cos(\theta/2),\\(x,y,z)=v=sin(\theta/2)r.

ω=cos(θ/2),(x,y,z)=v=sin(θ/2)r.

  看過前面的羅德里格旋轉公式的同學,一定對此表示十分熟悉……接下來便會介紹如何利用四元數進行3維向量的旋轉。
  對於單位四元數q=(\omega,v)=(cos(\theta/2),sin(\theta/2)r) 而言,我們很容易就可以知道它的逆元(自己拿筆在紙上驗證一下即可):
q^{-1}=(\omega,v)^{-1}=(cos(-\theta/2),sin(-\theta/2)r)\\=(cos(\theta/2),-sin(\theta/2)r)\\=(\omega,-v)=q^{*}.

  易知,單位四元數的逆元是和其共軛相等的。再次強調一下,這個性質僅僅是對單位四元數成立。有了它,四元數的旋轉定理(自己命名的,捂臉逃走(/ω\))也就順理成章地騰空出世了~

四元數旋轉定理:
  已知三維向量p 和單位四元數q ,將p 看成一個四元數(爲了方便,我依舊命名爲p ),即p=(0,p) ,則向量p 經旋轉q 操作後的結果爲:
p'=qpq^{-1}=qpq^{*}.


  還可以寫成如下形式:
p'=p+2\omega(v\times p)+2(v\times(v\times p)).

  (喂,這不就是羅德里格旋轉公式嗎……)

  先來看第二種形式,你們的猜測是沒錯的,這其實就是羅德里格旋轉公式的“變種”,證明方法也與羅德里格旋轉公式的證明方法類似,只要先把單位四元數q 拆成\omegav 即可,即:
四元數
\omega=cos(\theta/2),(x,y,z)=v=sin(\theta/2)\vec r,\\p'=p+2\omega(v\times p)+2(v\times(v\times p));


羅德里格旋轉公式
a=cos(\theta/2),(b,c,d)=r=sin(\theta/2)\vec r,\\p'=p+2a(r\times p)+2(r\times(r\times p)).

  不得不佩服一下,羅德里格真的是一個富有遠見的數學家……
  再來看看第一種形式:p'=qpq^{-1}=qpq^{*} ,它的證明可不簡單,但我覺得還是有必要寫一下,因爲它的證明確實是蠻精彩的。(當然,沒興趣的同學就跳過吧……)證明過程參考自博客三維旋轉:旋轉矩陣,歐拉角,四元數,不過我個人感覺博主的證明寫得有些亂,因此我整理如下:
p'=qvq^{-1}=qvq^{*}.

證明:如下圖所示,u 爲旋轉軸上的單位向量,旋轉角度爲\sigma ,向量v 旋轉到w 處,旋轉到\sigma /2 處爲k ,圖中並未標出。

這裏寫圖片描述
(圖片來自參考博客)

  令四元數q=(cos(\sigma/2),sin(\sigma/2)u) ,則
q=(cos(\sigma/2),sin(\sigma/2)u)=(\frac{kv}{{{|v|^2}}},v\times k)=\frac{1}{{{|v|^2}}}(0,-k)(0,v),


  爲了方便,仍舊用四元數k^* 表示(0,-k) ,四元數v 表示(0,v) ,即
q=\frac{1}{{{|v|^2}}}k^*v.

  現在令
w=qvq^*,

  如果能證明wv 的夾角是\sigma ,且v,k,w 在同一個平面上,則說明wv 繞旋轉軸u 旋轉\sigma 得到的,從而命題得證。
  不妨來計算一下wk^* 有:
wk^*=(qvq^*)k^*=qvq^{-1}k^*\\ \ =qv(\frac{1}{{{|v|^2}}}k^*v)^{-1}k^*\\ \ \ =|v|^2qvv^{-1}(k^*)^{-1}k^*\\ \quad \quad \quad \ \ \ \ =|v|^2q=|v|^2\frac{1}{{{|v|^2}}}k^*v=k^*v,

  對於上式wk^*=k^*v ,將其拆分爲四元數的\omega 部分和v 部分進行分析:\omega 部分相等表明w,k 夾角與k,v 的夾角相同,v 部分相等表明w,-k-k,v 所處的平面相同,這就說明wv 繞旋轉軸u 旋轉\sigma 得到的,從而命題得證。
  顯然,我們已經解決了問題3:爲什麼旋轉的公式是qpq^{-1} ?接下來我們嘗試解決問題4:爲什麼是\theta/2
  幸運的是,對於這個問題,我在一篇文章Understanding \ Quaternions 裏面找到了答案。限於篇幅,我就不貼出來了,英文比較好的同學可以看一下,英文比較吃力的同學也可以看一下譯文Understanding\ Quaternions 中文翻譯《理解四元數》
  想要進行一下直觀理解的也可以參看下圖:

這裏寫圖片描述
(圖片來自知乎問題如何形象地理解四元數?Yang\ Eninala 的回答)

  上圖非常直觀地回答了問題4:爲什麼在p'=qpq^{-1} 裏的q=(cos(\sigma/2),sin(\sigma/2)r) 用的是\theta/2 而非\theta
  直觀原因就是q,q^{-1} 做的均是一個\theta/2 的旋轉,且qp,pq^{-1} 不一定是一個純四元數(即第一個分量爲0的四元數,這是三維向量的特殊表示形式)。因此,爲了保證最終結果爲一個純四元數,我們須作兩次旋轉:第一次旋轉結果qp 跳出了原先的三維超平面,第二次旋轉結果pq^{-1} 才又回到了三維超平面的世界。

四元數與四維旋轉 

  說完了三維旋轉,終於可以講講四維旋轉這樣一種人腦很難想象的操作了。實際上,四元數定義的都是四維旋轉,而不是三維旋轉。三維旋轉僅僅是四維旋轉的一種特殊情形,就像二維旋轉是三維旋轉的一種特殊情形一樣。利用四元數進行三維旋轉時,其實是在一個四維空間上的某個三維超平面上進行的。
  一般來說,單獨一個四元數是無法執行四維旋轉的,一個四維旋轉可以唯一地被拆分爲一個左旋轉qL 和一個右旋轉qR ,表達出來就是p=qLpqR ,此處的p,p 已經不僅僅是一個純四元數了。

四元數的可視化 

  終於到最後一個問題了:我們都是生活在三維空間內的生物,該如何想象“生活在”在四維空間內的四元數呢?
  接下來我們僅僅考慮模不超過1的四元數(容易想象一些……),將其構成的4維超球切成三塊,它們都是四維空間的半球在3維空間上的投影(在我們看來就是三維球體了),這個“切”的依據便是四元數q=(w,v),wR,vR3w 的大小了。如下圖所示:


這裏寫圖片描述
模不超過1的四元數構成的4維超球“切割”示意圖

  經過投影切割後,我們可以得到兩個實心球(w>0w<0 )和一個空心球(w=0 )。在4維空間上,左右兩個實心球通過中間的空心球連接在一起。對於任意球體的某個截面而言,四元數“退化”成了一個三維向量,其長度爲|sin(θ/2)|q=(w,v)=(cos(θ/2),sin(θ/2)r,|r|=1 )。
  當截面上的向量長度等於0時,你就會得到一個單位四元數(此時sin(θ/2)=0,cos(θ/2)=1,πθπ )。w>0 所屬實心超球裏的四元數表示ππ 的旋轉,而w<0 所屬實心超球裏的四元數同樣表示ππ 的旋轉,只不過此時的旋轉軸需要被翻轉;w=0 所屬空心超球裏的四元數則代表大小爲π 的旋轉。

四元數的插值 

  前面已經介紹過歐拉角的萬向節鎖問題,正是由於萬向節鎖導致歐拉角無法很好地用於三維旋轉的插值(當出現萬向節鎖現象時無法按照原定旋轉順序得到預期的旋轉,即歐拉角用於三維旋轉的插值時並不平滑)。那麼。這時候就必須要四元數站出來扛大樑了~以下部分參考文章Understanding Quaternions 中文翻譯《理解四元數》以及四元數插值與均值(姿態平滑),限於篇幅,這裏只介紹計算機圖形學裏表示三維旋轉插值常用的SLERP 方案的球面線性插值。
  SLERP 方案可以在兩個朝向之間平滑地進行插值,不妨設第一個朝向爲q1=(ω1,x1,y1,z1) ,第二個朝向爲q2=(ω2,x2,y2,z2) ,它們都是單位四元數,它們的夾角是θ ;被插值前的點爲p ,插值後的點爲p ,插值參數爲t[0,1] ,當t=0p=q1 ,當t=1p=q2
  事實上,球面上的線性插值和一般的線性插值是有一些區別的。一般的線性插值公式爲vt=v1+(v2v1)t,t[0,1] ,我們可以將vt 理解爲起點爲原點,終點在v2v1 上的插值向量。如下圖所示:

 
這裏寫圖片描述
(圖片來自參考文章)

  當t=14,24,34 時,vtv2v1 等分爲四部分,但對應的弦長卻並不相同!說明,當t 均勻變化時,對應的旋轉角速度並不是均勻變化的,因此有必要採用更合理的插值公式。
  不妨設球面線性插值的計算公式爲vt=p(t)v1+q(t)v2,|v1|=|vt|=|v2|=1 ,現在我們要計算出p(t),q(t) 。注意到v1,v2 之間的夾角爲θv1,vt 之間的夾角爲tθvt,v2 之間的夾角爲(1t)θ 。如下圖所示:


這裏寫圖片描述
(圖片來自參考文章)

  將vt=p(t)v1+q(t)v2v1 作內積得:

vtv1=p(t)v1v1+q(t)v2v1,costθ=p(t)+q(t)cosθ,

  再將vt=p(t)v1+q(t)v2v2 作內積得:
vtv2=p(t)v1v2+q(t)v2v2,cos(1t)θ=p(t)cosθ+q(t),

  聯立求解並用三角函數和差公式化簡得:
p(t)=costθcos((1t)θ)cosθ1cos2θ=costθcos2θcostθsinθsintθcosθsin2θ=sin2θcostθsinθsintθcosθsin2θ=sinθcostθsintθcosθsinθ=sin((1t)θ)sinθ,q(t)=cos((1t)θ)costθcosθ1cos2θ=cosθcostθ+sinθsintθcostθcosθsin2θ=sinθsintθsin2θ=sintθsinθ,

  故計算向量的球面線性插值的計算公式如下所示:
vt=sin((1t)θ)sinθv1+sin(tθ)sinθv2.

 
 
(圖片來自參考文章)

  SLERP 方案將上述公式原封不動地套用到了四元數身上,得到:

qt=sin((1t)θ)sinθq1+sin(tθ)sinθq2.

  注意到,在使用這個公式之前還需要計算q1,q2 的夾角θ ,我們可以利用內積公式進行計算得到θ
cosθ=q1q2|q1||q2|=ω1ω2+x1x2+y1y2+z1z2|q1||q2|,θ=arccos(ω1ω2+x1x2+y1y2+z1z2|q1||q2|).

  需要注意的是,當q1,q2 內積爲負數時,直接使用上述計算公式會讓整個插值過程“繞遠路”,如下圖所示:

 
四元數的球面插值示意圖

  可以看到,當q1,q2 內積爲負數時,從q1 “走”到qt 需要繞過半個多球面(你能怎麼辦?相信你也很絕望吧QAQ)。這時候,我們需要對其中一個取反(即四元數四個分量都取其相反數),這裏我們對q1 取反,可以看到,從q1 “走”到qt 要比從q1 “走”到qt 少繞了半個球面!!!
  此外,當sinθ=0 時上述計算公式是無法使用的,因爲分母爲0整個分式是個未定式。此時,我們只能退而求其次採用平面上的線性插值公式了:qt=q1+(q2q1)t,t[0,1].

羣論觀點下的四元數

  本章建議學習過一些抽象代數的同學觀看,沒有學過的就跳過吧……(丫咩蘿,也是花了一些精力在這裏的o(╥﹏╥)o)部分內容參考自文章四元數的運算

  • 四元數除環
  • 羣旋轉
  • 四元數的矩陣表示
  • Q8

四元數除環 

  由除環的定義,四元數所構成的集合是一個除環(如果四元數還滿足交換律它就構成一個域)。
  四元數除環因爲它的不可交換性導致了一個很有趣的結果:四元數的n 階多項式可以擁有數目多於n 的根(即在四元數除環代數基本定理無法成立)。例如方程x2+1=0 就有無數多個解。事實上,只要b2+c2+d2=1,b,c,dR ,那麼x=0+bi+cj+dk 便是一個解。

羣旋轉 

  在“四元數和空間轉動”的維基百科裏提到:

  非零四元數的乘法羣在R3的實部爲零的部分上的共軛作用可以實現轉動。單位四元數(絕對值爲1的四元數)若實部爲cosθ ,它的共軛作用是一個角度爲2θ 的轉動,轉軸爲虛部的方向。四元數的優點是:
- 表達式無奇點(和例如歐拉角之類的表示相比)
- 比矩陣更簡煉(也更快速)
- 單位四元數的對可以表示四維空間中的一個轉動。

  那麼在羣論觀點下,四元數是如何和三維旋轉扯上聯繫的呢?不妨看看四元數除環的四個基1,i,j,k ,它們生成羣U(2) ,而U(2)/U(1)=SU(2)
  又SU(2)/Z2SO(3),SU(2)S3 ,其中,U(n)n 級酉羣(即n×n 階酉矩陣組成的羣),SU(2) 是2級特殊酉羣,SO(3) 是3級特殊正交羣(又稱爲三維旋轉羣),S3 是三維單位球面。這說明單位四元數通過羣同構能夠與三維旋轉產生極強的聯繫,且單位四元數從抽象意義上看是分佈在一個三維單位球面上的。
  同時,S3SO(3) 之間存在着一個2對1的同態滿射,這說明S3SO(3) 的雙重覆蓋,說人話就是每兩個四元數才能對應一個三維旋轉,這樣大家也許能對四元數旋轉公式p=qpq 有一個更深的理解吧……

四元數的矩陣表示 

   因爲單位四元數構成的三維單位球面S3 羣同構於2級特殊酉羣SU(2) ,因此我們也可以通過研究SU(2) 去刻畫S3 的性質。給出S3SU(2) 的同構映射:

S3SU(2):a+bi+cj+dk(abib+cib+cia+di).

  而SU(2) 的四個基爲
I=(1001),X=(i0  0i),Y=(0  11 1),Z=(0ii1),

  故
   X2=Y2=Z2=XYZ=1,(abib+cib+cia+di)=aI+bX+cY+dZ.

  此時SU(2) 表現出類似四元數的性質,這正是羣同構的奇妙之處!!!
  同理,我們還可以將單位四元數構成的三維單位球面S3 映射到一類特殊的四階實數矩陣上(我暫且把這類特殊的四階實數矩陣稱之爲M(4) ,我暫時還沒驗證是否是一個羣……有點懶……),映射如下所示:
S3M(4):a+bi+cj+dk  ab   dc  bacdd   c   a b  cd   ba.

  再令
I=1000010000100001,X=  0001  00 1  0  01001   000,Y=  0010  00011   000  0 1  00,Z=01 001  0  000  0  010  0  1   0,

  則
   X2=Y2=Z2=XYZ=1M=aI+bX+cY+dZ,MM(4).

  此時(4) 同樣表現出類似四元數的性質!!!
  事實上,上述兩種矩陣表示都可以認爲是四元數另外的存在形式。
  既然可以用2級特殊酉羣SU(2) 來研究單位四元數構成的三維單位球面S3 羣的結構,那麼我們是否又可以用單位四元數構成的三維單位球面S3 羣去研究三維旋轉羣SO(3) 的結構呢?相信這纔是四元數誕生的真正意義。答案當然是肯定的,在羣表示論裏面常常用不可約表示去研究一個羣的結構,由於涉及到太多專業名詞,限於篇幅,這裏僅僅介紹一個關於如何去研究三維旋轉羣SO(3) 結構的定理:

  設Vn 是兩個不定元xyn 次齊次多項式組成的複線性空間。考慮拓撲羣SL(2,C) 到拓撲羣GL(Vn) 的一個映射ϕn

ϕn:SL(2,C)GL(Vn),Aϕn(A),

  其中
(ϕn(A)f)(x,y)=f((x,y)A)=f(a11x+a21y,a12x+a22y),

  這裏fVn,A=(aij) ,則SU(2) 的上述不可約復表示ϕn(n=0,1,2,)SU(2) 的全部不等價的有限不可約復表示。從而SO(3) 的上述不可約復表示ϕ2m(m=0,1,2,)SO(3) 的全部不等價的有限不可約復表示。

Q8 

  因爲單位四元數構成的三維單位球面S3 羣同構於2級特殊酉羣SU(2) (熟悉的開場白……),我們不妨只取SU(2) 的部分元素來研究,從而能夠解釋S3 羣的部分結構。只取以下八個元素:

E=(1001),E,I=(  0110),I,J=(i00i),J,K=(0ii0),K.

  容易驗證,它構成一個羣,我們稱之爲Q8 。其中,E 是單位元,E 是二階元,I,I,J,J,K,K 是四階元。
  若將Q8 看成一個有序集,顯然,Q8 依次左乘其每一個元素會產生一個置換。比方說,Q8 左乘I :
(E,E,I,I,J,J,K,K)(I,I,E,E,K,K,J,J),

  這對應於一個置換(1324)(5768)。這是很符合凱萊定理的:任何一個羣都同一個變換羣同構,這意味着我們能通過研究常見的置換羣去“解剖”抽象的Q8 的結構。
  此外,Q8 還具有一個很棒的性質:Q8 的每一個子羣都是正規子羣。我們可以簡單證明一下。
證明:對於Q8 的任意子羣H ,由拉格朗日定理,|H|||Q8|=8 ,故|H|=1,2,4,8
  當|H|=1,8 時,HQ8 的平凡子羣,顯然是正規子羣;
  當|H|=2 時,H={E,E} ,易驗證,此時H 是一個正規子羣;
  當|H|=4 時,任取qQ8 ,若qH ,此時qH=Hq ;若qH ,則Q8=HHq=qHH ,從而qH=Hq
  綜上所述,Q8 的每一個子羣都是正規子羣,命題得證。
  因爲Q8 這個很棒的性質,要從Q8 中構造一個商羣就容易得多了,而這個操作在數學上是很常用的。可以說,很多走代數方向的研究生,掌握Q8 的各種性質幾乎已經成爲一個“能力標配”!!!

四元數在Unity 中的應用

  很多時候,我們不僅要仰望星空,還需要腳踏實地。四元數在Unity 這個遊戲引擎當中是很常用的,主要也是用於剛體的三維旋轉。
  Unity 提供了Quaternion.AngleAxis 這一個方法來創建旋轉,該方法的聲明如下所示:

static function AngleAxis (angle : float, axis : Vector3) : Quaternion 
//繞axis軸旋轉angle,創建一個旋轉,即返回一個四元數變換。

  這是繼承自Quaternion 類的一個方法。當我們想對某一個三維向量進行旋轉時,我們僅需要採用Quaternion.AngleAxis 返回的旋轉變換左乘該三維向量即可,不需要再進行右乘的操作(方便多了有木有⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄,就相當於前面所說的p=qpq 的形式)。代碼如下所示:

//將待旋轉向量繞y軸旋轉30度
Vector3 newVector = Quaternion.AngleAxis(30, Vector3.up) * oldVector; 

  在Unity 中四元數的插值也是很方便的,它提供了Quaternion.Lerp 這個方法進行插值,該方法的聲明如下所示:

static function Lerp (from : Quaternion, to : Quaternion, t : float) : Quaternion 
//通過t值from向to之間插值,並且規範化結果。
//這個比Slerp更快但是如果旋轉較遠看起來就比較差。

  這同樣是繼承自Quaternion 類的一個方法。在實際使用時,若你想實現一個物體在某兩個朝向之間轉動的效果,我們可以將它放到Update() 方法裏來實現,該方法會被每幀調用一次,代碼如下所示:

using UnityEngine;
using System.Collections;

public class example : MonoBehaviour {
    public Transform from;
    public Transform to;
    public float speed = 0.1F;
    void Update() {
        transform.rotation = Quaternion.Lerp(from.rotation, to.rotation, Time.time * speed);
    }
}

  Quaternion 類還提供了許多有用的方法,限於篇幅,大家可以閱讀UnityAPI 文檔深入瞭解。此外,網上也有很多實現了四元數的開源庫,大家可以去了解一下。就我個人而言,我認爲GLM 這個開源庫做得還是不錯的(沒試過其它的……囧)。
  呼呼呼……終於完成這篇“吐血之作”了,希望能對大家有幫助(也不知道有木有人看2333)。這篇寫得有點長,開學以後就沒那麼多時間寫了,以後的篇幅當然會短得多,而且更新的頻率可能不高……但能肯定的是以後的每一篇博文都會用心寫。下一篇博文的主題預計會是今年GDC 上提到的隨機數字生成(RNG ),可以小小期待一波(完結撒花(^▽^))!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章