奇怪的Android SensorManager如何重新映射CoordinateSystem

API演示 – >graphics – > 指南针

它只能正常工作,直到你不改变设备的自然方向。 在大多数手机是肖像和大多数10英寸的平板电脑是景观。 如果你改变,需要旋转90度。 我想看看该系统的3D修复。

100%肯定需要使用remapCoordinateSystem()方法。

我想看看(代码)如果我能看到一个解释如何计算这些轴映射(理论math),这将是很好的。

我试着去了解,但是我忘记了所有的线性代数。

这里说明了为什么我们必须使用,但不知道如何!

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); 

编辑:我写了一个小testing应用程序,在屏幕上它显示屏幕旋转: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被定义为vector积YZ(它与设备当前位置的地面相切,大致指向东)。

Y在设备当前位置与地面相切,并指向磁北极。

Z指向天空并垂直于地面。

看到上面的电话! 我想从右边往右走,背对着摄像头。

getOrientation返回这个:

在这里输入图像说明

X定义为vector积YZ(它与设备当前位置的地面相切,大致指向西)。

Y在设备当前位置与地面相切,并指向磁北极。

Z指向地心,垂直于地面。

values[0] :方位angular,绕Z轴旋转。

values[1] :绕X轴旋转。

values[2] :滚动,围绕Y轴旋转。

手机应该怎么样?

最后,我想要有像飞机这样的angular度的价值。 我的电话(我)前往北方:(偏航是方位angular)

在这里输入图像说明

  if ScreenRotation = 0 degree Pitch axis = -orientationAxisX = rotationAxisX Roll axis = orientationAxisY = rotationAxisY Yaw axis = orientationAxisZ = -rotationAxisZ 

为了完成切换分支,我只是想着遵循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; } 

这对我有用,希望有所帮助。

谢谢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; } } } 

这就是我在我的应用程序中如何做的魔术:

  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); 

如果将手机UIlocking为旋转0 ,则无需 remapCoordinateSystem() 即可获取以下值

 Pitch (phone) = -Pitch (API) Roll (phone) = Roll (API) Yaw (phone) = Azimuth (API) 
  • 至less接近0,0,0的值。

如果手机UI强制旋转90

在旧的方向上,偏航值为-90度( – PI / 2)! =>我现在要去东方,而不是北方。

如果我把手机拿到0,0,0的位置:

 Pitch (phone) = -Roll (API) Roll (phone) = -Pitch (API) Yaw (phone) = Azimuth (API) 

如果手机UI强制旋转180度

在旧的方向上,偏航值有+/- 180度(+/- PI)! =>我现在要去南方,而不是北方。

如果我把手机拿到0,0,0的位置:

 Pitch (phone) = Pitch (API) Roll (phone) = -Roll (API) Yaw (phone) = Azimuth (API) 

如果手机UI强制旋转270

偏航值在旧的方向上有+90度(+ PI / 2)! =>我现在要去西方,而不是北方。

如果我把手机拿到0,0,0的位置:

 Pitch (phone) = Roll (API) Roll (phone) = Pitch (API) Yaw (phone) = Azimuth (API) 

我写了一个小小的修复,并testing: 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度时,系统会被拧紧!

Github提供的完整代码