1. 程式人生 > >【Unity程式設計】尤拉角與萬向節死鎖(圖文版)

【Unity程式設計】尤拉角與萬向節死鎖(圖文版)

萬向節死鎖(Gimbal Lock)問題

上文中曾經說過,尤拉旋轉的順規和軸向定義,自然造就了“萬向節死鎖”問題。本文主要來探索它自然形成的原因。

陀螺儀

首先,我們來了解Gimbal 究竟是個什麼玩意兒。下面來自維基百科中關於Gimbal的一段引述:

平衡環架(英語:Gimbal)為一具有樞紐的裝置,使得一物體能以單一軸旋轉。由彼此垂直的樞紐軸所組成的一組三隻平衡環架,則可使架在最內的環架的物體維持旋轉軸不變,而應用在船上的陀螺儀、羅盤、飲料杯架等用途上,而不受船體因波浪上下震動、船身轉向的影響。

Gimbal

上圖就是一個Gimbal裝置了,它是一個陀螺儀。中間有一根豎軸,穿過一個金屬圓盤。金屬圓盤稱為轉子,豎軸稱為旋轉軸。轉子用金屬製成,應該是了增加質量,從而增大慣性。豎軸外側是三層巢狀的圓環,它們互相交叉,帶來了三個方向自由度的旋轉。
看著不停轉來轉去,有點暈,接下來看兩個靜態的。這兩張圖來自百度百科。

陀螺儀中文註釋: 陀螺儀

其中Gimbal只代表陀螺儀裝置中的平衡環,顯然維基百科上將它解釋成“平衡環架”更為合理。

Pitch、Yaw、Roll

在解釋陀螺儀的工作原理之前,我先介紹一些轉動的術語。在飛行器的航行中,進行XYZ三個方向旋轉的旋轉有專業的術語,見下圖:

Pitch、Yaw、Roll

沿著機身右方軸(Unity中的+X)進行旋轉,稱為pitch,中文叫俯仰
沿著機頭上方軸(Unity中的+Y)進行旋轉,稱為Yaw,中文叫偏航
沿著機頭前方軸(Unity中的+Z)進行旋轉,稱為Roll,中文叫桶滾

陀螺儀的工作原理

我們知道陀螺儀使用來測量平衡和轉速的工具,在載體高速轉動的時候,陀螺儀始終要通過自我調節,使得轉子保持原有的平衡,這一點是如何做到的?帶著這個問題,我們來看一下這個古老而又神祕的裝置的工作原理。

為了解釋清楚問題,我自己畫了一個簡單的陀螺儀示意圖。(金屬圓盤我就省略了,醜點兒也就別管了。。)
陀螺儀示意圖
這裡,我把三個Gimbal環用不同的顏色做了標記,底部三個軸向,RGB分別對應XYZ。
假設現在這個陀螺儀被放在一艘船上,船頭的方向沿著+Z軸,也就是右前方。

  • 現在假設,船體發生了搖晃,是沿著前方進行旋轉的搖晃,也就是桶滾。由於轉子和旋轉軸具有較大的慣性,只要沒有直接施加扭矩,就會保持原有的姿態。由於上圖中綠色的活動的連線頭處是可以靈活轉動的,此時將發生相對旋轉,從而出現以下的情形:
    桶滾平衡

  • 再次假設,船體發生了pitch搖晃,也就是俯仰。同樣,由於存在相應方向的可以相對旋轉的連線頭(紅色連線頭),轉子和旋轉軸將仍然保持平衡,如下圖:
    俯仰平衡

  • 最後假設,船體發生了yaw搖晃,也就是偏航,此時船體在發生水平旋轉。相對旋轉發生在藍色連線頭。如下圖:
    偏航平衡

最終,在船體發生Pitch、Yaw、Roll的情況下,陀螺儀都可以通過自身的調節,而讓轉子和旋轉軸保持平衡。

陀螺儀中的萬向節死鎖

現在看起來,這個陀螺儀一切正常,在船體發生任意方向搖晃都可以通過自身調節來應對。然而,真的是這樣嗎?

假如,船體發生了劇烈的變化,此時船首仰起了90度(這是要翻船的節奏。。。。),此時的陀螺儀調節狀態如下圖:
死鎖開始

此時,船體再次發生轉動,沿著當前世界座標的+Z軸(藍色軸,應該正指向船底)進行轉動,那麼來看看發生了什麼情況。

死鎖的陀螺儀

現在,轉子不平衡了,陀螺儀的三板斧不起作用了。它失去了自身的調節能力。那麼這是為什麼呢?
之前陀螺儀之所以能通過自身調節,保持平衡,是因為存在可以相對旋轉的連線頭。在這種情況下,已經不存在可以相對旋轉的連線頭了。
那麼連線頭呢?去了哪裡?顯然,它還是在那裡,只不過是,連線頭可以旋轉的相對方向不是現在需要的按著+Z軸方向。從上圖中,我們清楚地看到:

  • 紅色連線頭:可以給予一個相對俯仰的自由度。
  • 綠色連線頭:可以給予一個相對偏航的自由度。
  • 藍色連線頭:可以給予一個相對偏航的自由度。

沒錯,三個連線頭,提供的自由度只對應了俯仰和偏航兩個自由度,桶滾自由度丟失了。這就是陀螺儀上的“萬向節死鎖”問題。

用小程式來重現萬向節死鎖問題

首先,預設一下接下來的尤拉角變化順序。見下圖:
預設尤拉旋轉

上圖中,紅色框內的部分的列表,記錄了接下來尤拉角的增長變化過程。即它會從(0,0,0)變化到(90,0,0),再變化到(90,90,0),再變化到(90,180,0),再變化到(90,180,90),再變化到(90,180,180)。下圖是變化的過程演示。

YZ軸死鎖

現在可以看到:
- 當先執行X軸旋轉90度,此時在執行Pitch(俯仰)變化。
- 再在Y軸進行變化0-180度,此時在執行相對自身的Roll(桶滾)變化。
- 再在Z軸進行變化0-180度,此時仍在執行相對自身的Roll(桶滾)變化。

這裡所說的俯仰、桶滾、偏航都是相對自己區域性座標系的。這與上述的陀螺儀中出現的問題是一樣的,萬向節死鎖。也就是儘管尤拉角在XYZ三個軸向進行進動(持續增長或者減少),但是影響最終的結果,只對應了兩個軸向。

死鎖的過程解析

《Unity中尤拉旋轉》一文中我曾提到,是尤拉角順規和軸向的定義方式,造就了“萬向節死鎖”問題的自然形成。通過上述的例子,這裡作個詳細解釋。

首先我們知道,由於Unity中尤拉旋轉的順規的定義,圍繞Z軸的進動最先執行,所以,Z軸是“嚴格保護”的一個軸,就是說,當先沿著Z軸進行進動時,無論此時的XY是什麼值,最終的結果,圍繞Z軸的進動始終造成相對自身執行桶滾變化。
然而X、Y軸就不同了,我們先不考慮Y軸,假設其一直為0,先說X軸。如果Z軸也是保持為0,那麼圍繞X軸進動,最終的影響是預期的俯仰變化。如下圖:

YZ為0的X進動

然而當Z軸為90度時,圍繞X軸進動變成了偏航變化,如下圖:

Z先進動90的X進動

也就是說,尤拉角的X軸進動造成最後的變化結果,受到到了預先執行的Z軸進動的影響,它仍然會造成某個相對自身的軸向的變化,但是結果不唯一;同樣,尤拉角的Y軸進動,則受到了Z軸和X軸的影響,結果更加不唯一。

然而,以上的過程執行,都是嚴格遵守尤拉角的順規和軸向定義的。某些時刻,這種不確定的結果,就可能造成某個軸向自由度的丟失。
就拿下圖來說:
YZ軸死鎖

尤拉角Z軸的進動,最先執行,造成桶滾,這個沒問題。
尤拉角Y軸的進動,最後執行,造成沿著尤拉旋轉前的Y軸旋轉,這也是根據定義執行。然而現在這種沿著Y軸的旋轉,同樣也被對映到了物體的桶滾變化。

總結

總結來說,尤拉角的“萬向節死鎖”問題,是由於尤拉旋轉定義本身造成的。這種圍繞選旋轉前固定軸的先Z、再X、再Y的旋轉操作,與其最終所預期的三個軸向可以旋轉的結果並非一定是一對一的對映。某些情況下是多對一的對映,造成一些旋轉自由度的缺失,也就是“死鎖”。

建議

對於寫程式碼來說,你直接去改變Transform的尤拉角顯然是不合適的,通過本文也可以看到,這種結果幾乎是不可預測的。但是某些情況下,卻是可以預期的,就是你僅僅在一個軸向進動,其它兩個軸向保持為0,此時有效,並且直接修改尤拉角的程式碼效率應該是比較高的。