Android SensorManager 奇怪如何重新映射CoordinateSystem
Posted
技术标签:
【中文标题】Android SensorManager 奇怪如何重新映射CoordinateSystem【英文标题】:Android SensorManager strange how to remapCoordinateSystem 【发布时间】:2013-09-13 09:28:47 【问题描述】:API 演示 -> 图形 -> Compass
它只能正常工作,直到您不更改设备的自然方向。 在大多数手机中是纵向的,而大多数 10 英寸平板电脑是横向的。如果您进行更改,则需要将其旋转 90 度。我希望看到该系统的 3D 修复。
100% 肯定需要使用remapCoordinateSystem() 方法。
我想看看如何(代码),如果我能看到有关如何计算这些轴映射(理论数学)的解释,那就太好了。
我试图理解,但我忘记了所有线性代数。
Here 它说明了为什么我们必须使用,但没有说明如何使用!
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 轴在桌子上)
只有且仅当
getWindowManager().getDefaultDisplay().getRotation() == Surface.ROTATION_0
问题是如何完成这段代码:-那些案例分支
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 回复这个:
X 定义为向量积 Y.Z(它与设备当前位置的地面相切,大致指向东方)。
Y 在设备当前位置与地面相切,并指向磁北极。
Z指向天空,垂直于地面。
请看上面的电话!我想从左到右,后置摄像头接地。
getOrientation返回这个:
X 定义为向量积 Y.Z(它与设备当前位置的地面相切,大致指向西)。
Y 在设备当前位置与地面相切,并指向 磁北极。
Z 指向地球的中心并且垂直于地面。
values[0]
:方位角,绕Z轴旋转。
values[1]
:俯仰,绕X轴旋转。
values[2]
:滚动,绕 Y 轴旋转。
电话应该怎么样?
最后我想要像飞机这样的角度值。我的手机(我)向北:(偏航是方位角)
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;
这对我有用,希望有帮助。
【讨论】:
这个是我独立想出来的,所以一定是对的。投赞成票;) 在将手机平放在地上时效果很好,如果您需要通过相机 (AR) 观看时的值,您只需要 axisX = SensorManager.AXIS_X;轴Y = SensorManager.AXIS_Z;然后将旋转添加到第三个值 @BjörnKechel 您的意思是使用 AR 我将 screenRotation 添加到axisZ = SensorManager.AXIS_Y
?所以如果屏幕旋转,方位角和俯仰失速在相机(AR)位置仍然可以吗?
@unlimited101 我发布了一个新答案,对 AR 进行了更详细的描述【参考方案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;
【讨论】:
这是@keianhzo 写的更完整的答案。如果您正在构建 AR 应用程序,它就是您的首选。然而,这个答案缺少如何检查设置透视或平面模式的倾向。【参考方案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);
【讨论】:
它工作正常,但我不知道 Y 是什么值 - Y 不会真正工作【参考方案4】:如果 手机 UI 锁定到旋转 0,我会得到以下值 without remapCoordinateSystem()
Pitch (phone) = -Pitch (API)
Roll (phone) = Roll (API)
Yaw (phone) = Azimuth (API)
至少接近 0,0,0 值。
如果手机 UI 强制旋转 90:
Yaw 值在旧方向有 -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)
如果手机 UI 强制旋转 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
在大多数情况下是有效的。 当你绕 1 个轴快速旋转 180 度时,系统会被拧紧!
完整代码可用at Github
【讨论】:
【参考方案5】:我做的是
-
重新映射坐标系,如his answer 中建议的 keianhzo,根据屏幕旋转重新映射坐标
然后我重新映射结果坐标系
SensorManager.remapCoordinateSystem(rotationMatrixScreenRemapped, SensorManager.AXIS_X, SensorManager.AXIS_Z, rotationMatrixCameraRemapped);
按照documentation 中的建议,根据相机(类似 AR)重新映射坐标
到目前为止,我希望它运作良好!
【讨论】:
以上是关于Android SensorManager 奇怪如何重新映射CoordinateSystem的主要内容,如果未能解决你的问题,请参考以下文章
Android:如何使用 SensorManager.getAltitude(float p0, float p)?
ANDROID_MARS学习笔记_S05_001_用SensorManager获取传感器
获取Android设备的方向,Sensor和SensorManager实现手机旋转角度
Android SensorManager.java getOrientation 和 getRotationMatrix 算法