14

有 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){}
};

请帮忙。这非常令人沮丧。

我必须以这些价值观对待还是我做错了什么?

谢谢。

4

3 回答 3

21

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

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

向量在设备坐标空间中:

设备坐标

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

世界坐标

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

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

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

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

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

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

如果设备向上倾斜 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() 的输入。

于 2012-07-18T20:57:43.780 回答
7

我可能在这里在黑暗中拍摄,但如果我正确理解你的问题,你想知道为什么你得到[-179..179]而不是[0..360]

请注意,与where是一个整数(整数)-180相同+180和相同。180 + N*360N

换句话说,如果您想获得与方向传感器相同的数字,您可以这样做:

// x = orientationValues[0];
// y = orientationValues[1];
// z = orientationValues[2];
x = (x + 360.0) % 360.0;
y = (y + 360.0) % 360.0;
z = (z + 360.0) % 360.0;

这将为您提供所需[0..360]范围内的值。

于 2012-05-14T11:48:43.553 回答
1

您的计算中缺少一项关键计算。
在您执行getRotationMatrix 之后调用remapCoordinateSystem

将其添加到您的代码中,一切都会好起来的。你可以在这里
阅读更多关于它的信息。

于 2011-03-05T06:29:59.703 回答