陀螺仪

陀螺仪(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); 

    } 

}

Pro Android学习笔记(一五四):传感器(4):陀螺仪、加速传感器_顺时针

加速度传感器(acceleration)

Pro Android学习笔记(一五四):传感器(4):陀螺仪、加速传感器_android_02

加速度测量传感器有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。减去重力这很重要,当设备静止或者匀速运动时,可以获得设备的角度。

Pro Android学习笔记(一五四):传感器(4):陀螺仪、加速传感器_顺时针_03

我记得至少两年前,单位请一互联网专家讲课,提到了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轴与垂直地面方向(重力反方向)的夹角。相关的代码片段如下:

Pro Android学习笔记(一五四):传感器(4):陀螺仪、加速传感器_ide_04……


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,表示设备没有被移动。