当前位置: 首页 > 知识库问答 >
问题:

当用户直立手持手机时,方位角读数变为相反

闾丘博超
2023-03-14

我已经实现了罗盘阅读根据通常的建议,我可以在网上找到。我使用rotation_vector传感器类型,并使用标准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)
        }
    }
}

当你水平地拿着手机,就像拿着一个真正的指南针一样,这样就会有很好的表现。然而,当你拿着它像照相机一样,直立着,在你面前,读数就坏了。如果你将它倾斜甚至稍微超过直立,所以它向你倾斜,方位角转向相反的方向(突然180度旋转)。

显然,这个代码跟踪手机的Y轴的方向,在直立的手机上,Y轴变得垂直,当手机向你倾斜时,它的地面方向是向你倾斜的。

我能做些什么来改善这种行为,使它对手机的音调不敏感呢?

共有1个答案

穆彬郁
2023-03-14

显然,这个代码跟踪手机的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]);
     ...

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

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

  • 第0列:指向电话右侧的矢量
  • 第1列:指向电话上的矢量
  • 列2:指向电话正面的矢量

矢量是从地球坐标系(东、北、天)的角度来描述的。

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

  1. 选择电话的上轴(矩阵列1,存储在数组元素1、4、7中)
  2. 将其投影到地球水平面(这很容易,只需忽略元素7中存储的天空分量)
  3. 使用ATAN2从矢量的其余东分量和北分量推导角度。

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

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

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

    使北轴成为参考轴(在几何学中,它是 x轴)
  • 镜像角度:几何角度是逆时针的,但方位角必须是从北向的顺时针角度

当然,当手机的上轴垂直(“向天”),然后超过,它的方位角翻转180度。我们可以用一种非常简单的方法来解决这个问题:我们将使用手机的右轴来代替。请注意以下事项:

  • 当电话水平朝北时,其右轴与东轴对齐。在地球坐标系中,东轴是“X”几何轴,因此我们的0角参考是开箱即用的。
  • 当手机右转(向东)时,其方位角应上升,但几何角度为负。因此我们必须翻转几何角的符号。

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

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

而这微不足道的改变就是你所需要的全部!不需要调用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)
        }
    }
}

使用这段代码,您将得到与Google Maps完全相同的行为。

 类似资料:
  • 一、简介 V9系统开发手机门户,方便手机用户浏览本站点。 二、功能演示 1.添加站点 注:一个站点只能添加一个手机门户。 2.管理站点 站点修改,删除和分类的管理。 3.分类管理 输入(或选择)修改(或添加)的内容。点击“提交”完成保存操作。

  • CC视频支持用户使用手机APP发起直播,手机直播支持iOS、安卓和微信小程序。在直播发起前需要进行以下准备工作: 手机环境要求 操作系统:Android 4.3以上,iOS 9.0以上 网络带宽:上行带宽达到2Mbps以上,建议使用Wi-Fi网络 推流客户端安装 点击客户端安装包进行安装,安装过程中如提示开启相应权限请选择“开启”。安装完成后点击程序图标即可启动客户端。 iOS端APP下载 扫描二

  • 创建6个渲染到WebGLCubeRenderTarget的摄像机。 代码示例 // Create cube render target const cubeRenderTarget = new THREE.WebGLCubeRenderTarget( 128, { format: THREE.RGBFormat, generateMipmaps: true, minFilter: THREE.Li

  • QQ轻游戏不支持获取好友关系链。支持获取头像、昵称、性别三种用户信息 1.昵称 BK.MQQ.Account.getNick(openID,callback) 获取昵称 参数: 参数 类型 名称 备注 openID string 待查询用户的openid callback function 回调函数 返回值:无 例子: function callback(openID,nick){ BK.S

  • 接口说明 登录并返回JSONP数据(支持手机和用户名) 如需调用,请访问 开发者文档 来查看详细的接口使用说明 该接口仅开放给已获取SDK的开发者 API地址 POST /authcenter/api/login/v1.0.0/loginJSONP 是否需要登录 否 请求字段说明 参数 类型 请求类型 是否必须 说明 User-Agent string header 否 用户设备类型,web端可以

  • gPhone 手机上的RSS阅读器软件。