如何获得正确的方位角(磁方位)而不受屏幕方向的影响?

4
我希望获得当前的磁性方向,而不考虑当前屏幕方向(横屏或竖屏)。
我找到了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[]数组的其他值?还是我需要“重新映射坐标”?所以那个是实现这一目标的正确方式吗?
我正在为具有360°屏幕旋转且API级别为11+的设备开发。
我知道这些问题经常被问到,但我无法将它们的答案简单地转移到我的问题上。

我遇到了与此处发布的问题相同的问题:https://dev59.com/imgt5IYBdhLWcg3w2Q-U 但是那个答案对我没用。也许你会更幸运。 - Ridcully
你还需要吗?因为也许我们需要根据屏幕旋转使用事件值数组的其他值? - felixd
不,我取消了这个项目(一些AR内容),因为事实证明大多数Android设备的旋转传感器不够精确,无法满足我的预期。 - Ridcully
3个回答

8

好的,我终于成功让代码运行起来了!

首先,我注册了一个 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)");
    }
}

我使用SensorEventListener进行计算:

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度,这样当前测量方位角度将会有15度的差别。这是你想要的吗? - Hoan Nguyen
是的,就是这样。例如。 - felixd
嘿,SMOOTHING_FACTOR_COMPASS的值是多少? - Andres Rodriguez
你能添加所有的import和const吗? - Cristiano Coelho

4
Sensor.TYPE_ORIENTATION已被弃用,仅适用于设备平放的情况。使用Sensor.TYPE_ORIENTATION时,方位角是设备Y轴指向的方向。因此,如果设备竖直拿着,使用Y轴作为方位角的指向是没有意义的。只有计算后置摄像头指向的方向才有意义。要找到这个方向,您必须使用Sensor.TYPE_MAGNETIC_FIELDSensor.TYPE_GRAVITYSensor.TYPE_ACCELEROMETER。如果使用Sensor.TYPE_ACCELEROMETER,则必须过滤加速度计值。
使用这些传感器,您可以调用getRotationMatrix,然后在调用getOrientation之前调用remapCoordinateSystem(inR, AXIS_X, AXIS_Z, outR)。为了获得稳定的方向,您应该保留方向的历史记录,然后计算平均值。有关使用Sensor.TYPE_GRAVITY实现的示例,请参见Android getOrientation Azimuth gets polluted when phone is tilted

谢谢您提供的解决方案。现在我将根据此编写代码。 - felixd

0

我认为这段代码可能会对你有所帮助:

    //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;
}

感谢您的代码,很抱歉回复晚了。但是这只返回屏幕方向作为整数(以“ActivityInfo.SCREEN_ORIENTATION_XX”样式)。这是非常有用的代码部分,但您是否也知道如何使用旋转矩阵? - felixd
你能帮我解决这个问题吗? - Skizo-ozᴉʞS ツ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接