OpenGL: Rotation vector sensor of Android and Device motion of iOS


為了實現一個全景圖片展示的功能,需要借助手機的姿態傳感器,實現一個這樣的功能:當手機旋轉時,視角也跟着旋轉(讀者若理解不能,可以參考下現在流行的 VR 應用,使用陀螺儀模式時的效果,亦可稱作“單目 VR 效果”)。這個功能的實現原理為:利用手機傳感器得到手機的當前的姿態的信息(可以是用各種方式來描述的),然后調整投影的參數,實現最終的圖像跟着手機旋轉的效果。

手機姿態獲取

Android

Android 平台有各種各樣的傳感器(Sensor),不止一個 sensor 可以實現前文提到的目的。在 Android SDK API Guides 中可以找到所有可用的 sensors。

1 gyroscope

這個就是陀螺儀了,是硬件傳感器(hardware sensor),可以提供設備繞 xyz 三個軸旋轉的角速度。注意這里的坐標系就是設備自身的,短邊 x 軸,長邊 y 軸,z 軸穿越手機屏幕。為實現旋轉效果需要的數據是旋轉的角度(用以計算旋轉矩陣),所以如果要用 gyroscope 則需要對數據進行積分,最好加上濾波使效果平穩。

若欲得到角度變化量而非角速度,那麽,官方提供一個例子,可以通過乘上時間差並通過計算得到一個四元數,來代表角度的變化量。實際上使用發現的問題是,該傳感器得到的數據完全就是以設備為參考坐標的,當設備豎直朝向和橫向的時候,其表現是不變的,也就是說沒有將真實世界作為參考,這個對我的需求來說是不滿足的。

2 orientation

這是一個軟件傳感器(software sensor),返回的數據不是傳感器硬件直接采集得到的,而是根據硬件的數據進行合適的計算得到的。

閱讀文檔即可得知,此用於獲取設備在 Yaw,Pitch and Roll 三個方向的旋轉的角度,根據這三個角度也就可以確定其姿態了。但是官方文檔不推薦這樣使用,而是推薦 rotation vector sensor 並配合 getRotationMatrix() 來計算出這三個參數,至於原因,官方文檔只說現在留着這個傳感器類型是歷史原因,讓大家不要用了,沒解釋其他的。具體原因后面解釋。

3 rotation vector

這個 sensor 和上面的 orientation 一樣,都是軟件傳感器。這個表示的仍然是設備的姿態,但是與上面的不同,這個用一個四元數來表示設備的旋轉,旋轉的參考坐標系是真實的物理世界。

在應用該傳感器提供的數據時,值得注意的是,有可能傳感器返回的數據不一定是四個數據,也有可能是三個參數,比較安全的做法是這樣做:

        @Override
        public void onSensorChanged(SensorEvent event) {
            float[] quat = new float[4];
            SensorManager.getQuaternionFromVector(quat, event.values);
			// use the quaternion
            // ...
        }

可參考:TYPE_ROTATION_VECTOR

比較

那么這兩個傳感器似乎都能用,到底用哪個呢?顯而易見,第一個用起來不太方便。那么后兩者區別在哪?

實際上,官方就不推薦使用 orientation,已經將其標記為 deprecated,並且提供了另一個方法叫做 getOrientation(),用法較復雜一點,從加速度計和地磁傳感器獲取數據,根據此計算得旋轉矩陣,然后在根據旋轉矩陣計算三個方向的旋轉角度。個人感覺就是放棄了幫助開發者去計算這個數據,而是告訴大家應該怎么樣算,自己去算吧。為什么要廢棄呢,根據這里的解釋,舊版傳感器的問題主要是萬向節鎖,當某一個軸轉動 90 度,就很難准確描述另外兩個軸的轉動了,對萬向節鎖的理解可以看我之前的文章:萬向節鎖(Gimbal Lock)的理解。Android API 文檔這樣說:

The orientation sensor derives its data by processing the raw sensor data from the accelerometer and the geomagnetic field sensor. Because of the heavy processing that is involved, the accuracy and precision of the orientation sensor is diminished.

Specifically, this sensor is reliable only when the roll angle is 0. As a result, the orientation sensor was deprecated in Android 2.2 (API level 8), and the orientation sensor type was deprecated in Android 4.4W (API level 20).

相比較,rotation vector 用四元數來描述旋轉,避免了這個問題,所以我采用這個傳感器來采集設備姿態的信息。

iOS

iOS 平台稍微簡單一些,沒有那么多具體的傳感器。deviceMotion 就很好用,獲取 deviceMotion.attitude 這是一個CMAttitude對象,可以直接獲取用四元數表示的設備的姿態信息。

其實 CoreMotion 這個 Framework 也是可以提供裸陀螺儀數據等等,但是 CMDeviceMotion 下面的 attitude 這個屬性返回的數據是更加准確的,如果需要精確的數據,推薦使用這個方法。

實現旋轉

這里我先簡單闡述一下一個旋轉的意義:當我們看着一張圖片的時候,如果要實現旋轉這張圖的效果,有兩個辦法:

  1. 將圖片轉一下(調整物體的模型的參數)
  2. 我的腦袋轉一下(調整攝像機的參數,也就是改變觀察矩陣(即 look at matrix)的參數)

實際上這兩種方法都可以實現,實際應用的時候隨便選一個更合適自己的就可以了。我這里要說明的是如果采用后一個辦法,不能簡單地將兩個矩陣相乘就算數了,我們要從實際情況去考慮這個問題,考慮 OpenGL 的觀察矩陣的計算:

// GLU
gluLookAt(x0, y0, z0, xref, yref, zref, Vx, Vy, Vz)

// glm
GLM_FUNC_DECL tmat4x4<T, P> glm::lookAt (   tvec3< T, P > const &   eye,
tvec3< T, P > const &   center,
tvec3< T, P > const &   up 
)

兩種是一樣的,分別指定觀察坐標系的原點(eye),參考點(即視點,相機瞄准的點,center),和向上向量(up)。當旋轉相機時,需要將 center 和 up 同時應用這個相應的旋轉。我之前犯過的一個錯誤是:我僅僅把旋轉應用到了 center 上面,而沒有應用到 up vector,up vector 一直設置的是(0, 1, 0)。直觀點說就是:視點轉了,但是頭頂朝向沒有變化,這個是不符合邏輯的,我們需要讓視線N:P_0 - P_ref和 up vector 保持垂直。

問題解決

在實際實踐中,Android 的坑還真是特別的多,我發現使用 rotation vector 計算出旋轉矩陣,會根據手機當前和真實世界的關系產生一定偏移,也就是說手機朝向北方和朝向南方得到的數據是不一樣的,而我希望的是得到一個與手機初始的朝向無關(也就是手機繞真實世界的南北極組成的軸旋轉的角度),而與手機與地平線的夾角有關的這么一個數據。總的來說就是 rotation vector 做了一些我不想要的校正。同時我找到另一個 sensor:GAME_ROTATION_VECTOR,其介紹如下:

Identical to TYPE_ROTATION_VECTOR except that it doesn't use the geomagnetic field. Therefore the Y axis doesn't point north, but instead to some other reference, that reference is allowed to drift by the same order of magnitude as the gyroscope drift around the Z axis.

In the ideal case, a phone rotated and returning to the same real-world orientation will report the same game rotation vector (without using the earth's geomagnetic field). However, the orientation may drift somewhat over time. See TYPE_ROTATION_VECTOR for a detailed description of the values. This sensor will not have the estimated heading accuracy value.

使用這個我基本上實現我想要的效果,所以以后要用這個 Rotation vector 的時候,要考慮清楚到底要用哪一個。另外還有一個 Geomagnetic Rotation Vector 實際使用效果較差,精度較低,但是省電。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM