Android開發者指南-運動傳感器Motion Sensor

Android 平臺支持很多監測設備運動的傳感器。其中有兩個傳感器一定是基於硬件的(加速度計和陀螺儀),有三個可能基於硬件或軟件(重力計、線性加速計和旋轉向量傳感器)。 比如,某些設備的軟傳感器利用加速度計和磁力計來報送數據,而其它一些設備可能用陀螺儀來報送數據。 大部分 Android 平臺的設備都帶有加速計,有很多現在還帶有陀螺儀。軟傳感器的可用性變數更大一些,因爲它們常常依靠一個以上硬件傳感器來報送數據。
運動傳感器對於監測設備的移動非常有用,諸如傾斜、震動、旋轉、搖擺等。 這些動作通常是直觀反映了用戶的輸入(比如用戶在遊戲中操縱汽車或者運球),但也可能反映了設備所處的物理環境變化(比如你在開車,設備也隨着移動)。 在第一種場合中,你監測的運動是以設備或應用爲參照系;而在第二種場合,運動是以地球爲參照系的。 一般情況下,運動傳感器不是用來監測設備的方位的,但它們可以與其他傳感器合作使用,比如地磁傳感器,用於檢測設備的在地球參照系中的方位(詳見 方位傳感器
所有的運動傳感器都會在 SensorEvent 中 返回用多維數組表示的傳感數據。例如,在一個傳感器事件中,加速計會返回三維座標軸上的加速度數據,陀螺儀會返回三維座標軸上的旋轉速率數據。 這些數據以 float 數組的方式在參數中返回。表 1 列出了 Android 平臺支持的所有運動傳感器:
表 1.. Android 平臺支持的運動傳感器。
感器 傳感器事件數據 說明 測量單位
SensorEvent.values[0]
沿 x 軸的加速度(包括重力)。
m/s2
SensorEvent.values[1]
沿 y 軸的加速度(包括重力)。
SensorEvent.values[2]
沿 z 軸的加速度(包括重力)。
SensorEvent.values[0]
沿 x 軸的重力加速度。
m/s2
SensorEvent.values[1]
沿 y 軸的重力加速度。
SensorEvent.values[2]
沿 z 軸的重力加速度。
SensorEvent.values[0]
圍繞 x 軸的旋轉角速度。
rad/s
SensorEvent.values[1]
圍繞 y 軸的旋轉角速度。
SensorEvent.values[2]
圍繞 z 軸的旋轉角速度。
SensorEvent.values[0]]
沿 x 軸的加速度(不包括重力)。
m/s2
SensorEvent.values[1]
沿 y 軸的加速度(不包括重力)。
SensorEvent.values[2]
沿 z 軸的加速度(不包括重力)。
SensorEvent.values[0]]
旋轉向量沿 x 軸的部分(x * sin(θ/2))。
無無
SensorEvent.values[1]
旋轉向量沿 y 軸的部分(y * sin(θ/2))。
SensorEvent.values[2]]
旋轉向量沿 z 軸的部分(z * sin(θ/2))。
SensorEvent.values[3]]
旋轉向量的數值部分((cos(θ/2))1。
1 數值部分是可選的。
檢測和監視運動最常用的就是旋轉向量傳感器和重力傳感器。 旋轉向量傳感器尤爲強大,在有關運動的任務中用途十分廣泛,諸如檢測手勢、監測角度變化、監測相對方位的變化。 比如,如果你正在開發遊戲、增強現實(Augmented Reality)應用、二維或三維羅盤、相機防抖應用,那麼旋轉向量傳感器將十分有用。 在大多數場合,使用這兩個傳感器要比加速度計、磁力傳感器和方向傳感器更加合適。
Android 開源項目傳感器
Android 開源項目(AOSP)提供了三種基於軟件的運動傳感器:重力傳感器、線性加速度傳感器和旋轉向量傳感器。 Android 4.0 中對這三種傳感器進行了升級,目前利用陀螺儀(除了其它傳感器)來增加穩定性和提高性能。 如果你想試試這些傳感器,你可以用 getVendor() 和 getVersion() 方法來識別它們(製造商 vendor 爲 Google 公司);版本號爲3)。 因爲 Android 系統把這三種傳感器視爲備選傳感器,所以必須用 vendor 和版本號來識別它們。 比如,如果設備製造商已經提供了重力傳感器,則 AOSP 重力傳感器會顯示爲備選傳感器。 這三個傳感器都依賴於陀螺儀:如果設備未提供陀螺儀,則它們都不會顯示出來,也無法使用。
使用加速度計
加速度傳感器測量設備的加速度,包括重力加速度。以下代碼展示瞭如何獲取缺省的加速度傳感器的一個實例:

private SensorManager mSensorManager;
private Sensor mSensor;
  ...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

從概念上說,加速度傳感器通過測量施於傳感器上的作用力,並按以下關係來檢測設備的加速度(Ad)。
Ad = - ∑Fs / mass
然而,重力總是會按以下關係影響測量的精度。
Ad = -g - ∑F / mass
因此,如果設備是平放在桌子上的(沒有加速度),加速度計會讀到g = 9.81 m/s2。 同理,設備在自由落體或以 9.81 m/s2 的加速度墜向地面時,加速度計會讀到 g = 0 m/s2。 因此,要測出設備真實的加速度,必須排除加速計數據中的重力干擾。這可以通過高通濾波器來實現。 反之,低通濾波器則可以用於分離出重力加速度值。以下例程展示了它們的用法:

public void onSensorChanged(SensorEvent event){
  
// 在本例中,alpha 由 t / (t + dT)計算得來,
  
// 其中 t 是低通濾波器的時間常數,dT 是事件報送頻率

  
final float alpha = 0.8;

  
// 用低通濾波器分離出重力加速度
  gravity
[0]  = alpha * gravity[0] + (1 - alpha) * event.values[0];
  gravity
[1]  = alpha * gravity[1] + (1 - alpha) * event.values[1];

  gravity[2 = alpha * gravity[2] + (1 - alpha) * event.values[2];


  
// 用高通濾波器剔除重力干擾
  linear_acceleration
[0] = event.values[0] - gravity[0];
  linear_acceleration
[1] = event.values[1] - gravity[1];
  linear_acceleration
[2] = event.values[2] - gravity[2];
}

注意: 你可以使用很多技術來過濾傳感器數據。 以上例程只是使用了過濾器常量(alpha)來創建一個低通濾波器。 這個過濾器常量是由時間常量(t)和傳感器事件報送頻率(dt)推導出來的,t 大致等於過濾器觸發傳感器事件的間隔時間。 爲了演示,此例程使用 0.8 作爲 alpha 的值。如果你要用這個過濾方法,你可能需選用其它的 alpha 值。 
加速計使用了標準的傳感器 座標系 。這意味着,設備以原始方位平放在桌子上時,會發生以下狀況:
  • 如果你從左側平推設備(它向右移),則 x 方向加速度爲正值。
  • 如果你從下側平推設備(它向前移),則 y 方向加速度爲正值。
  • 如果以 A m/s2的加速度向空中移動設備,則 z 方向加速度等於 A + 9.81,即設備加速度(+A m/s2)減去重力加速度(-9.81 m/s2)。
  • 靜止設備的加速度值爲 +9.81,即設備加速度(0 m/s2)減去重力加速度(-9.81 m/s2)。
一般情況下,加速度計已足夠應付對設備移動情況的監測。幾乎所有 Android 平臺的手持和桌面終端都帶有加速度計,它的能耗比其它運動傳感器要少10倍。 不過它有一個缺點,就是你不得不實現低通和高通濾波器,以消除重力影響並減少噪聲數據。
Android SDK 給出了一個應用示例,展示了加速度傳感器的使用方法( Accelerometer Play )。
使用重力傳感器
重力傳感器能以三維向量的方式提供重力方向和數量值。以下代碼展示瞭如何獲取缺省的重力傳感器的一個實例:

private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager 
= (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor 
= mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);

單位與加速度傳感器的一樣(m/s2),座標系也相同。
注意: 當設備靜止時,重力傳感器的輸出應該與加速度計相同。
使用陀螺儀
陀螺儀測量設備圍繞 x、y、z 軸旋轉的速率,單位是 rad/s。以下代碼展示瞭如何獲取缺省的陀螺儀的一個實例:

private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager 
= (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor 
= mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);

陀螺儀的 座標系 與加速度傳感器的相同。逆時針方向旋轉用正值表示,也就是說,從 x、y、z 軸的正向位置觀看處於原始方位的設備,如果設備逆時針旋轉,將會收到正值。 這是標準的數學意義上的正向旋轉定義,而與方向傳感器定義的轉動不同。
通常,陀螺儀的輸出反映了轉動時的角度變化速率。例如:

// 創建常量,把納秒轉換爲秒。
private static final float NS2S = 1.0f / 1000000000.0f;
private final float[] deltaRotationVector = new float[4]();
private float timestamp;

public void onSensorChanged(SensorEvent event) {
  
// 根據陀螺儀採樣數據計算出此次時間間隔的偏移量後,它將與當前旋轉向量相乘。
  
if (timestamp != 0) {
    
final float dT = (event.timestamp - timestamp) * NS2S;
    
// 未規格化的旋轉向量座標值,。
    
float axisX = event.values[0];
    
float axisY = event.values[1];
    
float axisZ = event.values[2];

    
// 計算角速度
    
float omegaMagnitude = sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);

    
// 如果旋轉向量偏移值足夠大,可以獲得座標值,則規格化旋轉向量
    
// (也就是說,EPSILON 爲計算偏移量的起步值。小於該值的偏移視爲誤差,不予計算。)
    
if (omegaMagnitude > EPSILON) {
      axisX 
/= omegaMagnitude;
      axisY 
/= omegaMagnitude;
      axisZ 
/= omegaMagnitude;
    
}

    
// 爲了得到此次取樣間隔的旋轉偏移量,需要把圍繞座標軸旋轉的角速度與時間間隔合併表示。
    
// 在轉換爲旋轉矩陣之前,我們要把圍繞座標軸旋轉的角度表示爲四元組。
  
float thetaOverTwo = omegaMagnitude * dT / 2.0f;
    
float sinThetaOverTwo = sin(thetaOverTwo);
    
float cosThetaOverTwo = cos(thetaOverTwo);
    deltaRotationVector
[0] = sinThetaOverTwo * axisX;
    deltaRotationVector
[1] = sinThetaOverTwo * axisY;
    deltaRotationVector
[2] = sinThetaOverTwo * axisZ;
    deltaRotationVector
[3] = cosThetaOverTwo;
  
}
  timestamp 
= event.timestamp;
  
float[] deltaRotationMatrix = new float[9];
  
SensorManager.getRotationMatrixFromVector(deltaRotationMatrix,deltaRotationVector);
    
// 爲了得到旋轉後的向量,用戶代碼應該把我們計算出來的偏移量與當前向量疊加。
    
// rotationCurrent = rotationCurrent * deltaRotationMatrix;
   
}
}

標準的陀螺儀能夠提供未經過濾的原始旋轉數據,或是經過噪聲及漂移修正的數據。 實際生活中,陀螺儀的噪聲和漂移都會引入誤差,這是需要補償的。 通常你要利用其它傳感器來確定漂移和噪聲值,比如重力傳感器或加速計。
使用線性加速計
線性加速度傳感器能向你提供一個三維向量,表示延着三個座標軸方向的加速度,但不包括重力加速度。 以下代碼展示瞭如何獲取缺省的線性加速度傳感器的一個實例:

private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager 
= (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor 
= mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);

理論上說,這個傳感器基於以下公式給出加速度數據:
線性加速度 = 加速度 - 重力加速度
這個傳感器的典型應用是獲取去除了重力干擾的加速度數據。比如,你可以用這個傳感器來獲取汽車加速度。 線性加速度傳感器總是會有些偏差,你需要把這個偏差值抵消掉。最簡單的消除方式就是在你的應用中增加一個校準的環節。 在校準過程中,你可以要求用戶先把設備放在桌子上,再來讀取三個座標軸的偏差值。 然後你就可以從傳感器的讀數中減去這個偏差值,以獲取真實的線性加速度。
傳感器 座標系 與加速度傳感器使用的相同,單位也一樣(m/s2)。
使用旋轉向量傳感器
旋轉向量代表了設備的方位,由角度和座標軸信息組成,包含了設備圍繞座標軸(x、y、z)旋轉的角度θ。 以下代碼展示瞭如何獲取缺省的旋轉向量傳感器的一個實例:

private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager 
= (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor 
= mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);

旋轉向量的三個元素用以下方式表示:
x*sin(θ/2) y*sin(θ/2) z*sin(θ/2)
旋轉向量的大小等於 sin(θ/2),方向等於旋轉軸的方向。



旋轉向量的三個元素等於四元組的後三個部分(cos(θ/2)、x*sin(θ/2)、y*sin(θ/2)、z*sin(θ/2)),沒有單位。 x、y、z 軸的定義與加速度傳感器的相同。座標參照系定義爲直接正交基(參見圖 1)。 這個座標系具有以下特點:
  • X 定義爲向量積 Y x Z。它是以設備當前位置爲切點的地球切線,方向朝東。
  • Y 是以設備當前位置爲切點的地球切線,指向地磁北極。
  • Z 與地平面垂直,指向天空。
Android SDK 提供了一個示例應用,展示了旋轉向量傳感器的使用方法。 示例應用在 API Demos 中( OS - RotationVectorDemo )。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章