Understanding Quaternions
轉載自:http://www.qiujiawei.com/understanding-quaternions/
正文
在這篇文章中我會嘗試用簡單的方式去解釋四元數的概念,即用可視化的方式解釋四元數以及幾種對四元數的操作。我將把矩陣、歐拉角和四元數放在一起比較,並解釋什么時候該用四元數、什么時候該用歐拉角或矩陣。
在計算機圖形學中,我們使用轉換矩陣來表示空間中的一個位置以及朝向。一個轉換矩陣還可以表示對一個目標的縮放(scale)或錯切(shear)等。 我們可以把轉換矩陣想象成一個空間,當你用這個矩陣乘以向量、點(甚至矩陣)后, 你就把向量、點、矩陣轉換進這個空間了。
在這篇文章中,我不會討論轉換矩陣的細節。你可以查看我前面的文章,文章中描述了轉換矩陣的細節。
在這篇文章中,我想要討論一個可替代的方案,即用四元數來描述空間里的物體的朝向。
四元數的概念是由愛爾蘭數學家Sir William Rowan Hamilton發明的(1843年,都柏林)。Hamilton當時正和他的妻子前往愛爾蘭皇家研究院,當他從Brougham橋通過皇家運河時,他領悟到了一個激動人心的東西,並立刻把它刻在橋的一個石頭上:

William Rowan Hamilton Plaque on Broome Bridge on the Royal Canal commemorating his discovery of the fundamental formula for quaternion multiplication.
在我們能夠完全理解四元數之前,我們必須先知道四元數是怎么來的。四元數的根源其實是復數。
除了知名的數集(自然數、整數、實數、分數)之外,復數系統引入了一個新的數集——虛數。虛數的發明是為了解決一些特定的無解的方程,例如:

一般而言,數學家是不能忍受一個等式是無解的。於是,一個新的術語被發明了,它就是虛數,一個可以解決上面這個等式的數。
虛數有這樣的形式:
不要為這個術語較真,因為邏輯上這個數是不存在的。只要知道i是一個平方等於-1的東西即可。
虛數的集合可以用
來表示
復數的集合
是一個實數和一個虛數的和,形式如下:
可以認為所有實數都是b=0的復數、所有虛數都是a=0的復數。
加法:
減法:
復數的共軛就是指把復數的虛數部分變成負的。共軛復數的符號是
。
復數和它的共軛復數的乘積是:
我們使用共軛復數來計算復數的絕對值:
如果
的平方等於-1,那么
的n次冪也應該存在:
如果按照這個順序寫下去,會出現這樣一個模式: (1,\mathbf i,-1,-\mathbf i,1,...)
一個類似的模式也出現在遞增的負數冪:
你可能已經在數學里頭見過類似的模式,但是是以(x,y,-x,-y,x,...)的形式,這是在2D笛卡爾平面對一個點逆時針旋轉90度時生成的;(x,-y,-x,y,x,...)則是在2D笛卡爾平面對一個點順時針旋轉90度時生成的。

我們也能夠把復數映射到一個2D網格平面——復數平面,只需要把實數映射到橫軸、虛數映射到縱軸。

如前面的序列所示,我們可以認為,對一個復數乘以i,這個復數就在復數平面上旋轉了90度。
讓我們看看這是不是真的。我們隨機地在復數平面上取一個點:

t剛好是開始的p。如果我們把這些復數放到復數平面上,就得到下面的圖:

我們也可以按順時針方向旋轉,只需要把上面的乘數i改成-i。
我們也可以在復數平面上進行任意角度的旋轉,只需要定義下面這個復數:
這也是一個在復數平面繞原點逆時針旋轉任意點的方法。(譯注:這句話應該是在說旋轉矩陣)

補充:


(\mathbf i \mathbf j, \mathbf j \mathbf k, \mathbf k \mathbf i這幾個性質的可視化)
上圖展示了如何用i、j、k作為笛卡爾坐標系的單位向量。


再把這個表達式擴展成多個有序對的和:
如果把后3個四元數相加,並提取公共部分,就可以把等式改寫成:
這個等式是2個有序對的和。第1個有序對是一個實四元數,第2個是一個純四元數。這兩個四元數也可以合並成一個:
如果把下面的表達式代入上面的等式:
(譯注:注意,第三條和第四條並不是四元數的點積和叉積,而是向量的點積和叉積)
我們就得到了:
這就是四元數乘積的一般式。



我們可以把四元數寫成實四元數和純四元數的和:
給定任意的向量v,我們可以把這個向量寫成一個系數和一個單位方向向量的乘積:
將這個定義和純四元數的定義結合,就得到了:

我們現在可以把單位四元數的定義和四元數的加法形式結合到一起,就創造了一種新的四元數的表示法,這種表示法和復數的表示法形似:
這就給了我們一種和復數非常相似的四元數表示法:




和向量的點積相似,我們也可以計算2個四元數的點積,只需要將各個對應的系數相乘,然后相加:
前面我們定義了一個特殊的復數:旋轉數。它是用來旋轉2D復數平面的點的:
根據四元數和復數的相似性,應該有可能設計一個可以旋轉3D空間的點的四元數:
讓我們測試一下這個理論是否可靠,方法就是計算四元數q和向量p的積。第一步,我們把p寫成純四元數的形式:
以及單位四元數q:
我們可以看到結果是一個同時有系數、有虛向量的四元數。

好了,現在計算下qp:
這正是我們所期望的!
我們可以用圖像展示旋轉過程:

現在,讓我們考慮更一般化的四元數,即和p不正交的四元數。現在讓我們把p的向量部分偏移45度:
我們可以用圖像展示旋轉過程:


然而,還有一線生機。Hamilton發現(但沒有正式宣布),如果對qp右乘q的逆,出來的結果是一個純四元數,並且四元數向量部分的范數可以保持不變。讓我們試試應用在我們的例子里。

現在,把前面算出來的qp再次拿出來:
下面的圖像展示了旋轉結果:

所以我們可以看到,這個結果是一個純四元數,並且原四元數的向量的范數也保持住了。但是還有一個問題:向量被旋轉了90度而不是45度。這剛好是我們需要的度數的兩倍!為了正確地讓一個向量繞某個軸向量旋轉某個角度,我們必須以目標角度的一半來計算。因此,我們構造了下面的四元數:
這就是旋轉四元數的一般形式!
在計算機圖形學中使用四元數,其中一個重要原因是四元數非常適合用來表示空間中的旋轉。四元數解決了其他3維空間旋轉算法會遇到的惱人的問題,比如使用歐拉角來表示旋轉操作時會遇到的萬向節鎖問題(Gimbal lock)。
使用四元數,我們可以定義好幾種方案來表示3維空間的轉動插值。第一種是SLERP,它被用來把一個點(物體)從一個朝向平滑地插值到另一個朝向。第二個是SLERP的擴展版本,被稱為SQAD,它被用來處理用一系列朝向定義得到的一條路徑的插值。


接下來的目標是干掉上面四元數的差的分數部分,方法是計算四元數的t次冪(就是上面的那個插值參數t,區間是[0,1])。
四元數的冪運算的一般化公式是:
(譯注:上述的2次公式推導,其實省略了很多證明過程。具體可以參考:http://bpeers.com/blog/?itemid=861,http://bpeers.com/blog/?itemid=863,http://bpeers.com/blog/?itemid=866, http://bpeers.com/blog/?itemid=1001 )
對於t = 0,我們有:
而對於t = 1,有:
對於角旋轉的插值計算,我們利用q1和q2的角度分數差來調整原始朝向q1:
這也就是使用四元數的球面線性插值的一般形式。然而,這不是slerp函數的常用形式。
我們可以應用類似的用於計算向量的球面插值公式,到四元數里。計算向量的球面插值的一般形式定義如下:
用圖像表示如下:

這個公式可以原封不動地應用到四元數:
這個方案有2個問題,必須在實現過程中加以考慮。
第一,如果四元數點積的結果是負值,那么后面的插值就會在4D球面上繞遠路,這並不是我們想要的。為了解決這個問題,我們測試點積的結果,當結果是負值時,我們將2個四元數的其中一個取反,取反它的系數和向量部分,並不會改變它代表的朝向。而經過這一步操作,可以保證這個旋轉走的是最短路徑。
當q1q1和q2q2的角度差非常小,小到導致sinθ=0sinθ=0 時,會出現第二個問題。如果這個情況出現了,當我們除以sinθsinθ時就會得到一個未定義的結果。在這個情況下,我們可以回退去使用q1q1和q2q2的線性插值。

除了特別難理解之外,相比矩陣或歐拉角,四元數在表示旋轉這個事情上,擁有一些明顯的優點。
-
SLERP和SQUAD,提供了一種使得在朝向之間可以平滑過渡的方法。
-
使用四元數來串聯"旋轉",要比使用矩陣快得多。
-
對於單位四元數,逆向旋轉可以通過對向量部分取反來實現。而計算一個矩陣的逆矩陣是被認為比較慢的,如果這個矩陣未被標准正交化的話(標准正交矩陣的逆矩陣是它的轉置矩陣)。
-
從四元數轉換到矩陣,要比從歐拉角轉換到矩陣快一點。
-
四元數只需要4個數字(如果旋轉四元數已經單位化了那么只需要3個,實數部分可以在運行時計算)來表示一個旋轉,而矩陣需要至少9個數字。
盡管使用四元數有這么多優點,還是有缺點存在的。
-
因為浮點數的舍入運算錯誤,四元數可能會變無效。不過,這個錯誤可以通過重新單位化四元數來避免。
-
使用四元數最具威懾性的地方,還是四元數的理解難度大。我希望這個問題可以通過閱讀本文來解決。
存在一些已經實現了四元數、並且是正確的的數學程序庫。在我的個人經驗里,我發現GLM(OpenGL Math Library)是一個優秀的數學庫,它的四元數的實現極其不錯。如果你對在你的程序中使用四元數感興趣,那么我會推薦你使用這個數學庫。
我實現了一個小demo來演示一個四元數如何被用來旋轉一個3維物體。這個demo是用Unity3.5.2實現的,你可以免費下載它和閱讀它的腳本。zip文件也包含了一個Windows版的Unity程序。當然你可以自己構建一個Mac的版本。
