当用户直立握住手机时,方位角读数变为相反方向 [英] Azimuth reading changes to opposite when user holds the phone upright

查看:113
本文介绍了当用户直立握住手机时,方位角读数变为相反方向的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经按照我在网上可以找到的常见建议实施了指南针读数.我使用ROTATION_VECTOR传感器类型,并使用标准API调用将其转换为(azimuth, pitch, roll)三元组.这是我的代码:

I have implemented the compass reading according to the usual recommendations that I could find on the web. I use the ROTATION_VECTOR sensor type and I transform it into the (azimuth, pitch, roll) triple using the standard API calls. Here's my code:

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

当您水平握住手机时,这会产生很好的行为,就像是真正的指南针一样.但是,当您像照相机一样将其直立放置并摆在您面前时,读数会崩溃.如果您将其稍微倾斜超过垂直位置,那么它就会向您倾斜,方位角会朝相反的方向旋转(突然旋转180度).

This results in behavior that's quite good when you hold the phone horizontally, like you would a real compass. However, when you hold it like a camera, upright and in front of you, the reading breaks down. If you tilt it even slightly beyond upright, so it leans towards you, the azimuth turns to the opposite direction (sudden 180 degree rotation).

显然,此代码跟踪手机的y轴的方向(在立式手机上变为垂直),并且当手机向您倾斜时,它的地面方向也朝向您.

Apparently this code tracks the orientation of the phone's y-axis, which becomes vertical on an upright phone, and its ground orientation is towards you when the phone leans towards you.

我该怎么做才能改善这种行为,以使其对手机的音调不敏感?

What could I do to improve this behavior so it's not sensitive to the phone's pitch?

推荐答案

分析

显然,此代码跟踪手机的y轴的方向(在立式手机上变为垂直),并且当手机向您倾斜时,它的地面方向也朝向您.

Apparently this code tracks the orientation of the phone's y-axis, which becomes vertical on an upright phone, and its ground orientation is towards you when the phone leans towards you.

是的,这是正确的.您可以检查getOrientation()的代码以查看发生了什么:

Yes, this is correct. You can inspect the code of getOrientation() to see what's going on:

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

values[0]是您获得的方位角值.

values[0] is the azimuth value you got.

您可以将旋转矩阵R解释为指向设备三个主轴的向量的分量:

You can interpret the rotation matrix R as the components of the vectors that point in the device's three major axes:

  • 第0列:指向手机的 right
  • 的矢量
  • 列1:指向手机 up
  • 的矢量
  • 第2列:指向手机的 front
  • 的矢量
  • column 0: vector pointing to phone's right
  • column 1: vector pointing to phone's up
  • column 2: vector pointing to phone's front

矢量是从地球坐标系( sky )的角度描述的.

The vectors are described from the perspective of the Earth's coordinate system (east, north, and sky).

考虑到这一点,我们可以解释getOrientation()中的代码:

With this in mind we can interpret the code in getOrientation():

  1. 选择手机的轴(矩阵列1,存储在数组元素1、4、7中)
  2. 将其投影到地球的水平面(这很简单,只需忽略存储在元素7中的 sky 组件)
  3. 使用atan2从矢量的其余 east north 分量推断角度.
  1. select the phone's up axis (matrix column 1, stored in array elements 1, 4, 7)
  2. project it to the Earth's horizontal plane (this is easy, just ignore the sky component stored in element 7)
  3. Use atan2 to deduce the angle from the remaining east and north components of the vector.

这里还隐藏着另一个微妙之处:atan2的签名是

There's another subtlety hiding here: the signature of atan2 is

public static double atan2(double y, double x);

请注意参数顺序:y,然后是x.但是getOrientation east north 顺序传递参数.这实现了两件事:

Note the parameter order: y, then x. But getOrientation passes the arguments in the east, north order. This achieves two things:

  • 使为参考轴(在几何中为x轴)
  • 镜像角度:几何角度为逆时针方向,但方位角必须为与北方成顺时针方向的角度
  • makes north the reference axis (in geometry it's the x axis)
  • mirrors the angle: geometrical angles are anti-clockwise, but azimuth must be the clockwise angle from north

自然,当手机的轴垂直(向天空")然后超出其范围时,其方位角会翻转180度.我们可以通过一种非常简单的方法来解决此问题:我们将改用手机的 right 轴.请注意以下几点:

Naturally, when the phone's up axis goes vertical ("skyward") and then beyond, its azimuth flips by 180 degrees. We can fix this in a very simple way: we'll use the phone's right axis instead. Note the following:

  • 当电话为水平且朝北时,其轴与 east 轴对齐.在地球坐标系中的 east 轴是"x"几何轴,因此我们的0角参考点是现成的.
  • 当电话向右转(向东)时,其方位角应上升,但其几何角度将变为负数.因此,我们必须翻转几何角度的符号.
  • when the phone is horizontal and facing north, its right axis is aligned with the east axis. The east axis, in the Earth's coordinate system, is the "x" geometrical axis, so our 0-angle reference is correct out-of-the-box.
  • when the phone turns right (eastwards), its azimuth should rise, but its geometrical angle goes negative. Therefore we must flip the sign of the geometrical angle.

所以我们的新公式是这样:

So our new formula is this:

val azimuth = -atan2(R[3], R[0])

这项微不足道的更改就是您所需要的!无需调用getOrientation,只需将其应用于方向矩阵即可.

And this trivial change is all you need! No need to call getOrientation, just apply this to the orientation matrix.

到目前为止,太好了.但是,如果用户横向使用手机怎么办?电话的轴不受影响,但是现在用户将电话的左"或右"方向感知为前进"(取决于用户转动电话的方式).我们可以通过检查Display.rotation属性来对此进行更正.如果屏幕旋转,我们将使用手机的 up 轴与上方的 right 轴发挥相同的作用.

So far, so good. But what if the user is using the phone in the landscape orientation? The phone's axes are unaffected, but now the user perceives the phone's "left" or "right" direction as "ahead" (depending on how the user turned the phone). We can correct for this by inspecting the Display.rotation property. If the screen is rotated, we'll use the up axis of the phone to play the same role as the right axis above.

因此定向侦听器的完整代码变为:

So the full code of the orientation listener becomes this:

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

使用此代码,您将获得与Google Maps完全相同的行为.

With this code, you're getting the exact same behavior as on Google Maps.

这篇关于当用户直立握住手机时,方位角读数变为相反方向的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆