android - 传感器 TYPE_ACCELEROMETER/TYPE_MAGNETIC_FIELD 和 TYPE_ORIENTATION 之间的不同值

标签 android orientation sensors device-orientation

有 2 种方法可以获取 3 个旋转值(方位角、俯仰角、滚转角)。

一种是注册类型为 TYPE_ORIENTATION 的监听器。这是最简单的方法,我从每次旋转中得到正确的值范围,如文档所述:
方位角:[0, 359]
间距:[-180, 180]
滚动:[-90, 90]

另一个是第一次看到时最精确和最复杂的。 Android 推荐它,所以我想使用它,但我得到了不同的值。

方位角:[-180, 180]。 -180/180 是 S、0 i N、90 E 和 -90 W。
音高:[-90, 90]。 90 是 90,-90 是 -90,0 是 0 但 -180/180(屏幕朝下)是 0。
滚动:[-180, 180]。

我应该得到相同的值但有小数,对吗?

我有以下代码:

aValues = new float[3];
mValues = new float[3];

sensorListener = new SensorEventListener (){
    public void onSensorChanged (SensorEvent event){
        switch (event.sensor.getType ()){
            case Sensor.TYPE_ACCELEROMETER:
                aValues = event.values.clone ();
                break;
            case Sensor.TYPE_MAGNETIC_FIELD:
                mValues = event.values.clone ();
                break;
        }

        float[] R = new float[16];
        float[] orientationValues = new float[3];

        SensorManager.getRotationMatrix (R, null, aValues, mValues);
        SensorManager.getOrientation (R, orientationValues);

        orientationValues[0] = (float)Math.toDegrees (orientationValues[0]);
        orientationValues[1] = (float)Math.toDegrees (orientationValues[1]);
        orientationValues[2] = (float)Math.toDegrees (orientationValues[2]);

        azimuthText.setText ("azimuth: " + orientationValues[0]);
        pitchText.setText ("pitch: " + orientationValues[1]);
        rollText.setText ("roll: " + orientationValues[2]);
    }

    public void onAccuracyChanged (Sensor sensor, int accuracy){}
};

请帮忙。这很令人沮丧。

我必须用这些值(value)观来对待还是我做错了什么?

谢谢。

最佳答案

我知道我在这里玩线程死灵法师,但我最近一直在研究这些东西,所以我想我会投入 2 美分。

该设备不包含罗盘或倾角仪,因此它不直接测量方位角、俯仰角或滚转角。 (我们称之为欧拉角,顺便说一句)。相反,它使用加速度计和磁力计,两者都产生 3 空间 XYZ 向量。这些用于计算方位角等值。

向量在设备坐标空间中:

Device coordinates

世界坐标 Y 朝北,X 朝东,Z 朝上:

World coordinates

因此,设备的“中性”方向是平放在 table 上,设备顶部朝北。

加速度计产生一个“向上”方向的矢量。磁力计在“北”方向产生一个矢量。 (请注意,在北半球,由于 magnetic dip ,这往往指向下方。)

加速度计矢量和磁力计矢量可以通过 SensorManager.getRotationMatrix() 以数学方式组合,它返回一个 3x3 矩阵,它将设备坐标中的矢量映射到世界坐标,反之亦然。对于处于中间位置的设备,此函数将返回单位矩阵。

该矩阵不随屏幕方向变化。这意味着您的应用程序需要了解方向并相应地进行补偿。

SensorManager.getOrientation() 采用变换矩阵并计算方位角、俯仰角和滚转值。这些是相对于处于中间位置的设备进行的。

我不知道调用这个函数和只使用 TYPE_ORIENTATION 传感器有什么区别,除了这个函数让你先操作矩阵。

如果设备向上倾斜 90° 或接近 90°,那么欧拉角的使用就会失效。这是数学上的退化情况。在这个领域,设备应该如何知道您是在改变方位角还是滚转?

函数 SensorManager.remapCoordinateSystem() 可用于操作变换矩阵以补偿您可能了解的设备方向。但是,我的实验表明,这并不涵盖所有情况,甚至不包括一些常见情况。例如,如果您想为直立的设备重新映射(例如拍照),您需要将变换矩阵乘以这个矩阵:

1 0 0
0 0 1
0 1 0

在调用 getOrientation() 之前,这不是 remapCoordinateSystem() 支持的方向重映射之一 [如果我在这里遗漏了什么,请有人纠正我]。

好的,所以这一直是一种冗长的说法,如果您使用方向,无论是来自 TYPE_ORIENTATION 传感器还是来自 getOrientation(),您可能都做错了。您真正需要欧拉角的唯一时间是以用户友好的形式显示方向信息、注释照片、驱动飞行仪表显示或类似的东西。

如果您想进行与设备方向相关的计算,则几乎肯定最好使用变换矩阵并使用 XYZ 向量。

作为一名顾问,每当有人向我提出涉及欧拉角的问题时,我都会回过头来问他们真正想要做什么,然后找到一种方法来用向量来做。

回顾您最初的问题, getOrientation() 应返回 [-180 180] [-90 90] 和 [-180 180] 中的三个值(从弧度转换后)。在实践中,我们将方位角视为 [0 360) 中的数字,因此您只需将 360 添加到您收到的任何负数。您的代码看起来是正确的。如果我确切地知道您期望的结果以及您得到的结果,那将会有所帮助。

编辑添加:更多的想法。现代版本的 Android 使用一种叫做“传感器融合”的东西,这基本上意味着所有可用的输入——加速度计、磁力计、陀螺仪——都组合在一个数学黑匣子中(通常是卡尔曼滤波器,但取决于供应商)。所有不同的传感器——加速度、磁场、陀螺仪、重力、线性加速度和方向——都被作为这个黑匣子的输出。

只要有可能,您应该使用 TYPE_GRAVITY 而不是 TYPE_ACCELEROMETER 作为 getRotationMatrix() 的输入。

关于android - 传感器 TYPE_ACCELEROMETER/TYPE_MAGNETIC_FIELD 和 TYPE_ORIENTATION 之间的不同值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4174389/

相关文章:

Android:如何允许基于设备的方向

android - 如何在android中仅在横向模式下旋转显示?

java - 如何模拟 ECG(心电图)

ios - 在 iOS 上读取图像 EXIF 方向的值不正确?

android - 一个在电视和平板电脑上运行的应用程序(带 GPS)

安卓测试应用

android - Showcaseview for Long Press click - Android

android - 可穿戴设备根据方向旋转屏幕

python - 心电信号滤波

java - Android: "setTextFilterEnabled"方法有什么作用?