Android平台提供了两种传感器让我们可以确定设备的位置: 地磁传感器和方向传感器。 Android还提供了一种传感器让我们可以决定人脸离手机多近的时候关闭屏幕(距离传感器proximity sensor)。 地磁传感器和距离传感器都是基于硬件的。 大多数手持设备供应商都有提供一个地磁传感器。 同样, 手持设备制造商通常包含一个距离传感器来在通话的时候决定何时关闭屏幕。 方向传感器(orientation sensor)是基于软件的, 它通过加速度传感器和地磁传感器来计算数据。 但是方向传感器在Android2.2中已经不推荐使用。
位置传感器在确定设备在世界中所处的位置时会很有用。 比如我们可以使用地磁传感器跟加速传感器合作来决定设备相对于地磁北极的位置。 我们还可以使用方向传感器(或者基于传感器的方向方法)来确定设备相对于APP框架为参考的位置。 位置传感器通常不用于监测设备移动或者运动, 比如摇动, 倾斜等。
地磁传感器和方向传感器通过SensorEvent的多维数组返回数据。 栗如, 方向传感器在每次返回传感器事件的时候提供了地磁力在三维空间的强度值。 同样方向传感器则提供了方位角(Yaw偏航角), 俯仰角(pitch)和翻滚角(roll)。 下表提供了Android平台各位置传感器的信息:
SensorEvent.values[0]
沿x轴旋转矢量分量(x*sin(θ/2 ))。
无单位
SensorEvent.values[1]
沿y轴旋转矢量分量(y*sin(θ/2 ))。
SensorEvent.values[2]
沿z轴旋转矢量分量(z*sin(θ/2 ))。
TYPE_GEOMAGNETIC_ROTATION_VECTOR
SensorEvent.values[0]
沿x轴旋转矢量分量(x*sin(θ/2 ))。
无单位
SensorEvent.values[1]
沿y轴旋转矢量分量(y*sin(θ/2 ))。
SensorEvent.values[2]
沿z轴旋转矢量分量(z*sin(θ/2 ))。
TYPE_MAGNETIC_FIELD
SensorEvent.values[0]
沿x轴的地磁强度
μT
SensorEvent.values[1]
沿y轴的地磁强度
SensorEvent.values[2]
沿z轴的地磁强度
TYPE_MAGNETIC_FIELD_UNCALIBRATED
SensorEvent.values[0]
沿x轴的地磁强度(无硬铁校准hard iron calibration)
μT
SensorEvent.values[1]
沿y轴的地磁强度(无硬铁校准hard iron calibration)
SensorEvent.values[2]
沿z轴的地磁强度(无硬铁校准hard iron calibration)
SensorEvent.values[3]
沿x轴铁偏差校准(Iron bias estimation)
SensorEvent.values[4]
沿y轴铁偏差校准(Iron bias estimation)
SensorEvent.values[5]
沿z轴铁偏差校准(Iron bias estimation)
TYPE_ORIENTATION①
SensorEvent.values[0]
方位角(绕z轴的角度)
度
SensorEvent.values[1]
俯仰角(pitch) (绕x轴的角度)
SensorEvent.values[2]
翻滚角(roll) (绕y轴的角度)
TYPE_PROXIMITY
SensorEvent.values[0]
与对象的距离②
cm
① 该传感器在Android2.2版本中不再推荐使用。 Sensor framework提供了备用的方法, 下文会有介绍。
② 一些距离传感器只提供二进制数据代表远和近。
使用游戏旋转矢量传感器:
游戏旋转矢量传感器跟旋转矢量传感器是相同的, 除了它不使用地磁场。 因此Y轴不指向北边而是一些别的参考系。
因为游戏旋转矢量传感器不使用地磁场, 相关的方向因不受磁场影响而更加准确。 如果不在意北边在哪的话可以在游戏中使用该传感器, 这时候普通的旋转矢量就不合适了, 因为它依赖于磁场。 下面的代码演示了如何获取一个该传感器的实例:
private SensorManager mSensorManager;
private Sensor mSensor;
。..
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR);
使用地磁旋转矢量传感器:
地磁旋转矢量传感器跟旋转矢量传感器一样, 但是它使用地磁代替陀螺仪。 所以它的精确度会比普通旋转矢量传感器要低, 但是功耗也降低了。 应该只有当需要在后台获取旋转信息而不想要消耗太多电量的时候才使用它。 该传感器当与批处理(batching)一起是最有用的。
下面的代码演示了如何获取实例:
private SensorManager mSensorManager;
private Sensor mSensor;
。..
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR);
方向传感器:
方向传感器让我们可以监测设备相对于地球参考系的位置(特指地磁北极)。 下面代码演示了如何获取该传感器实例:
private SensorManager mSensorManager;
private Sensor mSensor;
。..
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
方向传感器通过使用设备的地磁场传感器和设备的加速度计合作获得它的数据。 通过使用这俩硬件传感器, 方向传感器可以提供这三个维度的数据:
l 方位角(绕z轴的角度)。 这个角度在地磁北极和设备的y轴之间。 比如如果设备的y轴对准地磁北极, 那么该值是0, 如果设备的y轴对准南极, 则该值为180. 同样的, 当y轴指向东边, 该值是90, 指向西边则为270.
l 俯仰角(pitch) (绕x轴的角度)。 处于z轴正方向和y轴正方向之间的时候该值是正的, z轴正方向和y轴负方向的时候, 该值是负的。 范围是180度~-180度。
l 翻滚角(roll) (绕y轴的角度)。 当处于z轴正方向和x轴正方向时该值为正。 Z轴正方向和x轴负方向的时候, 该值为负。 取值范围是90~-90度。
这个定义跟航空学中的方位角, 俯仰角和翻滚角是不一样的, 航空学中x轴表示沿飞机的长边(飞机尾部到头部)。 此外由于历史原因, 翻滚角在顺时针方向为正(数学上讲, 它应该在逆时针方向为正)。
方向传感器通过处理加速度计和地磁场传感器的数据来得到它自己的数据。 因为涉及的处理任务比较繁重, 所以精度和准确度被减少(只有当翻滚角分量为0的时候它的数据才可靠)。 因此, 方向传感器在Android2.2中就不推荐使用了。 官方推荐使用getRotationMatrix()方法和getOrientation()方法结合来计算方向值, 代替方向传感器。 我们还可以使用remapCoordinateSystem()方法来映射方向值到APP参考框架。 下面的代码演示了如何从方向传感器直接获得方向数据, 只有几乎没有翻滚角的时候才推荐这样使用:
public class SensorActivity extends Activity implements SensorEventListener {
private SensorManager mSensorManager;
private Sensor mOrientation;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mOrientation = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Do something here if sensor accuracy changes.
// You must implement this callback in your code.
}
@Override
protected void onResume() {
super.onResume();
mSensorManager.registerListener(this, mOrientation, SensorManager.SENSOR_DELAY_NORMAL);
}
@Override
protected void onPause() {
super.onPause();
mSensorManager.unregisterListener(this);
}
@Override
public void onSensorChanged(SensorEvent event) {
float azimuth_angle = event.values[0];
float pitch_angle = event.values[1];
float roll_angle = event.values[2];
// Do something with these orientation angles.
}
}
我们并不会经常用到处理方向传感器的原始数据。
使用地磁场传感器:
地磁场传感器让我们可以监测地球磁场的变化。 下面的代码展示如何获取它的实例:
private SensorManager mSensorManager;
private Sensor mSensor;
。..
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
该传感器提供了三维磁场的原始数据。 通常我们不需要直接使用该传感器, 而是使用旋转矢量传感器来确定旋转运动的原始数据, 或者我们还可以使用加速度计和地磁场传感器跟getRotationMatrix()方法合作获取旋转矩阵和倾角矩阵。 然后可以使用这些矩阵同getOrientation()和getInclination()方法来获得方位角和地磁倾角数据。
使用未校正的磁力计:
未校正的磁力计跟地磁场传感器相似, 但是它没有”硬铁校正”(hard iron calibration)。 工厂校正和温度校正依然应用于磁场。 未校正的磁力计在处理坏硬铁估计(bad hard iron estimations)的时候有用。 通常geomagneticsensor_event.value[0]将会接近uncalibrated_magnetometer_event.values[0]- uncalibrated_magnetometer_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_MAGNETIC_FIELD_UNCALIBRATED);
使用距离传感器(Proximity Sensor):
距离传感器让我们可以确定一个目标与设备的距离。 下面代码演示了如何获取它的实例:
private SensorManager mSensorManager;
private Sensor mSensor;
。..
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
距离传感器通常用来确定手持设备跟人脸的距离(比如用户接到电话或者在打电话的时候)。 大多数距离传感器返回绝对距离, 但是它们中的个别分子会返回”远/近”这样的信息。 下面的代码展示给我们如何使用这玩意儿:
public class SensorActivity extends Activity implements SensorEventListener {
private SensorManager mSensorManager;
private Sensor mProximity;
@Override
public final void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Get an instance of the sensor service, and use that to get an instance of
// a particular sensor.
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mProximity = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
}
@Override
public final void onAccuracyChanged(Sensor sensor, int accuracy) {
// Do something here if sensor accuracy changes.
}
@Override
public final void onSensorChanged(SensorEvent event) {
float distance = event.values[0];
// Do something with this sensor data.
}
@Override
protected void onResume() {
// Register a listener for the sensor.
super.onResume();
mSensorManager.registerListener(this, mProximity, SensorManager.SENSOR_DELAY_NORMAL);
}
@Override
protected void onPause() {
// Be sure to unregister the sensor when the activity pauses.
super.onPause();
mSensorManager.unregisterListener(this);
}
}