作者:裘德超
目前,大多数Android手机手机都内置了很多传感器,大家熟知的有重力传感器,亮度传感器,加速度传感器等。本文将为大家介绍Android系统中的运动传感器。
Android系统为用户提供了多个运动传感器,其中,加速度传感器和陀螺仪传感器经常是由硬件实现,而重力传感器,线形加速度传感器,旋转向量传感器一般是由软件实现出来的,由软件实现的模拟传感器从一个或多个硬件传感器中获取数据来模拟该虚拟传感器。对于每一个传感器事件,运动传感器都返回一个多维数组,其中包含了传感器感知的数据。下表总结了Android平台中的所有运动传感器:
与传感器有关的类主要包含在android.hardware包中。
SensorManager:可以用来管理Android设备中指出的所有的传感器。
Sensor:具体的传感器,例如重力传感器,陀螺仪传感器。
通过SensorManager类的registerListener方法可以监听某一个传感器,当被监测的传感器的检测到数据变化时就会产生一个事件,回调该传感器的监听器,我们可以在回调方法中执行相应的操作应对数据的变化。
下面是测试程序的布局:首先是一个用于显示当前机器包含的所有硬件传感器和软件传感器的列表。然后分别是重力传感器,加速度传感器(包括重力),线性加速度传感器(不包括重力),陀螺仪传感器,旋转向量传感器的检测到的数据的显示窗口。
res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:fadingEdge="vertical" android:scrollbars="vertical" > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" > <TextView android:id="@+id/metaPromt" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="metaPromt" /> <TextView android:id="@+id/gravityPromt" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="gravityPromt" /> <TextView android:id="@+id/accelerometerPromt" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="accelerometerPromt" /> <TextView android:id="@+id/linearAccelerometerPromt" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="linearAccelerometerPromt" /> <TextView android:id="@+id/gyroscopePromt" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="gyroscopePromt" /> <TextView android:id="@+id/rotationVectorPromt" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="rotationVectorPromt" /> </LinearLayout> </ScrollView>
src/org/reno/ MotionSensorsActivity.java
package org.reno; import java.util.List; import android.app.Activity; import android.content.Context; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; import android.widget.TextView; public class MotionSensorsActivity extends Activity { private SensorManager mSensorManager; private Sensor gravitySensor; private Sensor accelerometerSensor; private Sensor gyroscopeSensor; private Sensor linearAccelerometeSensor; private Sensor rotationVectorSensor; private TextView metaPromt; private TextView gravityPromt; private TextView accelerometerPromt; private TextView linearAccelerometerPromt; private TextView gyroscopePromt; private TextView rotationVectorPromt; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); metaPromt = (TextView) findViewById(R.id.metaPromt); // 从传感器管理器中获得全部的传感器列表 List<Sensor> allSensors = mSensorManager.getSensorList(Sensor.TYPE_ALL); // 显示有多少个传感器 metaPromt.setText("经检测该手机有" + allSensors.size() + "个传感器,他们分别是:\n"); // 显示每个传感器的具体信息 for (Sensor s : allSensors) { String tempString = "\n" + " 设备名称:" + s.getName() + "\n" + " 设备版本:" + s.getVersion() + "\n" + " 供应商:" + s.getVendor() + "\n"; switch (s.getType()) { case Sensor.TYPE_ACCELEROMETER: metaPromt.setText(metaPromt.getText().toString() + s.getType() + " 加速度传感器accelerometer" + tempString); break; case Sensor.TYPE_MAGNETIC_FIELD: metaPromt.setText(metaPromt.getText().toString() + s.getType() + " 电磁场传感器magnetic field" + tempString); break; case Sensor.TYPE_ORIENTATION: metaPromt.setText(metaPromt.getText().toString() + s.getType() + " 方向传感器orientation" + tempString); break; case Sensor.TYPE_GYROSCOPE: metaPromt.setText(metaPromt.getText().toString() + s.getType() + " 陀螺仪传感器gyroscope" + tempString); break; case Sensor.TYPE_LIGHT: metaPromt.setText(metaPromt.getText().toString() + s.getType() + " 环境光线传感器light" + tempString); break; case Sensor.TYPE_PRESSURE: metaPromt.setText(metaPromt.getText().toString() + s.getType() + " 压力传感器pressure" + tempString); break; case Sensor.TYPE_TEMPERATURE: metaPromt.setText(metaPromt.getText().toString() + s.getType() + " 温度传感器temperature" + tempString); break; case Sensor.TYPE_PROXIMITY: metaPromt.setText(metaPromt.getText().toString() + s.getType() + " 距离传感器proximity" + tempString); break; case Sensor.TYPE_GRAVITY: metaPromt.setText(metaPromt.getText().toString() + s.getType() + " 重力传感器gravity" + tempString); break; case Sensor.TYPE_ROTATION_VECTOR: metaPromt.setText(metaPromt.getText().toString() + s.getType() + " 旋转向量传感器:RotationVector" + tempString); break; default: metaPromt.setText(metaPromt.getText().toString() + s.getType() + " 未知传感器" + tempString); break; } } gravitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY); linearAccelerometeSensor = mSensorManager .getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION); accelerometerSensor = mSensorManager .getDefaultSensor(Sensor.TYPE_ACCELEROMETER); gyroscopeSensor = mSensorManager .getDefaultSensor(Sensor.TYPE_GYROSCOPE); rotationVectorSensor = mSensorManager .getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR); gravityPromt = (TextView) findViewById(R.id.gravityPromt); accelerometerPromt = (TextView) findViewById(R.id.accelerometerPromt); linearAccelerometerPromt = (TextView) findViewById(R.id.linearAccelerometerPromt); gyroscopePromt = (TextView) findViewById(R.id.gyroscopePromt); rotationVectorPromt = (TextView) findViewById(R.id.rotationVectorPromt); mSensorManager.registerListener(new SensorEventListener() { float[] gravity = new float[3]; float[] linear_acceleration = new float[3]; @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } @Override 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.8f; // 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]; gravityPromt.setText("gravityPromt: x=" + (int) linear_acceleration[0] + "," + "y=" + (int) linear_acceleration[1] + "," + "z=" + (int) linear_acceleration[2]); } }, gravitySensor, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(new SensorEventListener() { float[] gravity = new float[3]; float[] linear_acceleration = new float[3]; @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } @Override 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.8f; // 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]; accelerometerPromt.setText("accelerometerPromt: x=" + (int) linear_acceleration[0] + "," + "y=" + (int) linear_acceleration[1] + "," + "z=" + (int) linear_acceleration[2]); } }, accelerometerSensor, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(new SensorEventListener() { @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } @Override public void onSensorChanged(SensorEvent event) { linearAccelerometerPromt.setText("linearAccelerometerPromt: x=" + (int) event.values[0] + "," + "y=" + (int) event.values[1] + "," + "z=" + (int) event.values[2]); } }, linearAccelerometeSensor, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(new SensorEventListener() { // 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; @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // TODO Auto-generated method stub } @Override public void onSensorChanged(SensorEvent event) { // This timestep's delta rotation to be multiplied by the // current rotation // after computing it from the gyro sample data. if (timestamp != 0) { final float dT = (event.timestamp - timestamp) * NS2S; // Axis of the rotation sample, not normalized yet. float axisX = event.values[0]; float axisY = event.values[1]; float axisZ = event.values[2]; // Calculate the angular speed of the sample float omegaMagnitude = (float) Math.sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ); // Integrate around this axis with the angular speed by the // timestep // in order to get a delta rotation from this sample over // the timestep // We will convert this axis-angle representation of the // delta rotation // into a quaternion before turning it into the rotation // matrix. float thetaOverTwo = omegaMagnitude * dT / 2.0f; float sinThetaOverTwo = (float) Math.sin(thetaOverTwo); float cosThetaOverTwo = (float) Math.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); String promt = ""; for (int i = 0; i < deltaRotationMatrix.length; i++) { promt += deltaRotationMatrix[i] + "\n"; } gyroscopePromt.setText("gyroscopePromt: \n" + promt); // User code should concatenate the delta rotation we computed // with the current rotation // in order to get the updated rotation. // rotationCurrent = rotationCurrent * deltaRotationMatrix; } }, gyroscopeSensor, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(new SensorEventListener() { private final float[] deltaRotationVector = new float[4]; @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } @Override public void onSensorChanged(SensorEvent event) { deltaRotationVector[0] = event.values[0]; deltaRotationVector[1] = event.values[1]; deltaRotationVector[2] = event.values[2]; // deltaRotationVector[3] = event.values[3]; float[] deltaRotationMatrix = new float[9]; SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector); String promt = ""; for (int i = 0; i < deltaRotationMatrix.length; i++) { promt += deltaRotationMatrix[i] + "\n"; } rotationVectorPromt.setText("rotationVectorPromt: \n" + promt); } }, rotationVectorSensor, SensorManager.SENSOR_DELAY_GAME); } }
程序首先调用
(SensorManager)getSystemService(Context.SENSOR_SERVICE);
获取系统的传感器服务并且赋值给传感器管理器,然后通过调用
mSensorManager.getSensorList(Sensor.TYPE_ALL);
获取系统中所有类型的传感器。然后遍历所有的传感器,并且显示传感器的名称,制造商等信息。
然后调用mSensorManager.registerListener()方法监听某个传感器,当传感器检测到变化时会自动调用所注册的监听器的onSensorChanged(SensorEvent event)方法,其中event变量中包含了传感器检测到的数据,关于各个数据的作用见上文中的表一。
关于坐标系:
手机放桌面上,把手机往左边移动,那么x轴上的加速度为正,把手机往前移动,那么y轴的加速度为正,把手机往空中向上移动,那么z轴的加速度为正。
故对于加速度传感器,由于传感器默认当做自由落体运动时的加速度为0,所以手机静止时,加速度传感器返回的数据为0 –(-9.8)(即重力加速度g)。当手机以a的加速度向上移动时,加速度返回的数据为a-(-9.8)。然而由于我们的意识中当手机静止时的加速度应该为0。所以我们需要从系统返回的数据中过滤掉重力的作用。
关于陀螺仪传感器,由于传感器返回的event变量中的event.values数组中返回的分别是x, y, z轴中的角速度。所以我们需要算出这三个角速度的算数平方和的根,从而得出整体的角速度。然后把这个角速度乘以上次测量和本次测量之间的时间,算出手机总共旋转的角度,然后通过公式:
x*sin(θ/2)
y*sin(θ/2)
z*sin(θ/2)
算出手机在各个轴上旋转的角度。
执行效果:
由于本程序中同时监听的传感器过多,并且陀螺仪传感器和旋转向量传感器中涉及到了复杂的浮点数运算,所以会比较卡。
SourceCode下载链接:
https://github.com/renoqiu/MotionSensorTest
参考链接:
http://developer.android.com/guide/topics/sensors/sensors_motion.html
http://disanji.net/2011/02/21/android-sensor-programming-1/