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

15

获取3个旋转值(方位、俯仰和滚动)的方法有两种。

第一种是注册一个类型为TYPE_ORIENTATION的监听器。这是最简单的方法,我可以从每次旋转中获得正确范围的值,就像文档中所说的那样: 方位:[0, 359] 俯仰:[-180, 180] 滚动:[-90, 90]

另一种方法则是最精确但也是最难理解的一种方式,Android建议使用,因此我想使用它,但我却得到了不同的值。

方位:[-180, 180]。-180/180表示南,0表示北,90表示东,-90表示西。
俯仰:[-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){}
};

请帮忙。这真的很令人沮丧。

我需要处理那些值吗?还是我做错了什么?

谢谢。


1
我已经自己在这方面工作了大约两周了。你的代码看起来是正确的(根据我能找到的文档),但正如你所指出的,它与TYPE_ORIENTATION传感器结果不匹配。检查orientationValues[0]是否为负值并添加它似乎是一件简单的事情。但那还不够。你没有显示传感器更新的频率。我发现更快的更新会产生更好的结果,即使TYPE_ORIENTATION结果似乎相当稳定。如果你想一起工作,请联系davemac327@gmail.com - Dave MacLean
所有传感器的速度都是GAME,但我不认为问题与速度有关。很奇怪,因为所有博客、论坛(以及我正在阅读的书!)都以相同的方式实现传感器。 - Gabriel Llamas
3
大家都在讲如何使用加速度计和磁场传感器,但没有人说返回的值与TYPE_ORIENTATION不同,即使是官方文档也是如此。干得好,等待回答... - Gabriel Llamas
请看一下我的问题这里,或许会有用。 - Tim
3个回答

22
我知道我在这里是在玩线程复活术,但我最近一直在研究这个东西,所以我想分享我的看法。
该设备不包含指南针或倾斜仪,因此不能直接测量方位角、俯仰角或横滚角。(顺便说一下,我们称之为欧拉角)。相反,它使用加速度计和磁力计,两者都产生三维空间的XYZ向量。这些向量用于计算方位角等值。
向量在设备坐标空间中:

Device coordinates

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

World coordinates

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

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

通过SensorManager.getRotationMatrix()数学组合加速度计向量和磁力计向量,返回一个3x3矩阵,将设备坐标系中的向量映射到世界坐标系或反之。对于处于中性位置的设备,此函数将返回单位矩阵。

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

SensorManager.getOrientation()使用转换矩阵计算方位角、俯仰角和横滚角值。这些值是相对于处于中性位置的设备而言的。

我不知道调用此函数和使用TYPE_ORIENTATION传感器有什么区别,除了此函数可以让您先操作矩阵。

如果设备向上倾斜90度或接近90度,欧拉角的使用将失效。这在数学上是一个退化的情况。在这种情况下,设备如何知道您是否正在更改方位角或翻滚角?
函数SensorManager.remapCoordinateSystem()可用于操纵转换矩阵以补偿您可能了解的设备方向。然而,我的实验表明,这并不涵盖所有情况,甚至包括一些常见情况。例如,如果您想为直立设备重新映射(例如拍照),则需要将转换矩阵乘以此矩阵:
1 0 0
0 0 1
0 1 0

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

好的,这一切都是为了说,如果您正在使用方向传感器或从getOrientation()获取方向信息,则可能使用不正确。实际上,您只有在以用户友好的形式显示方向信息,注释照片,驱动飞行仪表盘或进行类似操作时才需要欧拉角。

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

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

回顾你最初的问题,getOrientation() 应该返回三个值在 [-180 180] [-90 90] 和 [-180 180] 之间(在从弧度转换后)。实际上,我们认为方位角是在 [0 360) 内的数字,所以你应该将任何负数加上 360。你的代码看起来写得很正确。如果我知道你期望得到什么结果以及你得到了什么结果,那将有所帮助。
编辑后添加:还有一些想法。现代 Android 版本使用称为“传感器融合”的东西,它基本上意味着所有可用的输入 - 加速计、磁力计、陀螺仪 - 都被组合在一起放入一个数学黑盒中(通常是卡尔曼滤波器,但取决于供应商)。所有不同的传感器 - 加速度、磁场、陀螺仪、重力、线性加速度和方向 - 都被视为这个黑盒的输出。
尽可能使用 TYPE_GRAVITY 而不是 TYPE_ACCELEROMETER 作为 getRotationMatrix() 的输入。

谢谢您的解释,请问您能指导一下这个问题吗?http://stackoverflow.com/questions/27137239/get-moving-device-direction-without-gps - Arash
1
我已经回答了那个问题。你可能不会喜欢它;这是一个非常非常难解决的问题。 - Edward Falk

7

我可能是在胡乱猜测,但如果我正确理解你的问题,你想知道为什么你得到的是[-179..179]而不是[0..360]

请注意-180+180相同,也与180 + N*360相同,其中N是整数。

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

// 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]范围内。


1
我不记得我是怎么解决这个问题的 xD,但是+1给你!:P - Gabriel Llamas

1

在你的计算中,你缺少了一个关键的计算。
在你执行getRotationMatrix之后,需要调用remapCoordinateSystem

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接