Android 傳感器 II-運動傳感器

概述:

Android平臺提供了一些傳感器讓我們可以監測設備的運動情況. 其中的兩種總是基於硬件的(加速度計和陀螺儀), 其中的三個是能軟能硬的(重力計, 線性加速計和旋轉矢量傳感器). 比如, 在一些設備上基於軟件的傳感器會從加速度計和磁力計來計算它們的數據, 但是在其它設備上它們可能也使用陀螺儀來作爲它們的數據來源. 大多數Android設備都擁有一個加速度計, 很多都已經包含陀螺儀. 基於軟件的傳感器的可用性更加多樣, 因爲它們通常依賴於一個或一個以上的硬件傳感器來作爲數據來源.

運動傳感器顧名思義是用於監測設備移動的, 比如傾斜, 搖動, 旋轉或擺動等. 這種運動通常來源於用戶的輸入(慄如, 用戶在汽車類的遊戲中轉動設備或者控制球類遊戲等), 但是它也可以來自於設備本身的位置(比如設備跟着汽車一起運動). 第一個栗子中, 監測的是相對於設備作爲參考框架. 第二個例子中則是相對於全世界做參考框架. 運動傳感器本身通常不用來監測設備位置, 但是可以同其它傳感器一起使用, 比如地磁傳感器, 來確定設備相對於世界中的位置.

所有的運動傳感器都會在SensorEvent中返回傳感器值的多維數組. 比如, 在一個傳感器事件中, 加速計會爲座標系的三個方向返回加速度數據, 陀螺儀返回三維旋轉速率數據. 這些數據值以float數組格式同其它SensorEvent參數一起返回. 下表總結了Android平臺上可用的運動傳感器:

傳感器

SensorEvent數據

描述

測量單位

TYPE_ACCELEROMETER

SensorEvent.values[0]

沿x軸的加速度(包括重力)

m/s2(平方)

SensorEvent.values[1]

沿y軸的加速度(包括重力)

SensorEvent.values[2]

沿z軸的加速度(包括重力)

TYPE_GRAVITY

SensorEvent.values[0]

沿x軸的重力

m/s2

SensorEvent.values[1]

沿y軸的重力

SensorEvent.values[2]

沿z軸的重力

TYPE_GYROSCOPE

SensorEvent.values[0]

繞x軸旋轉的速率

rad/s

SensorEvent.values[1]

繞y軸旋轉的速率

SensorEvent.values[2]

繞z軸旋轉的速率

TYPE_GYROSCOPE_UNCALIBRATED

SensorEvent.values[0]

繞x軸的旋轉速率(無偏移補償)

rad/s

SensorEvent.values[1]

繞y軸的旋轉速率(無偏移補償)

SensorEvent.values[2]

繞z軸的旋轉速率(無偏移補償)

SensorEvent.values[3]

繞x軸的預計偏移

SensorEvent.values[4]

繞y軸的預計偏移

SensorEvent.values[5]

繞z軸的預計偏移

TYPE_LINEAR_ACCELERATION

SensorEvent.values[0]

沿x軸的加速度(不包括重力)

m/s2

SensorEvent.values[1]

沿y軸的加速度(不包括重力)

SensorEvent.values[2]

沿z軸的加速度(不包括重力)

TYPE_ROTATION_VECTOR

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)).(標量分量是可選的)

TYPE_SIGNIFICANT_MOTION

N/A

N/A

N/A

TYPE_STEP_COUNTER

SensorEvent.values[0]

從傳感器激活那一刻起的步數

步數

TYPE_STEP_DETECTOR

N/A

N/A

N/A

旋轉矢量傳感器和重力傳感器是運動檢測中使用頻率最高的傳感器. 旋轉矢量傳感器是多功能的, 可以廣泛的用於運動相關的任務, 比如檢測手勢, 檢測角度改變, 檢測相關的方向變化. 在開發遊戲的時候經常用到旋轉矢量傳感器, 還有增強現實的應用, 二維或者三維的羅盤, 或者一個相機穩定的APP. 在大多數的情況下, 使用這些傳感器會比用加速度計和磁力計或者方向傳感器更好.

Android開源工程傳感器:

Android Open Source Project(AOSP)提供了三種基於軟件的運動傳感器: 一個重力傳感器, 一個線性加速度傳感器, 還有一個旋轉矢量傳感器. 這些傳感器在Android 4.0中添加, 現在使用設備的陀螺儀來提高穩定性和性能. 如果想要嘗試一下這些傳感器, 可以使用getVendor()和getVersion()方法(vendor是Google公司, 版本號是3). 使用vendor和version來識別這些傳感器是必要的, 因爲Android將這三個傳感器作爲輔助傳感器. 比如如果一個設備供應商提供了它們的重力傳感器, 那麼AOSP重力傳感器將會作爲一個輔助備用的傳感器來顯示. 所有的這三個傳感器都依賴於陀螺儀: 如果設備沒有陀螺儀, 這些傳感器將不會顯示也不能用.

使用加速度計:

加速度傳感器用來測量設備的加速度, 包括重力. 下面的代碼演示瞭如何獲取一個加速度傳感器的實例:

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

理論上講, 一個加速度傳感器確定加速度通過測量應用在傳感器本身的力(Fs)來計算出應用在設備上的力(Ad), 然後使用這個公式一套:

Ad = - ∑Fs / mass

然而根據這個公式重力會總是影響到測量的結果:

Ad = -g - ∑F / mass

根據這個原因, 當設備放在桌子上(沒有被加速), 加速度傳感器會讀取到一個值爲g=9.81m/s2. 同樣, 當設備在自由落體的時候, 加速度讀取到的值爲g=0m/s2. 因此想要測量到真實的加速度, 必須將重力移除. 這可以通過一個high-pass過濾器實現. 相反, 一個low-pass過濾器可以被用來隔離出重力. 栗子:

public void onSensorChanged(SensorEvent event){
  // In this example, alpha is calculated as t / (t + dT),
  // where t is the low-pass filter's time-constant and
  // dT is the event delivery rate.

  final float alpha = 0.8;

  // Isolate the force of gravity with the low-pass filter.
  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];

  // Remove the gravity contribution with the high-pass filter.
  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)來創建一個low-pass過濾器. 過濾器常量是從一個時間常量衍生的(t). 如果想要使用該方法, 那麼可能需要更換alpha.

加速度計使用標準的傳感器座標系統. 在實踐中, 這意味着當設備被放在水平的桌子上並處於自然方向的時候下面的條件適用:

l  如果將設備往右邊推, x加速度值爲正.

l  如果從設備底部推(這樣設備會遠離你), y加速度爲正.

l  如果將設備往天上推, 並且加速度爲Am/s2,z加速度將會等於A+9.81, 也就是設備加速度加重力加速度.

l  靜止的設備擁有一個等於9.81的加速度.

通常如果想要監測設備的運動的話, 加速度計是一個很好的傳感器. 幾乎每個Android的設備都帶有一個加速度計, 它比其它的傳感器消耗更少的能量(10倍). 缺陷則是我們得自己實現對重力的過濾和降噪.

使用重力傳感器:

重力傳感器提供了三維矢量來描述重力的方向和大小. 下面代碼可以讓我們取得重力傳感器的實例:

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

單位和座標系跟加速度計一樣. 設備靜止的時候, 重力傳感器和加速度傳感器輸出的結果應該相同.

陀螺儀:

陀螺儀測量的是設備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軸的正方向看過去, 設備逆時針旋轉, 則爲正向. 這是正方向的標準定義, 不同於方向傳感器的定義. 通常陀螺儀的輸出是整數, 隨着時間的推移按照時間戳計算旋轉的度數, 栗子:

// Create a constant to convert nanoseconds to seconds.
private static final float NS2S = 1.0f / 1000000000.0f;
private final float[] deltaRotationVector = new float[4]();
private float timestamp;

public void onSensorChanged(SensorEvent event) {
  // Thistimestep's delta rotation to be multiplied by the current rotation
  // aftercomputing it from the gyro sample data.
  if (timestamp != 0) {
    final float dT = (event.timestamp - timestamp) * NS2S;
    // Axis of therotation sample, not normalized yet.
    float axisX = event.values[0];
    float axisY = event.values[1];
    float axisZ = event.values[2];

    // Calculate theangular speed of the sample
    float omegaMagnitude = sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);

    // Normalize therotation vector if it's big enough to get the axis
    // (that is,EPSILON should represent your maximum allowable margin of error)
    if (omegaMagnitude > EPSILON) {
      axisX /= omegaMagnitude;
      axisY /= omegaMagnitude;
      axisZ /= omegaMagnitude;
    }

    // Integratearound this axis with the angular speed by the timestep
    // in order toget a delta rotation from this sample over the timestep
    // We willconvert this axis-angle representation of the delta rotation
    // into aquaternion before turning it into the rotation matrix.
    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);
    // User codeshould concatenate the delta rotation we computed with the current rotation
    // in order toget the updated rotation.
    //rotationCurrent = rotationCurrent * deltaRotationMatrix;
   }
}
 

標準陀螺儀提供原始旋轉數據而不經任何過濾, 也不去糾正噪音和偏差. 實踐中, 陀螺儀噪音和偏差將會引發錯誤, 所以需要糾正它們. 我們經常檢查通過檢查其它的傳感器來計算偏差和噪音, 比如重力傳感器或者加速度計.

使用未校準陀螺儀:

爲校準的陀螺儀跟陀螺儀相似, 不同的是它沒有對旋轉速率進行陀螺偏移補償. 工廠校準和溫度補償依然應用於旋轉速率. 爲校準陀螺儀對後期處理和融合方向數據很有用. 通常, gyroscope_event.value[0]將會跟uncalibrated_gyroscope_event.values[0]- uncalibrated_gyroscope_event.values[3]接近. 就是,

calibrated_x ~= uncalibrated_x -bias_estimate_x

注意: 爲校準陀螺儀提供了更多的原始數據並存在一些偏差, 但是它們的測量通過校準可以包含更少的跳變. 一些APP可能更喜歡這些爲校準的結果, 因爲更加的平滑和可靠. 慄如, 如果一個APP嘗試實現自己的傳感器合成, 引入校準實際上會扭曲結果.

除了旋轉速率, 爲校準陀螺儀還爲各軸提供了預計的偏差. 下面的代碼演示瞭如何獲取一個陀螺儀實例:

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

使用線性加速計:

線性加速傳感器提供了一個代表加速度的三維矢量, 但是不包含重力. 下面代碼演示瞭如何獲取一個它的實例:

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

從概念上講, 該傳感器提供的加速數據是根據這個公式:

linear acceleration = acceleration - acceleration due to gravity

我們通常在想要獲取不包含重力的加速數據的時候使用這個傳感器. 慄如, 可以使用這個傳感器來看汽車行駛的多快. 線性加速傳感器總是有一個偏移, 我們應該移除這個偏移. 最簡單的實現這個操作的方法是在APP中創建一個校準步驟. 校準過程中我們可以請求用戶將設備放置於一個桌子上, 然後讀取三個軸的偏移. 然後減去這個偏移來獲得真實的線性加速度. 它的座標系和加速度傳感器一樣, 單位也是m/s2.

使用旋轉矢量傳感器:

旋轉矢量(向量)用角度和數值的組合表示設備的方向, 設備繞軸旋轉的角度爲θ. 下面的代碼展示瞭如何獲取默認的旋轉矢量傳感器:

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軸的定義跟加速度計一樣. 座標系定義爲一個標準正交基(上圖). 該座標系統有下面三個特性:

X定義爲矢量積Y乘以Z. 它在設備當前所處的位置與地面相切, 並指向東邊.

Y也在設備當前的位置與地面相切, 並指向地磁北極.

Z則朝向天空並垂直於地面.

Android sample中包含使用旋轉矢量傳感器的栗子.

使用重要(significant)運動傳感器:

每次檢測到重要運動的時候出發傳感器然後關閉自己. 一個重要運動是指可以導致用戶座標改變的運動. 慄如, 走路, 騎自行車, 或者坐在一輛運行的汽車中. 下面的代碼演示瞭如何獲得一個該傳感器的實例, 還有如何註冊一個事件監聽器:

private SensorManager mSensorManager;
private Sensor mSensor;
private TriggerEventListener mTriggerEventListener;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);

mTriggerEventListener = new TriggerEventListener() {
    @Override
    public void onTrigger(TriggerEvent event) {
        // Do work
    }
};

mSensorManager.requestTriggerSensor(mTriggerEventListener, mSensor);

使用步數傳感器(stepcounter sensor):

步數傳感器可以從上一次重啓之後(步數傳感器被激活)用戶走過的總步數. 步數傳感器有更多的延遲(最多可以達到10s)但是比步檢測傳感器(step detector sensor, 下面介紹)更加精確. 栗子:

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

使用步檢測傳感器(stepdetector sensor):

步檢測傳感器當每次用戶邁出一步的時候觸發一次. 延遲通常低於2s. 栗子:

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

 

參考: https://developer.android.com/guide/topics/sensors/sensors_motion.html

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章