我已经根据我可以在网上找到的通常建议实施了指南针读数。我使用ROTATION_VECTOR传感器类型,并(azimuth, pitch, roll)使用标准 API 调用将其转换为三元组。这是我的代码:
fun Fragment.receiveAzimuthUpdates(
azimuthChanged: (Float) -> Unit,
accuracyChanged: (Int) -> Unit
) {
val sensorManager = activity!!.getSystemService(Context.SENSOR_SERVICE)
as SensorManager
val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)!!
sensorManager.registerListener(OrientationListener(azimuthChanged, accuracyChanged),
sensor, 10_000)
}
private class OrientationListener(
private val azimuthChanged: (Float) -> Unit,
private val accuracyChanged: (Int) -> Unit
) : SensorEventListener {
private val rotationMatrix = FloatArray(9)
private val orientation = FloatArray(3)
override fun onSensorChanged(event: SensorEvent) {
if (event.sensor.type != Sensor.TYPE_ROTATION_VECTOR) return
SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values)
SensorManager.getOrientation(rotationMatrix, orientation)
azimuthChanged(orientation[0])
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
if (sensor.type == Sensor.TYPE_ROTATION_VECTOR) {
accuracyChanged(accuracy)
}
}
}
Run Code Online (Sandbox Code Playgroud)
当您水平握持手机时,这会产生非常好的行为,就像真正的指南针一样。然而,当你像相机一样拿着它,直立在你面前时,读数就会崩溃。如果您将其倾斜至超过直立位置,使其向您倾斜,则方位角将转向相反的方向(突然旋转 180 度)。
显然,此代码跟踪手机 y 轴的方向,该轴在直立手机上变为垂直,当手机向您倾斜时,它的地面方向朝向您。
我可以做些什么来改善这种行为,使其对手机的音调不敏感?
显然,此代码跟踪手机 y 轴的方向,该轴在直立手机上变为垂直,当手机向您倾斜时,它的地面方向朝向您。
是的,这是正确的。您可以检查代码getOrientation()以了解发生了什么:
public static float[] getOrientation(float[] R, float[] values) {
/*
* / R[ 0] R[ 1] R[ 2] \
* | R[ 3] R[ 4] R[ 5] |
* \ R[ 6] R[ 7] R[ 8] /
*/
values[0] = (float) Math.atan2(R[1], R[4]);
...
Run Code Online (Sandbox Code Playgroud)
values[0] 是你得到的方位角值。
您可以将旋转矩阵解释R为指向设备三个主轴的向量的分量:
这些向量是从地球坐标系(东、北和天空)的角度描述的。
考虑到这一点,我们可以解释以下代码getOrientation():
atan2从向量的剩余东向和北向分量推导出角度。这里隐藏着另一个微妙之处:的签名atan2是
public static double atan2(double y, double x);
Run Code Online (Sandbox Code Playgroud)
注意参数顺序:y,然后x。但是getOrientation通过了东、北顺序的论据。这实现了两件事:
x轴)自然地,当手机的上轴垂直(“向天”)然后超出时,其方位角会翻转 180 度。我们可以用一种非常简单的方式来解决这个问题:我们将使用手机的右轴。请注意以下事项:
所以我们的新公式是这样的:
val azimuth = -atan2(R[3], R[0])
Run Code Online (Sandbox Code Playgroud)
而这个微不足道的改变就是你所需要的!无需调用getOrientation,只需将其应用于方向矩阵即可。
到现在为止还挺好。但是如果用户在横向使用手机呢?手机的轴不受影响,但现在用户将手机的“左”或“右”方向视为“向前”(取决于用户如何转动手机)。我们可以通过检查Display.rotation财产来纠正这一点。如果屏幕旋转,我们将使用手机的上轴来扮演与上面右轴相同的角色。
所以方向监听器的完整代码变成了这样:
private class OrientationListener(
private val activity: Activity,
private val azimuthChanged: (Float) -> Unit,
private val accuracyChanged: (Int) -> Unit
) : SensorEventListener {
private val rotationMatrix = FloatArray(9)
override fun onSensorChanged(event: SensorEvent) {
if (event.sensor.type != Sensor.TYPE_ROTATION_VECTOR) return
SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values)
val (matrixColumn, sense) = when (val rotation =
activity.windowManager.defaultDisplay.rotation
) {
Surface.ROTATION_0 -> Pair(0, 1)
Surface.ROTATION_90 -> Pair(1, -1)
Surface.ROTATION_180 -> Pair(0, -1)
Surface.ROTATION_270 -> Pair(1, 1)
else -> error("Invalid screen rotation value: $rotation")
}
val x = sense * rotationMatrix[matrixColumn]
val y = sense * rotationMatrix[matrixColumn + 3]
azimuthChanged(-atan2(y, x))
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
if (sensor.type == Sensor.TYPE_ROTATION_VECTOR) {
accuracyChanged(accuracy)
}
}
}
Run Code Online (Sandbox Code Playgroud)
使用此代码,您将获得与 Google 地图完全相同的行为。
| 归档时间: |
|
| 查看次数: |
559 次 |
| 最近记录: |