我正在使用Android重力和磁场传感器通过SensorManager.getRotationMatrix和SensorManager.getOrientation计算方向。这给了我方位角,俯仰和方向数。当设备平躺在桌子上时,结果看起来很合理。
但是,我在清单中禁用了纵向和横向之间的切换,因此getWindowManager()。getDefaultDisplay()。getRotation()始终为零。当我将设备旋转90度以使其垂直竖立时,我遇到了麻烦。有时这些数字似乎非常错误,我意识到这与Gimbal lock有关。但是,其他应用程序似乎没有这个问题。例如,我将我的应用与两个免费的传感器测试应用(Sensor Tester (Dicotomica)和Sensor Monitoring (R's Software))进行了比较。当设备平坦时,我的应用程序同意这些应用程序,但当我将设备旋转到垂直位置时,可能会有很大差异。这两个应用程序似乎彼此一致,所以他们如何解决这个问题呢?
答案 0 :(得分:9)
当设备不平坦时,您必须在致电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
}
上面的倾斜度是设备屏幕与世界东北平面之间的角度。它显示了设备倾斜的程度。
答案 1 :(得分:7)
我认为在设备不平坦时定义方向角的最佳方法是使用更合适的角度坐标系统,该坐标系统是从SensorManager.getOrientation(...)
得到的标准欧拉角。我建议我描述here on math.stackexchange.com。我还在an answer here中添加了一些实现它的代码。除了方位角的良好定义之外,它还有一个俯仰角的定义,它恰好是Math.acos(rotationMatrix[8])
给出的角度,这在另一个答案中提到了。
您可以从我在第一段中给出的两个链接中获取完整的详细信息。但是,总之,来自SensorManager.getRotationMatrix(...)的旋转矩阵 R 是
其中(E x ,E y ,E z ),(N x ,N y ,N z )和(G x ,G y ,G z )矢量指向东方,北方和重力方向。然后你想要的方位角由
给出
答案 2 :(得分:0)
我将使用 Hoan 的回答添加一个对我有用的示例,并重点介绍一些现在可用的新资源,这些资源在查看需要使用陀螺仪传感器的应用时可能会有所帮助。
首先,有一个带有示例应用的 Google 代码实验室 - 我发现完整的示例应用对于理解设备行为非常有用。
应用程序显示如下:
代码实验室和最终应用程序版本可在以下链接中获得(在撰写本文时):
特别是这允许您进行如下实验(它比描述更容易...):
将您的设备平放在桌子上,并尝试在平放的情况下在桌子上旋转,抬起顶部和底部(即,在离开桌子的同时从桌子上抬起显示器的顶部,通常是前置摄像头所在的位置)底部,如果您有主页按钮,通常位于桌面上。同样,抬起设备的左右边缘。观察应用中的方位角、俯仰角和滚转值。
在纵向模式下将您的设备靠在衣柜门上,然后再次尝试移动它。也可以开门看看效果。旋转到横向以查看差异。
我认为您可以从上面看到的关键是,一切都非常简单,在扁平时运行良好,而在不平坦时,一切都复杂且相互关联。
专门研究一个用例,我必须在纵向模式下垂直显示俯仰和滚转,以下对我有用:
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)
}
}