Как рассчитать азимут, шаг, ориентацию, когда мое устройство Android не плоское?
Я использую датчики гравитации и магнитного поля Android для расчета ориентации с помощью SensorManager.getRotationMatrix и SensorManager.getOrientation. Это дает мне номера азимута, шага и ориентации. Результаты выглядят разумно, когда устройство лежит на столе.
Однако в манифесте я отключил переключение между портретным и ландшафтным режимом, поэтому getWindowManager(). GetDefaultDisplay(). GetRotation() всегда равен нулю. Когда я поворачиваю устройство на 90 градусов, чтобы оно стояло вертикально, у меня возникают проблемы. Иногда цифры кажутся совершенно неправильными, и я понял, что это связано с блокировкой карданного подвеса. Однако другие приложения, похоже, не имеют этой проблемы. Например, я сравнил свое приложение с двумя бесплатными приложениями для тестирования датчиков ( Sensor Tester (Dicotomica) и Sensor Monitoring (R's Software)). Мое приложение соглашается с этими приложениями, когда устройство плоское, но когда я поворачиваю устройство в вертикальное положение, могут быть значительные различия. Кажется, что эти два приложения согласуются друг с другом, так как же они справляются с этой проблемой?
3 ответа
Я думаю, что лучший способ определить ваши углы ориентации, когда устройство не плоское, это использовать более подходящую систему угловых координат, чем стандартные углы Эйлера, которые вы получаете от SensorManager.getOrientation(...)
, Я предлагаю тот, который я опишу здесь, на math.stackexchange.com. Я также поместил некоторый код, который реализует это в ответе здесь. Apart from a good definition of azimuth, it also has a definition of the pitch angle which is exactly the angle given by Math.acos(rotationMatrix[8])
that is mentioned in another answer here.
Вы можете получить полную информацию по двум ссылкам, которые я дал в первом абзаце. Тем не менее, в итоге, ваша матрица вращения R от SensorManager.getRotationMatrix(...)
где (Ex, Ey, Ez), (Nx, Ny, Nz) и (Gx, Gy, Gz) - векторы, указывающие на восток, север и в направлении силы тяжести. Тогда требуемый угол азимута
Когда устройство не плоское, вы должны позвонить 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
}
Наклон выше - это угол между экраном устройства и мировой плоскостью Восток-Север. Он показывает, насколько сильно устройство наклоняется.
Я добавлю пример, который сработал для меня, используя ответ Хоана, а также выделю некоторые новые ресурсы, которые доступны сейчас и могут помочь при просмотре приложений, которым необходимо использовать гироскопические датчики.
Во-первых, есть лаборатория Google Code, доступная с образцом приложения - я обнаружил, что готовый образец приложения очень полезен для понимания поведения устройств.
Дисплей приложения выглядит так:
Таблицы кодов и окончательная версия приложения доступны по этим ссылкам (на момент написания):
- https://developer.android.com/codelabs/advanced-android-training-sensor-orientation#0
- https://github.com/google-developer-training/android-advanced/tree/master/TiltSpot
В частности, это позволяет вам экспериментировать следующим образом (это проще сделать, чем описать ...):
положите устройство на стол и поэкспериментируйте с вращением на столе в горизонтальном положении, поднимая верхнюю и нижнюю части (т. е. поднимите верхнюю часть дисплея, где обычно находится ваша передняя камера, со стола, оставив нижнюю часть, где находится кнопка вашего дома. обычно, если он у вас есть, на столе. Аналогичным образом поднимите левый и правый края устройства. Обратите внимание на значения азимута, тангажа и крена в приложении.
Приложите устройство к дверце шкафа в портретном режиме и снова поэкспериментируйте, перемещая его. Откройте дверь, чтобы тоже увидеть эффект. Поверните в альбомную ориентацию, чтобы увидеть разницу.
Ключевым моментом, который, я думаю, вы можете видеть из вышесказанного, является то, что все очень просто и хорошо работает, когда оно плоское, и все сложно и взаимосвязано, когда оно не плоское.
Рассматривая конкретный вариант использования, мне приходилось отображать угол наклона и крена в вертикальном положении в портретном режиме, у меня сработало следующее:
when (event?.sensor?.type) {
Sensor.TYPE_ROTATION_VECTOR -> {
// Calculate the rotation matrix
val rotMatrix = FloatArray(9)
var rotationMatrixAdjusted = FloatArray(9)
val azimuthChanged: (Float) -> Unit
val orientation = FloatArray(3)
SensorManager.getRotationMatrixFromVector(rotMatrix, event.values);
SensorManager.remapCoordinateSystem(rotMatrix,
SensorManager.AXIS_Y, SensorManager.AXIS_MINUS_X,
rotationMatrixAdjusted);
SensorManager.getOrientation(rotationMatrixAdjusted, orientation)
val rollAngleRadians = orientation[1]
val pitchAngleRadians = orientation[2]
//Report results back to listener
thisListener.onRollPitchEvent(rollAngleRadians, pitchAngleRadians)
}
}