Как рассчитать азимут, шаг, ориентацию, когда мое устройство Android не плоское?
Я использую датчики силы тяжести и магнитного поля Android для расчета ориентации с помощью SensorManager.getRotationMatrix и SensorManager.getOrientation. Это дает мне азимут, шаг и ориентировочные числа. Результаты выглядят разумно, когда устройство лежит на столе.
Однако я отключил переключатели между портретом и пейзажем в манифесте, так что getWindowManager(). getDefaultDisplay(). getRotation() всегда равен нулю. Когда я поворачиваю устройство на 90 градусов, чтобы он стоял вертикально, я сталкивался с проблемой. Иногда цифры кажутся совершенно неправильными, и я понял, что это относится к Gimbal lock. Однако у других приложений, похоже, нет этой проблемы. Например, я сравнил свое приложение с двумя бесплатными тестовыми приложениями для датчиков (Sensor Tester (Dicotomica) и Мониторинг датчиков (R Software)). Мое приложение соглашается с этими приложениями, когда устройство плоское, но при повороте устройства в вертикальное положение могут быть значительные различия. Кажется, что оба приложения согласны друг с другом, так как они обошли эту проблему?
Ответы
Ответ 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
}
Наклон выше - это угол между экраном устройства и мировым плоскостью Восток-Север. Он показывает, насколько устройство наклоняется.