在Android中获取方向向量

13

如何获取一个方向向量,表示设备背面相对于地球坐标系的朝向?

例如,如果将设备放在桌子上(屏幕朝上),它应该显示[0,0,-1],如果竖直朝北,则应该显示[1,0,0]等。

我知道如何根据方位、俯仰和翻转计算出它,只要这些值是相对于地球坐标系的。需要明确的是,我这里不是要求角速度,而是要求实际与切线平面相对的当前角度。因此,如果设备垂直且朝北,角度“alpha”应为0或360,角度“beta”应为90,“gamma”应为0。我也无法找到如何获取这些值。

我已经整天阅读API,但仍然找不到如何获取这两个值。

public void onSensorChanged(SensorEvent event) {
    // ?    
}

感谢任何见解。

2个回答

9

SensorManager.getRotationMatrix()执行以下操作,这是在我发现这一点之前编写的。 我将保留添加的解释,因为如果您想要校正磁北和真北之间的差异,仍然需要它。

大致算法是获取旋转矩阵,将向量[0,0,-1]乘以它,然后将其调整为您的坐标系。 为什么? Android文档提供了设备和世界的坐标系统

deviceworld

请注意,在Android设备坐标中,[0,0,-1]指向屏幕向后垂直。 如果您将旋转矩阵R与此向量相乘,则当设备朝上放置在桌子上时,您将在世界坐标中获得[0,0,-1]。 当它竖直朝北时,您将获得[0,-1,0],这表明您选择了一个坐标系,其中xy与Android系统相对交换,但这只是约定的变化。

请注意,R * [0,0,-1]^T只是R的第三列取反。 从这里得到伪代码:

getRotationMatrix(R);
Let v = first three elements of third column of R.
swap v[0] and v[1]

这应该可以得到你想要的东西。
下面是关于getRotationMatrix()在做什么的附加信息。
需要加速度计数据来确定方向“向下”,并需要磁力计数据来确定方向“向北”。你必须假设加速度计仅感测到重力(设备静止或以稳定速度移动)。然后,您需要将磁力计向量投影到垂直于重力向量的平面上(因为磁场通常不切于地球表面)。这给您两个轴。第三个轴是正交的,因此可以通过叉积计算。这就为您提供了设备系统中的地球坐标向量。看起来你需要的是反过来的:设备坐标在地球坐标中。对此只需构造方向余弦矩阵并求逆即可。
我要补充说明,以上讨论假定磁力计向量指向北。我认为(从高中科学!)实际上是朝磁南方向,但手头没有设备,所以无法尝试。当然,磁北/磁南与真实方向差别为零至180度,具体取决于您在地球上的位置。您可以检索GPS坐标并计算实际偏移量。
如果您不熟悉执行这些操作所需的数学知识,我可以进一步解释,但需要稍后进行。

非常感谢,这真的很有帮助。你们都给出了如此好的答案,我希望有一种方法可以同时选择它们。看起来我可以通过Sensor.TYPE_GRAVITY轻松获取重力向量 - 他们已经为我完成了这项工作。但我希望能够像获取“北”向量那样容易。 - Joey
最终我通过您的解释得到了它。原来我直接将重力传感器和磁场传感器返回的值传递给getRotationMatrix(),然后它就可以计算出一切。然后只需将该矩阵乘以[0,0,-1],就可以得到dv。 - Joey

9

阅读此页面:http://developer.android.com/reference/android/hardware/Sensor.html

在API 8及以上版本中,有“虚拟”传感器,它们通过组合所有有效传感器和适当的滤波器来生成。 "TYPE_ORIENTATION"传感器可以给出设备的整体方向,但由于某些方向的失败状态,该接口已被弃用。新的传感器是TYPE_ROTATION_VECTOR(API 9及以上),它将您的设备方向作为四元数给出。这确实是最好的传感器,但其中的数学运算有点复杂。

若不行,您可以调用SensorManager.getRotationMatrix(),并传递最新的重力和磁力计数据。这将返回一个旋转矩阵,该矩阵可用于将矢量从设备坐标转换到世界坐标或反之亦然(只需转置矩阵即可倒置)。

getOrientation()函数可以提供航向、俯仰角和横滚角,但这些与TYPE_ORIENTATION传感器具有相同的失败状态。

Examples:

  Device flat on a table, top facing north:
    1  0  0
    0  1  0
    0  0  1

  Tilted up 30 degrees (rotated about X axis)
    1   0      0
    0   0.86  -0.5
    0   0.5    0.86

  Device vertical (rotated about X axis), facing north:
    1  0  0
    0  0 -1
    0  1  0

  Device flat on a table, top facing west:
    0 -1  0
    1  0  0
    0  0  1

  Device rotated about its Y axis, onto its left side, top
  facing north:
    0  0 -1
    0  1  0
    1  0  0

这里是一些你可能会发现有用的示例代码:

public void onSensorChanged(SensorEvent event) {
    long now = event.timestamp;     // ns

    switch( event.sensor.getType() ) {
      case Sensor.TYPE_ACCELEROMETER:
        gData[0] = event.values[0];
        gData[1] = event.values[1];
        gData[2] = event.values[2];
        break;
      case Sensor.TYPE_MAGNETIC_FIELD:
        mData[0] = event.values[0];
        mData[1] = event.values[1];
        mData[2] = event.values[2];
        haveData = true;
        break;
    }

    if( haveData ) {
        double dt = (now - last_time) * .000000001;

        SensorManager.getRotationMatrix(R1, Imat, gData, mData);
        getOrientation(R1, orientation);
        pfdView.newOrientation(orientation[2], (float)dt);


        Log.d(TAG, "yaw: " + (int)(orientation[0]*DEG));
        Log.d(TAG, "pitch: " + (int)(orientation[1]*DEG));
        Log.d(TAG, "roll: " + (int)(orientation[2]*DEG));

        acft.compass = orientation[0];

        last_time = now;
    }
}

谢谢您的回复 - 我有两个问题,如果我使用TYPE_ROTATION_VECTOR,四元数是以世界坐标表示的吗?它已经被集成了吗,还是我需要将其集成以获得当前向量? - Joey
设备的世界坐标,我相信。已经转换为单位四元数,有时省略w项。老实说,我自己从未使用过它。我展示的代码来自我为1.5编写的飞行导航应用程序,在新的传感器类型出现之前。 - Edward Falk

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