Pro Android學習筆記(一五四):感測器(4):陀螺儀、加速感測器
陀螺儀
陀螺儀(Gyroscope sensor)測量裝置轉動的角速度。最早的陀螺儀發明在中國,科學應用則在西方,陀螺儀是為士大夫坐轎子看書是免收燭光搖曳發明的,這在很久之前一部西方拍的科教片看到,具體名字忘了。Pro Android 4.0中說陀螺儀的誤差會慢慢積累,因此通與加速感測器一致使用,通過Kalman filter進行修正。我們只簡單地進行陀螺儀資料的讀取。小例子和之前的很相似,我們只提供不同部分的程式碼片段。
public class GyroscopeSensorActivity extends Activity implements SensorEventListener{
……
@Override
protected void onCreate(Bundle savedInstanceState) {
……
sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
}
…… //註冊和登出感測器的監聽器
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) { … }
@Override /* 對於陀螺儀,測量的是x、y、z三個軸向的角速度,分別從values[0]、values[1]、values[2]中讀取,單位為弧度/秒。*/
public void onSensorChanged(SensorEvent event) {
if(event.sensor.getType() == Sensor.TYPE_GYROSCOPE)
showInfo("事件:" + " x:" + event.values[0] + " y:" + event.values[1] + " z:" + event.values[2]);
}
//在華為P6的機器上,陀螺儀非常敏感,平放在桌面,由於電腦照成的輕微震動在不斷地刷屏,為了避免寫UI造成的效能問題,只寫Log。
private void showInfo(String info){
//tv.append("\n" + info);
Log.d("陀螺儀",info);
}
}
加速度感測器(acceleration)
加速度測量感測器有x、y、z三軸,注意和2D螢幕的座標,以左上角作為原點的,而且Y向下。注意區分這兩個不同的座標系。
加速感測器的單位是加速度m/s2。如果手機平放好,x,y在位置為0,而z軸方向加速度=當前z方向加速度-g。由於g(重力加速度)垂直向下,則g=-9.81m/s2,即z軸 a=0-(-9.81)=9.81m/s2。也就是自由落體是為0。x、y、z的測量的加速度分別位於value[0]、value[1]、value[2]。
相關的程式碼和前面大同小異,我們就不再重複,感測器型別為Sensor.TYPE_ACCELEROMETER。減去重力這很重要,當裝置靜止或者勻速運動時,可以獲得裝置的角度。
我記得至少兩年前,單位請一網際網路專家講課,提到了AR(增強現實),問靠什麼感測器,其答案為陀螺儀,這是不對的,陀螺儀可以測量運動過程中的角速度,但要測量手機本身的角度,這靠的是加速儀器,因為有重力方向作為校準。
Android也提供了檢測手機旋轉角度的API,用於UI繪製,實際靠的就是加速感測器。
WindowManager window = (WindowManager)getSystemService(WINDOW_SERVICE);
//返回值為Surface.ROTATION_0(0)、Surface.ROTATION_90(1)、Surface.ROTATION_180(2)和Surface.ROTATION_270(3),可以用來確定螢幕UI的旋轉方向。注意:需要開啟“自動旋轉”才能有效檢查,否則均為Surface.ROTATION_0(手機以豎屏為主,一般都會0,但不保證都如此)。不是所以的手機都能檢測到這4個值,例如我的P6,沒有Surface.ROTATION_180,即UI不支援倒過來,如果有某個數值不支援,通過getRotation()獲取的數值可能並不準確,仍以P6為例,如果我們順時針轉90°,得到Surface.ROTATION_90,繼續順時針轉至180°,無檢測新數值,仍未Surface.ROTATION_90,再繼續順時針轉90°(至270°),仍顯示為Surface.ROTATION_90,而非Surface.ROTATION_270。
int rotation = window.getDefaultDisplay().getRotation(); //在Android2.2之前,為Display.getOrientation(),如果出現API和SDK 的API level相關,可通過Build.VERSION.SDK_INT獲得。
我們將手機平放在桌子上,通過加速度測量儀的x、y、z軸的數值,x、y為0,z為g,可以進行判斷,但是我們並不知道具體的朝向,平放是朝南還是向北,這需要地磁感應器,將在後面介紹。
下面的小例子,我們將對加速度測量儀的x,y,z數值進行修正處理,減去重力加速度,得到我們這個慣性系的加速度,並根據裝置的Y軸與垂直地面方向(重力反方向)的夾角。相關的程式碼片段如下:
……
private float[] gravity = new float[3]; //重力在裝置x、y、z軸上的分量
private float[] motion = new float[3]; //過濾掉重力後,加速度在x、y、z上的分量
private double ratioY;
private double angle;
private int counter = 1;
@Override
public void onSensorChanged(SensorEvent event) {
for(int i = 0 ; i < 3; i ++){
/* accelermeter是很敏感的,看之前小例子的log就知道。因為重力是恆力,我們移動裝置,它的變化不會太快,不象搖晃手機這樣的外力那樣突然。因此通過low-pass filter對重力進行過濾。這個低通濾波器的權重,我們使用了0.1和0.9,當然也可以設定為0.2和0.8。 */
gravity[i] = (float) (0.1 * event.values[i] + 0.9 * gravity[i]);
motion[i] = event.values[i] - gravity[i];
}
//計算重力在Y軸方向的量,即G*cos(α)
ratioY = gravity[1]/SensorManager.GRAVITY_EARTH;
if(ratioY > 1.0)
ratioY = 1.0;
if(ratioY < -1.0)
ratioY = -1.0;
//獲得α的值,根據z軸的方向修正其正負值。
angle = Math.toDegrees(Math.acos(ratioY));
if(gravity[2] < 0)
angle = - angle;
//避免頻繁掃屏,每10次變化顯示一次值
if(counter ++ % 10 == 0){
tv.setText("Raw Values : \n"
+ " x,y,z = "+ event.values[0] + "," + event.values[1] + "," + event.values[2] + "\n"
+ "Gravity values : \n"
+ " x,y,z = "+ gravity[0] + "," + gravity[1] + "," + gravity[2] + "\n"
+ "Motion values : \n"
+ " x,y,z = "+ motion[0] + "," + motion[1] + "," + motion[2] + "\n"
+ "Y軸角度 :" + angle );
tv.invalidate();
counter = 1;
}
}
……
motion[3]是過濾重力後的數值,如果各值如果非常接近0,表示裝置沒有被移動。
相關小例子程式碼:Pro Android學習:感測器小例子