无论屏幕方向如何,如何获得正确的方位(磁方向)?

Posted

技术标签:

【中文标题】无论屏幕方向如何,如何获得正确的方位(磁方向)?【英文标题】:How do I get the correct bearing (magnetic orientation) regardless of screen orientation? 【发布时间】:2013-08-12 07:51:22 【问题描述】:

无论当前屏幕方向(横向或纵向)如何,我都想获取当前的磁方向。

我找到了this 示例,但它与方向无关,对吧? this 也没有帮助我。我也读过http://android-developers.blogspot.de/2010/09/one-screen-turn-deserves-another.html

这是我目前不推荐使用的方法,我不想使用(短):

mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);

private SensorEventListener sensorEventListener = new SensorEventListener() 

    public void onSensorChanged(SensorEvent event) 

        /* Get measured value */
        float current_measured_bearing = (float) event.values[0];

        /* Compensate device orientation */
        switch (((WindowManager) getSystemService(WINDOW_SERVICE))
                .getDefaultDisplay().getRotation()) 
        case Surface.ROTATION_90:
            current_measured_bearing = current_measured_bearing + 90f;
            break;
        case Surface.ROTATION_180:
            current_measured_bearing = current_measured_bearing - 180f;
            break;
        case Surface.ROTATION_270:
            current_measured_bearing = current_measured_bearing - 90f;
            break;
        

但最后一部分肯定是错的!在这种情况下,如何正确使用较新的方法getRotationMatrix()? (与方向无关)或者我是否只需要使用基于旋转矩阵的 event.values[] 数组的其他值?还是我需要“重新映射坐标”?那么that 是实现这一目标的正确方法吗?

我正在为具有 360° 屏幕旋转和 API 级别 11+ 的设备进行开发。

我知道这些问题经常被问到,但我无法将他们的答案转移到我的问题中。

【问题讨论】:

我在这里发布了同样的问题:***.com/questions/11772923/… 但答案对我不起作用。也许你有更多的运气。 你还需要吗?因为也许我们“必须使用 event.values[] 数组的其他值?”基于屏幕旋转? 不,我取消了这个项目(一些 AR 的东西),因为事实证明大多数 Android 设备的旋转传感器都不够精确。 【参考方案1】:

好的,我终于设法让代码工作了!

首先,我注册了Sensor.TYPE_MAGNETIC_FIELDSensor.TYPE_GRAVITY:(就像Hoan Nguyen 说的!)

/**
 * Initialize the Sensors (Gravity and magnetic field, required as a compass
 * sensor)
 */
private void initSensors() 

    LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
    SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
    Sensor mSensorGravity = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
    Sensor mSensorMagneticField = sensorManager
            .getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);

    /* Initialize the gravity sensor */
    if (mSensorGravity != null) 
        Log.i(TAG, "Gravity sensor available. (TYPE_GRAVITY)");
        sensorManager.registerListener(mSensorEventListener,
                mSensorGravity, SensorManager.SENSOR_DELAY_GAME);
     else 
        Log.i(TAG, "Gravity sensor unavailable. (TYPE_GRAVITY)");
    

    /* Initialize the magnetic field sensor */
    if (mSensorMagneticField != null) 
        Log.i(TAG, "Magnetic field sensor available. (TYPE_MAGNETIC_FIELD)");
        sensorManager.registerListener(mSensorEventListener,
                mSensorMagneticField, SensorManager.SENSOR_DELAY_GAME);
     else 
        Log.i(TAG,
                "Magnetic field sensor unavailable. (TYPE_MAGNETIC_FIELD)");
    

我使用 SensorEventListner 进行计算:

private SensorEventListener mSensorEventListener = new SensorEventListener() 

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) 
    

    @Override
    public void onSensorChanged(SensorEvent event) 

        if (event.sensor.getType() == Sensor.TYPE_GRAVITY) 

            mGravity = event.values.clone();

         else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) 

            mMagnetic = event.values.clone();

        

        if (mGravity != null && mMagnetic != null) 

            /* Create rotation Matrix */
            float[] rotationMatrix = new float[9];
            if (SensorManager.getRotationMatrix(rotationMatrix, null,
                    mGravity, mMagnetic)) 

                /* Compensate device orientation */
                // http://android-developers.blogspot.de/2010/09/one-screen-turn-deserves-another.html
                float[] remappedRotationMatrix = new float[9];
                switch (getWindowManager().getDefaultDisplay()
                        .getRotation()) 
                case Surface.ROTATION_0:
                    SensorManager.remapCoordinateSystem(rotationMatrix,
                            SensorManager.AXIS_X, SensorManager.AXIS_Y,
                            remappedRotationMatrix);
                    break;
                case Surface.ROTATION_90:
                    SensorManager.remapCoordinateSystem(rotationMatrix,
                            SensorManager.AXIS_Y,
                            SensorManager.AXIS_MINUS_X,
                            remappedRotationMatrix);
                    break;
                case Surface.ROTATION_180:
                    SensorManager.remapCoordinateSystem(rotationMatrix,
                            SensorManager.AXIS_MINUS_X,
                            SensorManager.AXIS_MINUS_Y,
                            remappedRotationMatrix);
                    break;
                case Surface.ROTATION_270:
                    SensorManager.remapCoordinateSystem(rotationMatrix,
                            SensorManager.AXIS_MINUS_Y,
                            SensorManager.AXIS_X, remappedRotationMatrix);
                    break;
                

                /* Calculate Orientation */
                float results[] = new float[3];
                SensorManager.getOrientation(remappedRotationMatrix,
                        results);

                /* Get measured value */
                float current_measured_bearing = (float) (results[0] * 180 / Math.PI);
                if (current_measured_bearing < 0) 
                    current_measured_bearing += 360;
                

                /* Smooth values using a 'Low Pass Filter' */
                current_measured_bearing = current_measured_bearing
                        + SMOOTHING_FACTOR_COMPASS
                        * (current_measured_bearing - compass_last_measured_bearing);

                /* Update normal output */
                visual_compass_value.setText(String.valueOf(Math
                        .round(current_bearing))
                        + getString(R.string.degrees));

                /*
                 * Update variables for next use (Required for Low Pass
                 * Filter)
                 */
                compass_last_measured_bearing = current_measured_bearing;

            
        
    
;

【讨论】:

你想在这里实现什么?将您的设备保持在纵向模式,然后将设备旋转 15 度,current_measured_bearing 将相差 15 度。这是你想要的吗? 是的,例如。 嘿,SMOOTHING_FACTOR_COMPASS 的值是多少?? 你能添加所有的导入和常量吗?【参考方案2】:

Sensor.TYPE_ORIENTATION 已折旧,仅在设备平坦时才有效。使用Sensor.TYPE_ORIENTATION 时,方位角(方位角)是设备Y-axis 指向的方向。因此,如果设备保持垂直,Y-axis 用作轴承的方向没有意义。只有计算后置摄像头指向的方向才有意义。要找到这个方向,您必须使用Sensor.TYPE_MAGNETIC_FIELDSensor.TYPE_GRAVITYSensor.TYPE_ACCELEROMETER。如果使用Sensor.TYPE_ACCELEROMETER,则必须过滤加速度计值。 使用这些传感器,您可以先调用getRotationMatrix,然后再调用remapCoordinateSystem(inR, AXIS_X, AXIS_Z, outR),然后再调用getOrientation。要获得稳定的方向,您应该保留方向的历史记录,然后计算平均值。对于使用 TYPE_GRAVITY 的实现,请检查 Android getOrientation Azimuth gets polluted when phone is tilted

【讨论】:

感谢您提供此解决方案。所以现在我要根据这个来写代码。【参考方案3】:

我认为这段代码可以帮助你:

    //get orientation
private int getScreenOrientation() 
    int rotation = getWindowManager().getDefaultDisplay().getRotation();
    DisplayMetrics dm = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(dm);
    int width = dm.widthPixels;
    int height = dm.heightPixels;
    int orientation;
    // if the device's natural orientation is portrait:
    if ((rotation == Surface.ROTATION_0
            || rotation == Surface.ROTATION_180) && height > width ||
        (rotation == Surface.ROTATION_90
            || rotation == Surface.ROTATION_270) && width > height) 
        switch(rotation) 
            case Surface.ROTATION_0:
                orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
                break;
            case Surface.ROTATION_90:
                orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
                break;
            case Surface.ROTATION_180:
                orientation =
                    ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
                break;
            case Surface.ROTATION_270:
                orientation =
                    ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
                break;
            default:
                Log.e(TAG, "Unknown screen orientation. Defaulting to " +
                        "portrait.");
                orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
                break;              
        
    
    // if the device's natural orientation is landscape or if the device
    // is square:
    else 
        switch(rotation) 
            case Surface.ROTATION_0:
                orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
                break;
            case Surface.ROTATION_90:
                orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
                break;
            case Surface.ROTATION_180:
                orientation =
                    ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
                break;
            case Surface.ROTATION_270:
                orientation =
                    ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
                break;
            default:
                Log.e(TAG, "Unknown screen orientation. Defaulting to " +
                        "landscape.");
                orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
                break;              
        
    

    return orientation;

【讨论】:

感谢您的代码,并为这么晚的答案感到抱歉,但这只会将屏幕方向返回为 int(在ActivityInfo.SCREEN_ORIENTATION_XX 样式中)。这是一段非常有用的代码,但是你也知道如何使用旋转矩阵吗? 你能帮我解决这个question吗?

以上是关于无论屏幕方向如何,如何获得正确的方位(磁方向)?的主要内容,如果未能解决你的问题,请参考以下文章

两点之间的方位角+地球曲率问题

无论手机方向如何,都获得垂直于重力方向的加速度。

JS 如何获取和监听屏幕方向的改变?

如何更改屏幕方向并获得更改的画布大小

Rk3288 7.1.1安卓源码设置屏幕竖屏或横屏

如何在 TextView 中使用方向箭头