天天看點

Understanding Quaternions 中文翻譯《了解四元數》Understanding Quaternions 中文翻譯《了解四元數》

Understanding Quaternions 中文翻譯《了解四元數》

Tags: math, quaternion

原文位址:http://www.3dgep.com/understanding-quaternions/

正文

在這篇文章中我會嘗試用簡單的方式去解釋四元數的概念,即用可視化的方式解釋四元數以及幾種對四元數的操作。我将把矩陣、歐拉角和四元數放在一起比較,并解釋什麼時候該用四元數、什麼時候該用歐拉角或矩陣。

内容結構

  • 介紹
  • 複數
    • 複數的加減
    • 複數的系數縮放
    • 複數的積
    • 複數的平方
    • 共轭複數
    • 複數的絕對值
    • 兩複數的商
  • i的幂
  • 複數平面
    • 旋轉數(Rotors)
  • 四元數
    • 作為有序數的四元數
    • 四元數的加減
    • 四元數的積
    • 實四元數
    • 四元數的系數縮放
    • 純四元數
    • 四元數的加法形式
    • 機關四元數
    • 四元數的二進制形式
    • 共轭四元數
    • 四元數範數
    • 四元數規範化
    • 四元數的逆
    • 四元數的點積
  • 旋轉
  • 四元數插值
    • SLERP
      • 四元數的差
      • 四元數的幂運算
      • 2個四元數的分數差
      • 注意事項
    • SQUARD
  • 總結
  • 下載下傳Demo

介紹

在計算機圖形學中,我們使用轉換矩陣來表示空間中的一個位置以及朝向。一個轉換矩陣還可以表示對一個目标的縮放(scale)或錯切(shear)等。 我們可以把轉換矩陣想象成一個空間,當你用這個矩陣乘以向量、點(甚至矩陣)後, 你就把向量、點、矩陣轉換進這個空間了。

在這篇文章中,我不會讨論轉換矩陣的細節。你可以檢視我前面的文章,文章中描述了轉換矩陣的細節。

在這篇文章中,我想要讨論一個可替代的方案,即用四元數來描述空間裡的物體的朝向。

四元數的概念是由愛爾蘭數學家Sir William Rowan Hamilton發明的(1843年,都柏林)。Hamilton當時正和他的妻子前往愛爾蘭皇家研究院,當他從Brougham橋通過皇家運河時,他領悟到了一個激動人心的東西,并立刻把它刻在橋的一個石頭上:

i2=j2=k2=ijk=−1i2=j2=k2=ijk=−1

Understanding Quaternions 中文翻譯《了解四元數》Understanding Quaternions 中文翻譯《了解四元數》

William Rowan Hamilton Plaque on Broome Bridge on the Royal Canal commemorating his discovery of the fundamental formula for quaternion multiplication.

複數

在我們能夠完全了解四元數之前,我們必須先知道四元數是怎麼來的。四元數的根源其實是複數。

除了知名的數集(自然數、整數、實數、分數)之外,複數系統引入了一個新的數集——虛數。虛數的發明是為了解決一些特定的無解的方程,例如:x2+1=0x2+1=0要解決這個等式,必須讓x2=−1x2=−1,這當然是不行的,因為任意實數的平方都是非負數。

一般而言,數學家是不能忍受一個等式是無解的。于是,一個新的術語被發明了,它就是虛數,一個可以解決上面這個等式的數。

虛數有這樣的形式:

i2=−1i2=−1

不要為這個術語較真,因為邏輯上這個數是不存在的。隻要知道i是一個平方等于-1的東西即可。

虛數的集合可以用II來表示。

複數的集合CC是一個實數和一個虛數的和,形式如下:

z=a+bi a,b∈R, i2=−1z=a+bi a,b∈R, i2=−1

可以認為所有實數都是b=0的複數、所有虛數都是a=0的複數。

複數的加減

加法:

(a1+b1i)+(a2+b2i)=(a1+a2)+(b1+b2)i(a1+b1i)+(a2+b2i)=(a1+a2)+(b1+b2)i

減法:

(a1+b1i)−(a2+b2i)=(a1−a2)+(b1−b2)i(a1+b1i)−(a2+b2i)=(a1−a2)+(b1−b2)i

複數的系數縮放

λ(a1+b1i)=λa1+λb1iλ(a1+b1i)=λa1+λb1i

複數的積

z1=(a1+b1i)z1=(a1+b1i)z2=(a2+b2i)z2=(a2+b2i)z1z2=(a1+b1i)(a2+b2i)=a1a2+a1b2i+b1a2i+b1b2i2z1z2=(a1+b1i)(a2+b2i)=a1a2+a1b2i+b1a2i+b1b2i2z1z2=(a1a2−b1b2)+(a1b2+b1a2)iz1z2=(a1a2−b1b2)+(a1b2+b1a2)i

複數的平方

z=(a+bi)z=(a+bi)z2=(a+bi)(a+bi)z2=(a+bi)(a+bi)z2=(a2−b2)+2abiz2=(a2−b2)+2abi

共轭複數

複數的共轭就是指把複數的虛數部分變成負的。共轭複數的符号是¯zz¯或z∗z∗。

z=(a+bi)z=(a+bi)

z∗=(a−bi)z∗=(a−bi)

複數和它的共轭複數的乘積是:

zz∗=(a+bi)(a−bi)=a2−abi+abi+b2=a2+b2zz∗=(a+bi)(a−bi)=a2−abi+abi+b2=a2+b2

複數的絕對值

我們使用共轭複數來計算複數的絕對值:

z=(a+bi)z=(a+bi)

|z|=√zz∗=√(a+bi)(a−bi)=√a2+b2|z|=zz∗=(a+bi)(a−bi)=a2+b2

兩複數的商

z1=(a1+b1i)z1=(a1+b1i)z2=(a2+b2i)z2=(a2+b2i)z1z2=a1+b1ia2+b2i=(a1+b1i)(a2−b2i)(a2+b2i)(a2−b2i)z1z2=a1+b1ia2+b2i=(a1+b1i)(a2−b2i)(a2+b2i)(a2−b2i)=a1a2−a1b2i+b1a2i−b1b2i2a22+b22=a1a2−a1b2i+b1a2i−b1b2i2a22+b22

=a1a2+b1b2a22+b22+b1a2−a1b2a22+b22i=a1a2+b1b2a22+b22+b1a2−a1b2a22+b22i

i的幂

如果ii的平方等于-1,那麼ii的n次幂也應該存在:

i0=1i0=1i1=ii1=ii2=−1i2=−1i3=ii2=−ii3=ii2=−ii4=i2i2=1i4=i2i2=1i5=ii4=ii5=ii4=ii6=ii5=i2=−1i6=ii5=i2=−1

如果按照這個順序寫下去,會出現這樣一個模式: (1,\mathbf i,-1,-\mathbf i,1,...)

一個類似的模式也出現在遞增的負數幂:

i0=1i0=1i−1=−ii−1=−ii−2=−1i−2=−1i−3=ii−3=ii−4=1i−4=1i−5=−ii−5=−ii−6=−1i−6=−1

你可能已經在數學裡頭見過類似的模式,但是是以(x,y,-x,-y,x,...)的形式,這是在2D笛卡爾平面對一個點逆時針旋轉90度時生成的;(x,-y,-x,y,x,...)則是在2D笛卡爾平面對一個點順時針旋轉90度時生成的。

Understanding Quaternions 中文翻譯《了解四元數》Understanding Quaternions 中文翻譯《了解四元數》

複數平面

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

Understanding Quaternions 中文翻譯《了解四元數》Understanding Quaternions 中文翻譯《了解四元數》

如前面的序列所示,我們可以認為,對一個複數乘以i,這個複數就在複數平面上旋轉了90度。

讓我們看看這是不是真的。我們随機地在複數平面上取一個點:

p=2+ip=2+i

p乘以i後得到q:q=pi=(2+i)i=2i+i2=−1+2iq=pi=(2+i)i=2i+i2=−1+2i

q乘以i後得到r:r=qi=(−1+2i)i=−i+2i2=−2−ir=qi=(−1+2i)i=−i+2i2=−2−i

r乘以i後得到s:s=ri=(−2−i)i=−2i−i2=1−2is=ri=(−2−i)i=−2i−i2=1−2i

s乘以i後得到t:t=si=(1−2i)i=i−2i2=2+it=si=(1−2i)i=i−2i2=2+i

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

Understanding Quaternions 中文翻譯《了解四元數》Understanding Quaternions 中文翻譯《了解四元數》

我們也可以按順時針方向旋轉,隻需要把上面的乘數i改成-i。

旋轉數(Rotors)

我們也可以在複數平面上進行任意角度的旋轉,隻需要定義下面這個複數:q=cosθ+isinθq=cosθ+isinθ

任意的複數乘以q:

p=a+bip=a+biq=cosθ+isinθq=cosθ+isinθpq=(a+bi)(cosθ+isinθ)pq=(a+bi)(cosθ+isinθ)a′+b′i=acosθ−bsinθ+(asinθ+bcosθ)ia′+b′i=acosθ−bsinθ+(asinθ+bcosθ)i

也可以寫成矩陣的形式:

[a′−b′b′a′]=[cosθ−sinθsinθcosθ][a−bba][a′−b′b′a′]=[cosθ−sinθsinθcosθ][a−bba]

這也是一個在複數平面繞原點逆時針旋轉任意點的方法。(譯注:這句話應該是在說旋轉矩陣)

四元數

了解了複數系統和複數平面後,我們可以額外增加2個虛數到我們的複數系統,進而把這些概念拓展到3維空間。

四元數的一般形式:

q=s+xi+yj+zk   s,x,y,z∈Rq=s+xi+yj+zk   s,x,y,z∈R

上面的公式是根據Hamilton的著名的表達式得到的:

i2=j2=k2=ijk=−1i2=j2=k2=ijk=−1

以及:

ij=k   jk=i   ki=jij=k   jk=i   ki=jji=−k   kj=−i   ik=−jji=−k   kj=−i   ik=−j

你可能已經注意到了,i、j、k之間的關系非常像笛卡爾坐标系下機關向量的叉積規則:

x×y=z   y×z=x   z×x=yx×y=z   y×z=x   z×x=yy×x=−z   z×y=−x   x×z=−yy×x=−z   z×y=−x   x×z=−y

Hamilton自己也發現i、j、k虛數可以被用來表達3個笛卡爾坐标系機關向量i、j、k,并且仍然保持有虛數的性質,也即i2=j2=k2=−1i2=j2=k2=−1。

Understanding Quaternions 中文翻譯《了解四元數》Understanding Quaternions 中文翻譯《了解四元數》

(\mathbf i \mathbf j, \mathbf j \mathbf k, \mathbf k \mathbf i這幾個性質的可視化)

上圖展示了如何用i、j、k作為笛卡爾坐标系的機關向量。

作為有序數的四元數

我們可以用有序對的形式,來表示四元數:[s,v]   s∈R,v∈R3[s,v]   s∈R,v∈R3

其中的v,也可以用它各自獨立的3個分量表示:

q=[s,xi+yj+zk]   s,x,y,z∈Rq=[s,xi+yj+zk]   s,x,y,z∈R

使用這種表示法,我們可以更容易地展示四元數和複數之間的相似性。

四元數的加減

和複數類似,四元數也可以被加減:

qa=[sa,a]qa=[sa,a]qb=[sb,b]qb=[sb,b]qa+qb=[sa+sb,a+b]qa+qb=[sa+sb,a+b]qa−qb=[sa−sb,a−b]qa−qb=[sa−sb,a−b]

四元數的積

我們也可以表示四元數的乘積:

qaqb=[sa,a][sb,b]qaqb=[sa,a][sb,b]=(sa+xai+yaj+zak)(sb+xbi+ybj+zbk)=(sa+xai+yaj+zak)(sb+xbi+ybj+zbk)=(sasb−xaxb−yayb−zazb)=(sasb−xaxb−yayb−zazb)+(saxb+sbxa+yazb−ybza)i+(saxb+sbxa+yazb−ybza)i+(sayb+sbya+zaxb−zbxa)j+(sayb+sbya+zaxb−zbxa)j+(sazb+sbza+xayb−xbya)k+(sazb+sbza+xayb−xbya)k

可以看到,四元數的乘積依然還是一個四元數。如果我們把虛數i、j、ki、j、k替換成有序對:

i=[0,i]   j=[0,j]   k=[0,k]i=[0,i]   j=[0,j]   k=[0,k]

以及還有[1,0] = 1,将它們代入前面的表達式,就得到了:

qaqb=(sasb−xaxb−yayb−zazb)[1,0]qaqb=(sasb−xaxb−yayb−zazb)[1,0]+(saxb+sbxa+yazb−ybza)[0,i]+(saxb+sbxa+yazb−ybza)[0,i]+(sayb+sbya+zaxb−zbxa)[0,j]+(sayb+sbya+zaxb−zbxa)[0,j]+(sazb+sbza+xayb−xbya)[0,k]+(sazb+sbza+xayb−xbya)[0,k]

再把這個表達式擴充成多個有序對的和:

qaqb=[(sasb−xaxb−yayb−zazb),0]qaqb=[(sasb−xaxb−yayb−zazb),0]+[0,(saxb+sbxa+yazb−ybza)i]+[0,(saxb+sbxa+yazb−ybza)i]+[0,(sayb+sbya+zaxb−zbxa)j]+[0,(sayb+sbya+zaxb−zbxa)j]+[0,(sazb+sbza+xayb−xbya)k]+[0,(sazb+sbza+xayb−xbya)k]

如果把後3個四元數相加,并提取公共部分,就可以把等式改寫成:

qaqb=[(sasb−xaxb−yayb−zazb),0]qaqb=[(sasb−xaxb−yayb−zazb),0]+[0,sa(xbi+ybj+zbk)+sb(xai+yaj+zak)+[0,sa(xbi+ybj+zbk)+sb(xai+yaj+zak)+(yazb−ybza)i+(zaxb−zbxa)j+(xayb−xbya)k]+(yazb−ybza)i+(zaxb−zbxa)j+(xayb−xbya)k]

這個等式是2個有序對的和。第1個有序對是一個實四元數,第2個是一個純四元數。這兩個四元數也可以合并成一個:

qaqb=[(sasb−xaxb−yayb−zazb),qaqb=[(sasb−xaxb−yayb−zazb),sa(xbi+ybj+zbk)+sb(xai+yaj+zak)sa(xbi+ybj+zbk)+sb(xai+yaj+zak)+(yazb−ybza)i+(zaxb−zbxa)j+(xayb−xbya)k]+(yazb−ybza)i+(zaxb−zbxa)j+(xayb−xbya)k]

如果把下面的表達式代入上面的等式:

a=xai+yaj+zaka=xai+yaj+zakb=xbi+ybj+zbkb=xbi+ybj+zbka⋅b=xaxb+yayb+zazba⋅b=xaxb+yayb+zazba×b=(yazb−ybza)i+(zaxb−zbxa)j+(xayb−xbya)ka×b=(yazb−ybza)i+(zaxb−zbxa)j+(xayb−xbya)k

(譯注:注意,第三條和第四條并不是四元數的點積和叉積,而是向量的點積和叉積)

我們就得到了:

qaqb=[sasb−a⋅b,sab+sba+a×b]qaqb=[sasb−a⋅b,sab+sba+a×b]

這就是四元數乘積的一般式。

實四元數

一個實四元數是一個虛部向量為零向量的四元數:

q=[s,0]q=[s,0]

兩個實四元數的乘積是另一個實四元數:

qa=[sa,0]qa=[sa,0]qb=[sb,0]qb=[sb,0]qaqb=[sa,0][sb,0]=[sasb,0]qaqb=[sa,0][sb,0]=[sasb,0]

這和2個虛部為0的複數的乘積幾乎一樣:

z1=a1+0iz1=a1+0iz2=a2+0iz2=a2+0iz1z2=(a1+0i)(a2+0i)=a1a2z1z2=(a1+0i)(a2+0i)=a1a2

四元數的系數縮放

我們也可以用一個系數(實數)去乘四元數:

q=[s,v]q=[s,v]λq=λ[s,v]=[λs,λv]λq=λ[s,v]=[λs,λv]

我們可以用實四元數與普通四元數的乘積,來确認這個等式是否正确:

q=[s,v]q=[s,v]λ=[λ,0]λ=[λ,0]λq=[λ,0][s,v]=[λs,λv]λq=[λ,0][s,v]=[λs,λv]

純四元數

和實四元數相似,Hamilton也定義了純四元數。純四元數是s=0的四元數:

q=[0,v]q=[0,v]

也可以寫成下面的形式:

q=xi+yk+zkq=xi+yk+zk

然後是2個純四元數的乘積:

qa=[0,a]qa=[0,a]qb=[0,b]qb=[0,b]qaqb=[0,a][0,b]=[−a⋅b,a×b]qaqb=[0,a][0,b]=[−a⋅b,a×b]

四元數的加法形式

我們可以把四元數寫成實四元數和純四元數的和:

q=[s,v]q=[s,v]=[s,0]+[0,v]=[s,0]+[0,v]

機關四元數

給定任意的向量v,我們可以把這個向量寫成一個系數和一個機關方向向量的乘積:

v=v^v  v=|v|,|^v|=1v=vv^  v=|v|,|v^|=1

将這個定義和純四元數的定義結合,就得到了:

q=[0,v]q=[0,v]=[0,v^v]=[0,vv^]=v[0,^v]=v[0,v^]

然後,我們可以定義機關四元數了,它是一個s=0、vv為機關向量的四元數:

^q=[0,^v]q^=[0,v^]

四元數的二進制形式

我們現在可以把機關四元數的定義和四元數的加法形式結合到一起,就創造了一種新的四元數的表示法,這種表示法和複數的表示法形似:

q=[s,v]q=[s,v]=[s,0]+[0,v]=[s,0]+[0,v]=[s,0]+v[0,^v]=[s,0]+v[0,v^]=s+v^q=s+vq^

這就給了我們一種和複數非常相似的四元數表示法:

z=a+biz=a+biq=s+v^qq=s+vq^

共轭四元數

共轭四元數的計算,就是将四元數的虛向量取反:

q=[s,v]q=[s,v]q∗=[s,−v]q∗=[s,−v]

四元數和它的共轭四元數的乘積:

qq∗=[s,v][s,−v]qq∗=[s,v][s,−v]=[s2−v⋅(−v),−sv+sv+v×(−v)]=[s2−v⋅(−v),−sv+sv+v×(−v)]=[s2+v⋅v,0]=[s2+v⋅v,0]=[s2+v2,0]=[s2+v2,0]

四元數範數

回憶下複數範數的定義:

|z|=√a2+b2|z|=a2+b2zz∗=|z|2zz∗=|z|2

類似的,四元數的範數可以這樣定義:

q=[s,v]q=[s,v]|q|=√s2+v2|q|=s2+v2

這也讓我們可以這樣表達四元數範數:

qq∗=|q|2qq∗=|q|2

四元數規範化

利用四元數範數的定義,就可以對四元數進行規範化。要讓一個四元數規範化,隻需要讓這個四元數去除以它的範數:

q′=q√s2+v2q′=qs2+v2

舉一個例子,讓我們規範化下面這個四元數:

q=[1,4i+4j−4k]q=[1,4i+4j−4k]

第一步,先計算q的範數:

|q|=√12+42+42+(−4)2|q|=12+42+42+(−4)2=√49=7=49=7

然後,q除以|q|:

q′=q|q|q′=q|q|=(1+4i+4j−4k)7=(1+4i+4j−4k)7=17+47i+47j−47k=17+47i+47j−47k

四元數的逆

四元數的逆用q−1q−1表示。要計算四元數的逆,需要用四元數的共轭四元數去除以四元數的範數的平方:

q−1=q∗|q|2q−1=q∗|q|2

為了證明這個式子,我們先根據逆的定義,有:

qq−1=[1,0]=1qq−1=[1,0]=1

兩邊都左乘共轭四元數 q∗q∗ :

q∗qq−1=q∗q∗qq−1=q∗

将上文中的qq∗=|q|2qq∗=|q|2代入這個式子,得到:

|q|2q−1=q∗|q|2q−1=q∗

q−1=q∗|q|2q−1=q∗|q|2

對于機關四元數,它的範數是1,是以可以寫成:

q−1=q∗q−1=q∗

四元數的點積

和向量的點積相似,我們也可以計算2個四元數的點積,隻需要将各個對應的系數相乘,然後相加:

q1=[s1,x1i+y1j+z1k]q1=[s1,x1i+y1j+z1k]q2=[s2,x2i+y2j+z2k]q2=[s2,x2i+y2j+z2k]q1⋅q2=s1s2+x1x2+y1y2+z1z2q1⋅q2=s1s2+x1x2+y1y2+z1z2

我們也可以利用四元數點積,來計算四元數之間的角度差:

cosθ=s1s2+x1x2+y1y2+z1z2|q1||q2|cosθ=s1s2+x1x2+y1y2+z1z2|q1||q2|

對于機關四元數,我們可以簡化上面的等式:cosθ=s1s2+x1x2+y1y2+z1z2cosθ=s1s2+x1x2+y1y2+z1z2

旋轉

前面我們定義了一個特殊的複數:旋轉數。它是用來旋轉2D複數平面的點的:q=cosθ+isinθq=cosθ+isinθ

根據四元數和複數的相似性,應該有可能設計一個可以旋轉3D空間的點的四元數:q=[cosθ,sinθv]q=[cosθ,sinθv]

讓我們測試一下這個理論是否可靠,方法就是計算四元數q和向量p的積。第一步,我們把p寫成純四元數的形式:

p=[0,p]p=[0,p]

以及機關四元數q:

q=[s,λ^v]q=[s,λv^]

進而:

p′=qp=[s,λ^v][0,p]p′=qp=[s,λv^][0,p]=[−λ^v⋅p,sp+λ^v×p]=[−λv^⋅p,sp+λv^×p]

我們可以看到結果是一個同時有系數、有虛向量的四元數。

讓我們先考慮特殊的情形:pp與^vv^正交。這種情況下,點乘部分等于0:−λ^v⋅p=0−λv^⋅p=0。是以上面的四元數就變成了純四元數:

p′=[0,sp+λ^v×p]p′=[0,sp+λv^×p]

這時候,要使pp繞^vv^旋轉,我們隻需要代入s=cosθs=cosθ和λ=sinθλ=sinθ:

p′=[0,cosθp+sinθ^v×p]p′=[0,cosθp+sinθv^×p]

現在,讓我們找一個例子來測試上面的公式。譬如繞z軸(就是k軸)旋轉45度,那麼我們的四元數q就變成:

q=[cosθ,sinθk]q=[cosθ,sinθk]=[√22,√22k]=[22,22k]

然後,選一個特殊的p,并且p要和k軸正交,譬如把p放到i軸上,也就是:p=[0,2i]p=[0,2i]

好了,現在計算下qp:

p′=qpp′=qp=[√22,√22k][0,2i]=[22,22k][0,2i]=[0,2√22i+2√22k×i]=[0,222i+222k×i]=[0,√2i+√2j]=[0,2i+2j]

結果是一個繞了k軸轉了45度的純四元數。 我們可以确認這個四元數的向量部分的長度是:

p′=√√22+√22=2p′=22+22=2

這正是我們所期望的!

我們可以用圖像展示旋轉過程:

Understanding Quaternions 中文翻譯《了解四元數》Understanding Quaternions 中文翻譯《了解四元數》

現在,讓我們考慮更一般化的四元數,即和p不正交的四元數。現在讓我們把p的向量部分偏移45度:

^v=√22i+√22kv^=22i+22k

p=2ip=2i

q=[cosθ,sinθ^v]q=[cosθ,sinθv^]

p=[0,p]p=[0,p]

然後算qp:

p′=qpp′=qp=[cosθ,sinθ^v][0,p]=[cosθ,sinθv^][0,p][−sinθ^v⋅p,cosθp+sinθ^v×p][−sinθv^⋅p,cosθp+sinθv^×p]

代入我們設定的^v,pv^,p,以及θ=45∘θ=45∘,得到:

p′=[−√22(√22i+√22k)⋅(2i),√222i+√22(√22i+√22k)×2i]p′=[−22(22i+22k)⋅(2i),222i+22(22i+22k)×2i]=[−1,√2i+j]=[−1,2i+j]注意,算出來的結果已經不是純四元數了,并且,它并沒有旋轉45度、範數也不再是2(反而變小了,變成√33)

我們可以用圖像展示旋轉過程:

Understanding Quaternions 中文翻譯《了解四元數》Understanding Quaternions 中文翻譯《了解四元數》
嚴格來說,這樣子在3維空間中表示p′p′是不正确的。因為它其實是一個4維的向量!為了簡單起見,我隻将這個四元數的向量部分顯示出來。

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

首先計算:

q=[cosθ,sinθ(√22i+√22k)]q=[cosθ,sinθ(22i+22k)]

q−1=[cosθ,−sinθ(√22i+√22k)]q−1=[cosθ,−sinθ(22i+22k)]

(譯注:這裡q−1=q∗q−1=q∗是因為q是機關四元數)

再代入θ=45∘θ=45∘,得到:

q−1=[√22,−√22(√22i+√22k)]q−1=[22,−22(22i+22k)]

12[√2,−i−k]12[2,−i−k]

現在,把前面算出來的qp再次拿出來:

qp=[−1,√2i+j]qp=[−1,2i+j]qpq−1=[−1,√2i+j]12[√2,−i−k]qpq−1=[−1,2i+j]12[2,−i−k]=12[−√2−(√2i+j)⋅(−i−k),i+k+√2(√2i+j)−i+√2j+k]=12[−2−(2i+j)⋅(−i−k),i+k+2(2i+j)−i+2j+k]=12[−√2+√2,i+k+2i+√2j−i+√2j+k]=12[−2+2,i+k+2i+2j−i+2j+k]=[0,i+√2j+k]=[0,i+2j+k]

這下是純四元數了,并且它的範數是:

|qpq−1|=√12+√22+12=√4=2|qpq−1|=12+22+12=4=2

這和原始的p的範數一緻。

下面的圖像展示了旋轉結果:

Understanding Quaternions 中文翻譯《了解四元數》Understanding Quaternions 中文翻譯《了解四元數》

是以我們可以看到,這個結果是一個純四元數,并且原四元數的向量的範數也保持住了。但是還有一個問題:向量被旋轉了90度而不是45度。這剛好是我們需要的度數的兩倍!為了正确地讓一個向量繞某個軸向量旋轉某個角度,我們必須以目标角度的一半來計算。是以,我們構造了下面的四元數:

q=[cos12θ,sin12θ^v]q=[cos12θ,sin12θv^]

這就是旋轉四元數的一般形式!

四元數插值

在計算機圖形學中使用四元數,其中一個重要原因是四元數非常适合用來表示空間中的旋轉。四元數解決了其他3維空間旋轉算法會遇到的惱人的問題,比如使用歐拉角來表示旋轉操作時會遇到的萬向節鎖問題(Gimbal lock)。

使用四元數,我們可以定義好幾種方案來表示3維空間的轉動插值。第一種是SLERP,它被用來把一個點(物體)從一個朝向平滑地插值到另一個朝向。第二個是SLERP的擴充版本,被稱為SQAD,它被用來處理用一系列朝向定義得到的一條路徑的插值。

SLERP

SLERP代表Spherical Linear Int*erp*olation。SLERP可以在2個朝向之間平滑地插值。

第一個朝向設為q1q1,第二個朝向設為q2q2 (請記住,這2個訓示朝向的四元數是機關四元數,不然閱讀下文會混亂)。被插值前的點設為pp,插值後的點設為p′p′。而插值參數t,當t=0時會把pp轉到q1q1,當t=1時會轉到q2q2。

标準的線性插值公式是(譯注:這個公式是笛卡爾坐标系下的,不是指四元數):

p′=p1+t(p2−p1)p′=p1+t(p2−p1)

應用這個等式的一般步驟是:

  • 計算p1、p2p1、p2之間的差。
  • 根據參數t,計算兩個點的差的小數值(因為0<=t<=1)
  • 把第二步的值加上原始點的值,算出結果

我們可以把這個基礎公式,套用到2個用四元數表示的朝向的插值上。

四元數的差

根據上面的公式的第一步,我們必須先計算q1、q2q1、q2的差。對于四元數來說,這等價于計算2個四元數的角度差(angular difference):

diff=q−11q2diff=q1−1q2

(譯注:由q1pdiff=q2q1pdiff=q2推出 )

四元數的幂運算

接下來的目标是幹掉上面四元數的差的分數部分,方法是計算四元數的t次幂(就是上面的那個插值參數t,區間是[0,1])。

四元數的幂運算的一般化公式是:

qt=exp(tlogq)qt=exp(tlog⁡q)

其中,(純)四元數的exp函數的公式是:

eq=exp(q)=exp([0,θ^v])eq=exp(q)=exp([0,θv^])=[cosθ,sinθ^v]=[cosθ,sinθv^]

(純)四元數的對數公式是:

logq=log(cosθ+sinθ^v)log⁡q=log⁡(cosθ+sinθv^)=log(exp(θ^v))=log⁡(exp(θv^))=θ^v=θv^=[0,θ^v]=[0,θv^]

(譯注:上述的2次公式推導,其實省略了很多證明過程。具體可以參考: 四元數公式的補充 )

對于t = 0,我們有:

q0=exp(0logq)q0=exp(0log⁡q)=exp([cos(0),sin(0)^v])=exp([cos(0),sin(0)v^])=exp([1,0])=exp([1,0])=[1,0]=[1,0]

而對于t = 1,有:

q1=exp(1logq)=qq1=exp(1log⁡q)=q

2個四元數的分數差

對于角旋轉的插值計算,我們利用q1和q2的角度分數差來調整原始朝向q1:

q′=q1(q−11q2)tq′=q1(q1−1q2)t

這也就是使用四元數的球面線性插值的一般形式。然而,這不是slerp函數的常用形式。

我們可以應用類似的用于計算向量的球面插值公式,到四元數裡。計算向量的球面插值的一般形式定義如下:

vt=sin((1−t)θ)sinθv1+sin(tθ)sinθv2vt=sin((1−t)θ)sinθv1+sin(tθ)sinθv2

用圖像表示如下:

Understanding Quaternions 中文翻譯《了解四元數》Understanding Quaternions 中文翻譯《了解四元數》

這個公式可以原封不動地應用到四元數:

qt=sin((1−t)θ)sinθq1+sin(tθ)sinθq2qt=sin((1−t)θ)sinθq1+sin(tθ)sinθq2

但這個公式需要提供角度θθ,我們可以計算q1q1和q2q2的點積進而得出角度θθ:

cosθ=q1⋅q2|q1||q2|cosθ=q1⋅q2|q1||q2|

cosθ=s1s2+x1x2+y1y2+z1z2|q1||q2|cosθ=s1s2+x1x2+y1y2+z1z2|q1||q2|

θ=cos−1(s1s2+x1x2+y1y2+z1z2|q1||q2|)θ=cos−1(s1s2+x1x2+y1y2+z1z2|q1||q2|)

注意事項

這個方案有2個問題,必須在實作過程中加以考慮。

第一,如果四元數點積的結果是負值,那麼後面的插值就會在4D球面上繞遠路,這并不是我們想要的。為了解決這個問題,我們測試點積的結果,當結果是負值時,我們将2個四元數的其中一個取反,取反它的系數和向量部分,并不會改變它代表的朝向。而經過這一步操作,可以保證這個旋轉走的是最短路徑。

當q1q1和q2q2的角度差非常小,小到導緻sinθ=0sinθ=0 時,會出現第二個問題。如果這個情況出現了,當我們除以sinθsinθ時就會得到一個未定義的結果。在這個情況下,我們可以回退去使用q1q1和q2q2的線性插值。

SQUAD

正如一個SLERP可以被用來計算四元數之間的插值,一個SQUAD (Spherical and Quadrangle)可以被用來對旋轉路徑進行平滑插值。

如果我們有四元數序列:

q1,q2,q3,⋯,qn−2,qn−1,qnq1,q2,q3,⋯,qn−2,qn−1,qn

然後我們再定義一個"輔助"四元數(sisi),它是一個中間控制點:

si=exp(−log(qi+1q−1i)+log(qi−1q−1i)4)qisi=exp(−log⁡(qi+1qi−1)+log⁡(qi−1qi−1)4)qi

是以,沿着子曲線的朝向可以定義為:

qi−1,qi,qi+1,qi+2qi−1,qi,qi+1,qi+2

在t時刻的朝向就是:

squad(qi,qi+1,si,si+1,t)=slerp(slerp(qi,qi+1,t),slerp(si,si+1,t),2t(1−t))squad(qi,qi+1,si,si+1,t)=slerp(slerp(qi,qi+1,t),slerp(si,si+1,t),2t(1−t))

總結

除了特别難了解之外,相比矩陣或歐拉角,四元數在表示旋轉這個事情上,擁有一些明顯的優點。

  • SLERP和SQUAD,提供了一種使得在朝向之間可以平滑過渡的方法。
  • 使用四元數來串聯"旋轉",要比使用矩陣快得多。
  • 對于機關四元數,逆向旋轉可以通過對向量部分取反來實作。而計算一個矩陣的逆矩陣是被認為比較慢的,如果這個矩陣未被标準正交化的話(标準正交矩陣的逆矩陣是它的轉置矩陣)。
  • 從四元數轉換到矩陣,要比從歐拉角轉換到矩陣快一點。
  • 四元數隻需要4個數字(如果旋轉四元數已經機關化了那麼隻需要3個,實數部分可以在運作時計算)來表示一個旋轉,而矩陣需要至少9個數字。

盡管使用四元數有這麼多優點,還是有缺點存在的。

  • 因為浮點數的舍入運算錯誤,四元數可能會變無效。不過,這個錯誤可以通過重新機關化四元數來避免。
  • 使用四元數最具威懾性的地方,還是四元數的了解難度大。我希望這個問題可以通過閱讀本文來解決。

存在一些已經實作了四元數、并且是正确的的數學程式庫。在我的個人經驗裡,我發現GLM(OpenGL Math Library)是一個優秀的數學庫,它的四元數的實作極其不錯。如果你對在你的程式中使用四元數感興趣,那麼我會推薦你使用這個數學庫。

下載下傳Demo

我實作了一個小demo來示範一個四元數如何被用來旋轉一個3維物體。這個demo是用Unity3.5.2實作的,你可以免費下載下傳它和閱讀它的腳本。zip檔案也包含了一個Windows版的Unity程式。當然你可以自己建構一個Mac的版本。

Understanding Quaternions.zip

繼續閱讀