Подтвердить что ты не робот

Как рассчитать азимут, шаг, ориентацию, когда мое устройство Android не плоское?

Я использую датчики силы тяжести и магнитного поля Android для расчета ориентации с помощью SensorManager.getRotationMatrix и SensorManager.getOrientation. Это дает мне азимут, шаг и ориентировочные числа. Результаты выглядят разумно, когда устройство лежит на столе.

Однако я отключил переключатели между портретом и пейзажем в манифесте, так что getWindowManager(). getDefaultDisplay(). getRotation() всегда равен нулю. Когда я поворачиваю устройство на 90 градусов, чтобы он стоял вертикально, я сталкивался с проблемой. Иногда цифры кажутся совершенно неправильными, и я понял, что это относится к Gimbal lock. Однако у других приложений, похоже, нет этой проблемы. Например, я сравнил свое приложение с двумя бесплатными тестовыми приложениями для датчиков (Sensor Tester (Dicotomica) и Мониторинг датчиков (R Software)). Мое приложение соглашается с этими приложениями, когда устройство плоское, но при повороте устройства в вертикальное положение могут быть значительные различия. Кажется, что оба приложения согласны друг с другом, так как они обошли эту проблему?

4b9b3361

Ответ 1

Я думаю, что лучший способ определить ваши углы ориентации, когда устройство не плоское, - это использовать более подходящую систему координат angular, которая соответствует стандартным углам Эйлера, которые вы получаете от SensorManager.getOrientation(...). Я предлагаю тот, который я описываю здесь, на math.stackexchange.com. Я также добавил код, который реализует его в здесь.. Помимо хорошего определения азимута, он также имеет определение угла тангажа, который точно равен углу, указанному в Math.acos(rotationMatrix[8]), который упоминается здесь в другом ответе.

Вы можете получить полную информацию из двух ссылок, которые я привел в первом абзаце. Однако, в заключение, ваша матрица вращения R из SensorManager.getRotationMatrix(...) -

equation with definition of R matrix

где (E x, E y, E z), (N x, N y, N z) и (G x, G y, G z) являются векторы, указывающие на восток, север и в сторону гравитации. Тогда азимутальный угол, который вы хотите, задается

equation defining the azimuth angle

Ответ 2

Если устройство не является плоским, вы должны вызвать remapCoordinateSystem(inR, AXIS_X, AXIS_Z, outR); перед вызовом getOrientation.

Возврат azimuth на getOrientation получается ортогональным проектированием блока устройства Y axis в мир East-North plane, а затем вычисляется угол между результирующим вектором проектирования и северной осью.

Теперь мы обычно думаем о направлении, которое указывает направление, на которое указывает задняя камера. Это направление -Z, где Z - ось устройства, указывающая на экран. Когда устройство плоское, мы не думаем о направлении и не принимаем то, что когда-либо давалось. Но когда он не является плоским, мы ожидаем, что это направление -Z. Но getOrientation вычислить направление Y axis, поэтому нам нужно поменять Y and Z axes перед вызовом getOrientation. Это именно то, что делает remapCoordinateSystem(inR, AXIS_X, AXIS_Z, outR), оно сохраняет X axis неповрежденным и отображает Z to Y.

Итак, как вы знаете, когда до remap или нет. Вы можете сделать это, проверив

float inclination = (float) Math.acos(rotationMatrix[8]);
if (result.inclination < TWENTY_FIVE_DEGREE_IN_RADIAN 
            || result.inclination > ONE_FIFTY_FIVE_DEGREE_IN_RADIAN)
{
    // device is flat just call getOrientation
}
else
{
    // call remap
}

Наклон выше - это угол между экраном устройства и мировым плоскостью Восток-Север. Он показывает, насколько устройство наклоняется.