Различные значения между датчиками TYPE_ACCELEROMETER / TYPE_MAGNETIC_FIELD и TYPE_ORIENTATION - PullRequest
14 голосов
/ 13 ноября 2010

Есть 2 способа получить 3 значения поворота (азимут, шаг, крен).

Каждый регистрирует слушателя типа TYPE_ORIENTATION.Это самый простой способ, и я получаю правильный диапазон значений для каждого поворота, поскольку в документации написано: azimuth: [0, 359] шаг: [-180, 180] roll: [-90, 90]

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

азимут: [-180, 180].-180/180 - это S, 0 i N, 90 E и -90 W.шаг: [-90, 90].90 - это 90, -90 - это -90, 0 - это 0, но -180/180 (лежащий с экраном вниз) - 0.roll: [-180, 180].

Я должен получить те же значения, но с десятичными числами, верно?

У меня есть следующий код:

aValues = new float[3];
mValues = new float[3];

sensorListener = new SensorEventListener (){
    public void onSensorChanged (SensorEvent event){
        switch (event.sensor.getType ()){
            case Sensor.TYPE_ACCELEROMETER:
                aValues = event.values.clone ();
                break;
            case Sensor.TYPE_MAGNETIC_FIELD:
                mValues = event.values.clone ();
                break;
        }

        float[] R = new float[16];
        float[] orientationValues = new float[3];

        SensorManager.getRotationMatrix (R, null, aValues, mValues);
        SensorManager.getOrientation (R, orientationValues);

        orientationValues[0] = (float)Math.toDegrees (orientationValues[0]);
        orientationValues[1] = (float)Math.toDegrees (orientationValues[1]);
        orientationValues[2] = (float)Math.toDegrees (orientationValues[2]);

        azimuthText.setText ("azimuth: " + orientationValues[0]);
        pitchText.setText ("pitch: " + orientationValues[1]);
        rollText.setText ("roll: " + orientationValues[2]);
    }

    public void onAccuracyChanged (Sensor sensor, int accuracy){}
};

Пожалуйста, помогите,Это очень расстраивает.

Должен ли я обращаться с этими значениями или я что-то не так делаю?

Спасибо.

Ответы [ 3 ]

21 голосов
/ 19 июля 2012

Я знаю, что я играю здесь с некромантом, но в последнее время я много над этим работаю, поэтому я решил добавить 2 throw.

Устройство не содержит компаса или инклинометра, поэтому оно не измеряет азимут, шаг или крен непосредственно. (Мы называем эти углы Эйлера, кстати). Вместо этого он использует акселерометры и магнитометры, которые создают трехмерные векторы XYZ. Они используются для вычисления значений азимута и т. Д.

Векторы находятся в координатном пространстве устройства:

Device coordinates

Координаты мира: Y обращен на север, X направлен на восток и Z направлен вверх:

World coordinates

Таким образом, «нейтральная» ориентация устройства лежит на спине на столе, верхняя часть устройства обращена на север.

Акселерометр выдает вектор в направлении "ВВЕРХ". Магнитометр создает вектор в «северном» направлении. (Обратите внимание, что в северном полушарии это имеет тенденцию указывать вниз из-за магнитного падения .)

Вектор акселерометра и вектор магнитометра можно математически объединить с помощью SensorManager.getRotationMatrix (), который возвращает матрицу 3x3, которая отображает векторы в координатах устройства на мировые координаты или наоборот. Для устройства в нейтральном положении эта функция вернет единичную матрицу.

Эта матрица не зависит от ориентации экрана. Это означает, что ваше приложение должно знать ориентацию и получать соответствующую компенсацию.

SensorManager.getOrientation () принимает матрицу преобразования и вычисляет значения азимута, шага и крена. Они взяты относительно устройства в нейтральном положении.

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

Если устройство наклонено вверх на 90 ° или около него, использование углов Эйлера не работает. Это вырожденный случай математически. В этой области, как устройство должно знать, меняете ли вы азимут или крен?

Функция SensorManager.remapCoordinateSystem () может использоваться для манипулирования матрицей преобразования, чтобы компенсировать то, что вы, возможно, знаете об ориентации устройства. Тем не менее, мои эксперименты показали, что это не охватывает все случаи, даже некоторые из распространенных. Например, если вы хотите переназначить устройство в вертикальном положении (например, сфотографировать), вам нужно умножить матрицу преобразования на эту матрицу:

1 0 0
0 0 1
0 1 0

перед вызовом getOrientation (), и это не одно из переназначений ориентации, которые поддерживает remapCoordinateSystem () [кто-то, пожалуйста, исправьте меня, если я что-то здесь упустил].

ОК, так что все это было многословным способом сказать, что если вы используете ориентацию, либо из датчика TYPE_ORIENTATION или из getOrientation (), вы, вероятно, делаете это неправильно. Единственный раз, когда вы на самом деле хотите получить углы Эйлера, - это отображать информацию об ориентации в удобной для пользователя форме, комментировать фотографию, управлять дисплеем летательного аппарата или что-то подобное.

Если вы хотите выполнять вычисления, связанные с ориентацией устройства, вам почти наверняка лучше использовать матрицу преобразования и работать с векторами XYZ.

Работая консультантом, всякий раз, когда кто-то приходит ко мне с проблемой, касающейся углов Эйлера, я делаю резервную копию и спрашиваю их, что они действительно пытаются сделать, а затем находит способ сделать это векторы вместо.

Оглядываясь на исходный вопрос, getOrientation () должна вернуть три значения в [-180 180] [-90 90] и [-180 180] (после преобразования в радианы). На практике мы думаем об азимуте как числа в [0 360), поэтому вам просто нужно добавить 360 к любым полученным отрицательным числам. Ваш код выглядит правильно, как написано. Было бы полезно, если бы я точно знал, каких результатов вы ожидаете и что вместо этого получаете.

Отредактировано, чтобы добавить: еще пара мыслей.В современных версиях Android используется так называемое «объединение датчиков», что в основном означает, что все доступные входные данные - акселерометр, магнитометр, гироскоп - объединены в математический черный ящик (обычно это фильтр Калмана, но зависит от поставщика).Все различные датчики - ускорение, магнитное поле, гироскопы, гравитация, линейное ускорение и ориентация - принимаются в качестве выходных данных из этого черного ящика.

Когда бы ни было возможно, вы должны использовать TYPE_GRAVITY вместо TYPE_ACCELEROMETER в качествеввод для getRotationMatrix ().

7 голосов
/ 14 мая 2012

Я мог бы снимать здесь в темноте, но если я правильно понимаю ваш вопрос, вам интересно, почему вы получаете [-179..179] вместо [0..360]?

Обратите внимание, что -180 соответствует +180 и 180 + N*360, где N - целое число (целое число).

Другими словами, если вы хотите получить те же числа, что и с датчиком ориентации, вы можете сделать это:

// x = orientationValues[0];
// y = orientationValues[1];
// z = orientationValues[2];
x = (x + 360.0) % 360.0;
y = (y + 360.0) % 360.0;
z = (z + 360.0) % 360.0;

Это даст вам значения в диапазоне [0..360], как вы хотели.

1 голос
/ 05 марта 2011

Вам не хватает одного критического вычисления в ваших вычислениях.
Вызов remapCoordinateSystem после того, как вы выполните getRotationMatrix .

Добавитьэто к вашему коду, и все будет хорошо.
Вы можете узнать больше об этом здесь .

...