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

Моето приложение за разширена реалност се нуждае от посоката на компаса на изгледа на камерата и има много примери за получаване на посока от сензорния мениджър.

Въпреки това установявам, че получената стойност е различна в зависимост от ориентацията на телефона - пейзажът, завъртян надясно, е с около 10 градуса различен от пейзажа, завъртян наляво (разликата между ROTATION_0 и ROTATION_180 е по-малка, но все още е различна). Тази разлика е достатъчна, за да развали всеки AR ефект.

Има ли нещо общо с калибрирането? (Не съм убеден, че правя нещо с цифрата 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
човек! Търся SO повече от 2 часа и има много ГРЕШНИ отговори. Вашият е първият, който наистина работи правилно за мен. Благодаря за това. - person BoD; 27.08.2013
comment
Копирах и редактирах поста. Не разбрах, че Mush има mGravity = event.values, трябва да е event.values.clone(). Същото и за mGeomagnetic - 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