Направление компаса зависит от ориентации телефона

Моему приложению дополненной реальности нужен компасный азимут камеры, и есть много примеров получения направления от диспетчера датчиков.

Однако я обнаружил, что результирующее значение отличается в зависимости от ориентации телефона: альбомный режим, повернутый вправо, примерно на 10 градусов отличается от горизонтального, повернутого влево (разница между ROTATION_0 и ROTATION_180 меньше, но все же отличается). Этой разницы достаточно, чтобы испортить любой эффект дополненной реальности.

Это как-то связано с калибровкой? (Я не уверен, что правильно делаю цифру 8 - я пробовал разные способы, которые нашел на YouTube).

Есть идеи, почему есть разница? Я напутал с матрицей вращения? У меня есть возможность ограничить приложение одной ориентацией, но меня все еще беспокоит, что показания компаса все еще не очень точны (хотя после фильтрации они довольно стабильны)

public void onSensorChanged(SensorEvent event) {
        if (event.accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) {
            return;
        }

        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)  mGravity = event.values;
        if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) mGeomagnetic = event.values;

        if (mGravity != null && mGeomagnetic != null) {

            float[] rotationMatrixA = mRotationMatrixA;
            if (SensorManager.getRotationMatrix(rotationMatrixA, null, mGravity, mGeomagnetic)) {

                float[] rotationMatrixB = mRotationMatrixB;

                Display display = getWindowManager().getDefaultDisplay(); 
                int deviceRot = display.getRotation();

                switch (deviceRot)
                {
                // portrait - normal
                case Surface.ROTATION_0: SensorManager.remapCoordinateSystem(rotationMatrixA,
                        SensorManager.AXIS_X, SensorManager.AXIS_Z,
                        rotationMatrixB);
                break;
                // rotated left (landscape - keys to bottom)
                case Surface.ROTATION_90: SensorManager.remapCoordinateSystem(rotationMatrixA,
                        SensorManager.AXIS_Z, SensorManager.AXIS_MINUS_X,
                        rotationMatrixB); 
                break;
                // upside down
                case Surface.ROTATION_180: SensorManager.remapCoordinateSystem(rotationMatrixA,
                        SensorManager.AXIS_X, SensorManager.AXIS_Z,
                        rotationMatrixB); 
                break;
                // rotated right
                case Surface.ROTATION_270: SensorManager.remapCoordinateSystem(rotationMatrixA,
                        SensorManager.AXIS_MINUS_Z, SensorManager.AXIS_X,
                        rotationMatrixB); 
                break;

                default:  break;
                }

                float[] dv = new float[3]; 
                SensorManager.getOrientation(rotationMatrixB, dv);
                // add to smoothing filter
                fd.AddLatest((double)dv[0]); 
            }
            mDraw.invalidate();     
        }
    }



Ответы (2)


Попробуйте это

public void onSensorChanged(SensorEvent event) {
    if (event.accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) {
        return;
    }

    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)  mGravity = event.values.clone ();
    if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) mGeomagnetic =  event.values.clone ();

    if (mGravity != null && mGeomagnetic != null) {

        float[] rotationMatrixA = mRotationMatrixA;
        if (SensorManager.getRotationMatrix(rotationMatrixA, null, mGravity, mGeomagnetic)) {

            float[] rotationMatrixB = mRotationMatrixB;
            SensorManager.remapCoordinateSystem(rotationMatrixA,
                    SensorManager.AXIS_X, SensorManager.AXIS_Z,
                    rotationMatrixB);
            float[] dv = new float[3]; 
            SensorManager.getOrientation(rotationMatrixB, dv);
            // add to smoothing filter
            fd.AddLatest((double)dv[0]); 
        }
        mDraw.invalidate();     
    }
}

Вам не нужен оператор switch, кажется, есть много путаницы в отношении getRotationMatrix, remapCoordinateSystem и getOrientation из вопросов stackoverflow.
Я, вероятно, напишу подробное объяснение этого в ближайшем будущем.

person Hoan Nguyen    schedule 21.05.2012
comment
Мужчина! Я искал ТАК более 2 часов, и есть много НЕПРАВИЛЬНЫХ ответов. Ваш - первый, который действительно работает правильно для меня. Спасибо за это. - person BoD; 27.08.2013
comment
Я скопировал и отредактировал пост. Я не знал, что у Mush было mGravity = event.values, это должно быть event.values.clone(). То же самое для mGeo Magnetic - person Hoan Nguyen; 27.08.2013
comment
SensorManager.remapCoordinateSystem это решение. Спасибо. - person Douglas Nassif Roma Junior; 18.06.2015
comment
Ответ @Mark Kranzler неверен. Вопрос задает направление обзора камеры, которое является направлением оси устройства -z. getOrientation возвращает азимут как направление оси Y. Таким образом, вам нужна remapCoordinateSystem. Ось z устройства указывает в одном и том же направлении независимо от ориентации устройства. То есть ось z указывает в одном и том же направлении, независимо от того, находится ли устройство в портретной, альбомной или любой другой промежуточной ориентации. Таким образом, нет необходимости учитывать ориентацию устройства. - person Hoan Nguyen; 18.06.2015
comment
Этот ответ правильный, если ваш телефон находится в альбомной ориентации, а экран устройства указывает на вас (AR для альбомной ориентации работает нормально). Если ваш телефон лежит на столе в альбомной ориентации, он не работает. Хоан Ньюген, я также прочитал почти каждый ваш пост о компасе и проверил код dsensor, это действительно поучительно. Можете ли вы поделиться, как реализовать портретную и альбомную ориентацию, когда устройство лежит горизонтально или экран перпендикулярен земле? - person Thracian; 08.05.2017
comment
Если телефон плоский то координату не переназначаешь. Здесь происходит то, что значение [0] getOrientation — это угол между осью Y устройства и магнитным севером. Когда вы переназначаете координату, как указано выше, значение [0] в getOrientation представляет собой угол между отрицательной осью z устройства и магнитным севером. Если телефон лежит плашмя, то минус оси z устройства указывает на небо или землю, и поэтому говорить об угле между ним и магнитным севером не имеет смысла. - person Hoan Nguyen; 10.05.2017

Ответ Хоана на самом деле неверен, потому что он не учитывает поворот дисплея. Это правильный ответ.

person Matt Kranzler    schedule 15.04.2014