Android SensorManager странно, как remapCoordinateSystem
Демо API → Графика → Compass
Он работает только правильно, пока вы не измените естественную ориентацию устройства.
В большинстве телефонов портрет и большинство 10-дюймовых планшетов - это пейзаж. Если вы измените, чем нужно, поверните его на 90 градусов. Я хотел бы увидеть 3D-решение для этой системы.
100% обязательно нужно использовать метод remapCoordinateSystem().
Я хотел бы видеть, как (код), если бы я мог видеть объяснение того, как вычисляется такое отображение осей (теоретическая математика), было бы хорошо.
Я попытался понять, но я забыл всю линейную алгебру.
Здесь говорится, почему мы должны использовать, но не сообщая, как это сделать.
float R[] = new float[9];
// X (product of Y and Z) and roughly points East
// Y: points to Magnetic NORTH and tangential to ground
// Z: points to SKY and perpendicular to ground
float I[] = new float[9];
boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);
Кажется, что эти координаты относятся к этой позиции: - устройство говорит в таблице (оси x и y находятся на таблице)
![device orientation]()
Только и только если
getWindowManager().getDefaultDisplay().getRotation() == Surface.ROTATION_0
Вопрос заключается в том, как заполнить этот код: - те ветки case
switch (mScreenRotation) {
case Surface.ROTATION_0:
Log.v("SurfaceRemap", "0 degree");
axisX = SensorManager.AXIS_X;// is this valid?
axisY = SensorManager.AXIS_Y;// is this valid?
break;
case Surface.ROTATION_90:
Log.v("SurfaceRemap", "90 degree");
// examples says remapCoordinateSystem(inR, AXIS_Y, AXIS_MINUS_X, outR);
axisX = SensorManager.AXIS_Y;
axisY = SensorManager.AXIS_MINUS_X;
break;
case Surface.ROTATION_180:
Log.v("SurfaceRemap", "180 degree");
break;
case Surface.ROTATION_270:
Log.v("SurfaceRemap", "270 degree");
break;
default:
Log.v("SurfaceRemap", "don't know the mScreenRotation value: "+mScreenRotation+" you should never seen this message!");
break;
}
boolean remapped = SensorManager.remapCoordinateSystem(R, axisX, axisY, R);
float orientation[] = new float[3];
SensorManager.getOrientation(R, orientation);// All three angles above are in radians and positive in the counter-clockwise direction.
inclination = SensorManager.getInclination(I);
Изменить:
Я написал небольшое тестовое приложение, в котором на экране отображается поворот экрана: 0, 90, 270 градусов (теперь он не может сделать 180)
Кажется, если Rotation 0 не изменилось (axisX = SensorManager.AXIS_X;axisY = SensorManager.AXIS_Y;
), чем 90 градусов должно быть:
axisX = SensorManager.AXIS_MINUS_Y;
axisY = SensorManager.AXIS_X;
чем в документации Google, где-то неверные значения! Вопрос: где?!
getRotationMatrix вернуть это:
![enter image description here]()
X определяется как векторное произведение Y.Z(оно касательно к основанию на текущем местоположении устройства и примерно соответствует востоку).
Y тангенциально к земле в месте расположения устройства и указывает на магнитный Северный полюс.
Z указывает на небо и перпендикулярно земле.
Смотрите телефон выше! Я хочу, чтобы налево, с обратной камерой на землю.
getOrientation вернуть это:
![enter image description here]()
X определяется как векторное произведение Y.Z(оно касательно к основанию на текущем местоположении устройства и примерно соответствует западу).
Y тангенциально к земле в месте расположения устройства и указывает на магнитный Северный полюс.
Z указывает на центр Земли и перпендикулярно земле.
values[0]
: азимут, вращение вокруг оси Z.
values[1]
: шаг, вращение вокруг оси X.
values[2]
: рулон, вращение вокруг оси Y.
Как должен быть телефон?
Наконец, я хотел бы иметь значения углов, таких как самолеты. Мой телефон (я), направляющийся на север: (рыскание - азимут)
![enter image description here]()
if ScreenRotation = 0 degree
Pitch axis = -orientationAxisX = rotationAxisX
Roll axis = orientationAxisY = rotationAxisY
Yaw axis = orientationAxisZ = -rotationAxisZ
Ответы
Ответ 1
Чтобы завершить ветки коммутатора, я просто попытаюсь подумать, следуя методу remapCoordinateSystem javadoc:
X определяет, на какой мировой оси и направлении ось X устройства отображается.
Y определяет, на какой мировой оси и направлении ось Y устройство отображается.
Итак, поверните ваше устройство из его естественной ориентации (90, 180 или 270 градусов) и спросите себя: X положительная ось в исходной ориентации устройства, к какой оси соответствует текущая ориентация устройства?. И то же самое для оси Y.
Итак, если ваше устройство повернуто на 90 градусов, вы увидите, что исходная положительная ось X соответствует текущей положительной оси Y, а исходная положительная ось Y соответствует отрицательной оси X текущей ориентации.
Итак, это должно быть:
switch (mScreenRotation) {
case Surface.ROTATION_0:
axisX = SensorManager.AXIS_X;
axisY = SensorManager.AXIS_Y;
break;
case Surface.ROTATION_90:
axisX = SensorManager.AXIS_Y;
axisY = SensorManager.AXIS_MINUS_X;
break;
case Surface.ROTATION_180:
axisX = SensorManager.AXIS_MINUS_X;
axisY = SensorManager.AXIS_MINUS_Y;
break;
case Surface.ROTATION_270:
axisX = SensorManager.AXIS_MINUS_Y;
axisY = SensorManager.AXIS_X;
break;
default:
break;
}
Это сработало для меня, надеюсь, что это поможет.
Ответ 2
Спасибо keianhzo, ваш ответ отлично работает с телефонами на земле. Для AR-приложений, где вы смотрите "через" дисплей, я нашел, что это работает:
используйте правильную ось:
int screenRotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
//use the correct axis
int axisX = SensorManager.AXIS_X;
int axisY = SensorManager.AXIS_Y;
switch (mMode) {
case LOOK_THROUGH: {
// look through always uses x and z
axisX = SensorManager.AXIS_X;
axisY = SensorManager.AXIS_Z;
break;
}
case FLAT: {
// flat changes the x axis depending on rotation state
switch (screenRotation) {
case Surface.ROTATION_0:
axisX = SensorManager.AXIS_X;
axisY = SensorManager.AXIS_Y;
break;
case Surface.ROTATION_90:
axisX = SensorManager.AXIS_Y;
axisY = SensorManager.AXIS_MINUS_X;
break;
case Surface.ROTATION_180:
axisX = SensorManager.AXIS_MINUS_X;
axisY = SensorManager.AXIS_MINUS_Y;
break;
case Surface.ROTATION_270:
axisX = SensorManager.AXIS_MINUS_Y;
axisY = SensorManager.AXIS_X;
break;
default:
break;
}
break;
}
default:
break;
}
Получить градусы ориентации:
boolean success = SensorManager.remapCoordinateSystem(getQuaternion().getMatrix4x4().getMatrix(), axisX, axisY, mRotationMatrixTransformed);
if (success) {
SensorManager.getOrientation(mRotationMatrixTransformed, mOrientationValues);
for (int i = 0; i < 3; i++) {
mOrientationDegrees[i] = (float) Math.toDegrees(mOrientationValues[i]);
}
//And for look through, add the rotation state
if (mMode == MODE.LOOK_THROUGH) {
// look through has different angles depending on rotation state
switch (screenRotation) {
case Surface.ROTATION_90: {
mOrientationDegrees[2] += 90;
break;
}
case Surface.ROTATION_180: {
mOrientationDegrees[2] += 180;
break;
}
case Surface.ROTATION_270: {
mOrientationDegrees[2] += 270;
break;
}
}
}
Ответ 3
Вот как я делаю магию в своем приложении:
float[] rotationMatrixOrig = new float[9];
SensorManager.getRotationMatrix(rotationMatrixOrig, null, lastAccelerometerValue, lastMagnetometerValue);
int screenRotation = app.getCurrentActivity().getWindowManager().getDefaultDisplay().getRotation();
int axisX, axisY;
boolean isUpSideDown = lastAccelerometerValue[2] < 0;
switch (screenRotation) {
case Surface.ROTATION_0:
axisX = (isUpSideDown ? SensorManager.AXIS_MINUS_X : SensorManager.AXIS_X);
axisY = (Math.abs(lastAccelerometerValue[1]) > 6.0f ?
(isUpSideDown ? SensorManager.AXIS_MINUS_Z : SensorManager.AXIS_Z) :
(isUpSideDown ? SensorManager.AXIS_MINUS_Y : SensorManager.AXIS_Y));
break;
case Surface.ROTATION_90:
axisX = (isUpSideDown ? SensorManager.AXIS_MINUS_Y : SensorManager.AXIS_Y);
axisY = (Math.abs(lastAccelerometerValue[0]) > 6.0f ?
(isUpSideDown ? SensorManager.AXIS_Z : SensorManager.AXIS_MINUS_Z) :
(isUpSideDown ? SensorManager.AXIS_X : SensorManager.AXIS_MINUS_X));
break;
case Surface.ROTATION_180:
axisX = (isUpSideDown ? SensorManager.AXIS_X : SensorManager.AXIS_MINUS_X);
axisY = (Math.abs(lastAccelerometerValue[1]) > 6.0f ?
(isUpSideDown ? SensorManager.AXIS_Z : SensorManager.AXIS_MINUS_Z) :
(isUpSideDown ? SensorManager.AXIS_Y : SensorManager.AXIS_MINUS_Y));
break;
case Surface.ROTATION_270:
axisX = (isUpSideDown ? SensorManager.AXIS_Y : SensorManager.AXIS_MINUS_Y);
axisY = (Math.abs(lastAccelerometerValue[0]) > 6.0f ?
(isUpSideDown ? SensorManager.AXIS_MINUS_Z : SensorManager.AXIS_Z) :
(isUpSideDown ? SensorManager.AXIS_MINUS_X : SensorManager.AXIS_X));
break;
default:
axisX = (isUpSideDown ? SensorManager.AXIS_MINUS_X : SensorManager.AXIS_X);
axisY = (isUpSideDown ? SensorManager.AXIS_MINUS_Y : SensorManager.AXIS_Y);
}
float[] rotationMatrix = new float[9];
SensorManager.remapCoordinateSystem(rotationMatrixOrig, axisX, axisY, rotationMatrix);
Ответ 4
Если пользовательский интерфейс телефона заблокирован для вращения 0, я получаю следующие значения без remapCoordinateSystem()
Pitch (phone) = -Pitch (API)
Roll (phone) = Roll (API)
Yaw (phone) = Azimuth (API)
Если пользовательский интерфейс телефона принудительно вращается 90:
Значение рыскания имеет -90 градусов (- PI/2) при старой ориентации!!! = > Я пойду на Восток в реальности, а не на Север.
Если я беру телефон на 0,0,0 позиции:
Pitch (phone) = -Roll (API)
Roll (phone) = -Pitch (API)
Yaw (phone) = Azimuth (API)
Если пользовательский интерфейс телефона принудительно повернут на 180:
Значение yaw имеет +/- 180 градусов (+/- PI) при старой ориентации!!! = > Я пойду на Юг в реальности, а не на Север.
Если я беру телефон на 0,0,0 позиции:
Pitch (phone) = Pitch (API)
Roll (phone) = -Roll (API)
Yaw (phone) = Azimuth (API)
Если пользовательский интерфейс телефона принудительно вращается 270:
Значение yaw имеет +90 градусов (+ PI/2) при старой ориентации!!! = > Я пойду на Запад в действительности, а не на Север.
Если я беру телефон на 0,0,0 позиции:
Pitch (phone) = Roll (API)
Roll (phone) = Pitch (API)
Yaw (phone) = Azimuth (API)
Я написал небольшое исправление и протестировал с помощью: android:screenOrientation="fullSensor"
public static final void fixRotation0(float[] orientation) { //azimuth, pitch, roll
orientation[1] = -orientation[1]; // pitch = -pitch
}
public static final void fixRotation90(float[] orientation) { //azimuth, pitch, roll
orientation[0] += Math.PI / 2f; // offset
float tmpOldPitch = orientation[1];
orientation[1] = -orientation[2]; //pitch = -roll
orientation[2] = -tmpOldPitch; // roll = -pitch
}
public static final void fixRotation180(float[] orientation) { //azimuth, pitch, roll
orientation[0] = (float)(orientation[0] > 0f ? (orientation[0] - Math.PI) : (orientation[0] + Math.PI)); // offset
orientation[2] = -orientation[2]; // roll = -roll
}
public static final void fixRotation270(float[] orientation) { //azimuth, pitch, roll
orientation[0] -= Math.PI / 2; // offset
float tmpOldPitch = orientation[1];
orientation[1] = orientation[2]; //pitch = roll
orientation[2] = tmpOldPitch; // roll = pitch
}
В большинстве случаев работает.
Когда вы быстро вращаетесь на 180 градусов вокруг 1 оси, система будет завинчена!
Полный код доступен в Github