Android手机方向概述(包括指南针)


107

我一直试图绕过Android方向传感器。我以为我明白了。然后我意识到自己没有。现在我想(希望)我再次感觉更好,但我仍然不是100%。我将尝试解释我对此的零散理解,并希望如果我在部分内容上有误或填写任何空白,人们将能够纠正我。

我想象我正站在经度0度(本初子午线)和纬度0度(赤道)上。这个位置实际上是在非洲沿海的大海中,但请耐心等待。我将手机握在脸前,使手机的底部指向我的脚;我正对着北方(正对格林威治),因此电话的右侧将东方指向非洲。在这个方向上(参考下图),我的X轴指向东方,Z轴指向南方,Y轴指向天空。

现在,手机上的传感器使您可以在这种情况下确定设备的方向(而不是位置)。这部分总是让我感到困惑,可能是因为在我接受某项功能之前,我想先了解一下它们是如何工作的。似乎手机结合了两种不同的技术来确定其方向。

在开始之前,请想象一下,以上述方向站在纬度和经度为0度的那片假想土地上。还要想象一下,您被蒙住双眼并将鞋子固定在游乐场的环形交叉路口上。如果有人将您向后推,您将跌倒(朝北)并伸出双手来摔倒。同样,如果有人将您的左肩推开,您的右手就会摔倒。您的内耳具有“重力感应器” (youtube片段),可让您检测自己是向前/向后跌倒,还是向左/向右跌倒或向下(或向上!)跌倒!因此,人类可以检测到与手机相同的X和Z轴周围的对齐和旋转。

现在,假设有人现在在回旋处将您旋转90度,因此您现在正对着东方。您正在绕Y轴旋转。该轴是不同的,因为我们无法对其进行生物学检测。我们知道我们成一定角度,但我们不知道相对于地球磁北极的方向。相反,我们需要使用外部工具……电磁罗盘。这使我们能够确定我们所面对的方向。我们的手机也是如此。

现在,手机还具有3轴加速度计。我没有知道它们实际上是如何工作的,但我将其可视化的方式是将重力想象为恒定且均匀的“雨”从天上掉下来,并想象上图中的轴为可以检测流过的雨量的管子。当手机直立时,所有的雨水都会流过Y型“管”。如果逐渐旋转手机,使其屏幕面向天空,则通过Y的雨水量将减少至零,而通过Z的雨量将稳步增加,直到最大量的雨水通过。同样,如果我们现在将手机倾斜到其侧面,则X形管最终将聚集最大的雨量。因此,根据手机的方向,通过测量流经3根管子的雨水量,可以计算出方向。

该电话还具有一个电子指南针,其行为类似于普通指南针-它的“虚拟针”指向磁性北极。Android会合并来自这两个传感器的信息,以便每当生成一个SensorEventof 时,TYPE_ORIENTATIONvalues[3]数组便会具有
值[0]:方位角-(位于北磁北的罗盘)
值[1]:螺距,绕x轴旋转(是电话)向前或向后倾斜)
值[2]:围绕y轴滚动,旋转(手机向左或向右倾斜)

因此,我认为(即我不知道)Android提供方位角(罗盘方位角)而不是读取第三个加速度计的原因是罗盘方位角更有用。我不确定他们为什么不赞成使用这种类型的传感器,因为现在看来您需要在系统中为SensorEventtype 为的注册一个侦听器TYPE_MAGNETIC_FIELDvalue[]需要将事件的数组传递到SensorManger.getRotationMatrix(..)方法中以获得旋转矩阵(请参见下文),然后将其传递到SensorManager.getOrientation(..)方法中。有谁知道为什么Android团队不赞成使用Sensor.TYPE_ORIENTATION?这是效率的事情吗?这是对类似问题的评论中所隐含的含义,但是您仍然需要在该注释中注册其他类型的侦听器。开发/示例/指南针/src/com/example/android/compass/CompassActivity.java示例。

我现在想谈谈旋转矩阵。(这是我最不确定的地方)因此,在上面我们有Android文档中的三个数字,我们将它们称为A,B和C。

A = SensorManger.getRotationMatrix(..)方法图,它表示世界坐标系

B = SensorEvent API使用的坐标系。

C = SensorManager.getOrientation(..)方法图

因此,我的理解是A代表“世界坐标系”,我认为这是指行星上的位置以(纬度,经度)加上可选的(海拔)的方式给出的方式。X是“东”坐标,Y是“北”坐标。Z指向天空并代表高度。

手机坐标系如图B所示是固定的。其Y轴始终指向顶部。手机会不断计算旋转矩阵,并允许在两者之间进行映射。因此,我是否认为旋转矩阵将B的坐标系转换为C是正确的?因此,当您调用SensorManager.getOrientation(..)方法时,将使用values[]与图C相对应的值的数组。当电话指向天空时,旋转矩阵为单位矩阵(矩阵数学等价为1),这意味着在对齐设备时不需要映射与世界坐标系。

好。我想我最好现在停止。就像我之前说过的那样,我希望人们能告诉我我在哪里弄糟或帮助了人们(或使人们更加困惑!)。


25
我真的很喜欢这个问题。我无法回答,但我喜欢。
Octavian A. Damiean 2011年

4
蒂姆,你有没有得到答案?我一直在挠头。这是我见过的记录最差的API之一。
Pierre-Luc Paour 2011年

真的不担心。我不得不继续前进。有一天我会回到这个问题。

1
在这里,我有同样的问题,差不多吗?还有响应,解决方案。做公共我的代码在Github上。

我也想知道同样的事情,如果我从互联网上获得帮助,我已经在Android设备上实现了指南针,并且工作正常,但是令人困惑的是...假设我的设备放在地面上对着我,它指向北,现在我拿起​​手机,将手机垂直放在头顶上方,而脸仍然朝着我。首先,针应该改变方向,为什么。按照我的看法,我不应该改变我的方向,但是我的应用程序和我下载的所有其他应用程序都在改变。谁能解释为什么?
Syed Raza Mehdi 2014年

Answers:


26

您可能想要查看“ 一屏转值得另一篇” 文。它说明了为什么需要旋转矩阵。

简而言之,即使旋转设备,手机的传感器也始终使用相同的坐标系。

在未锁定到单个方向的应用程序中,旋转设备时,屏幕坐标系会更改。因此,当设备从其默认查看模式旋转时,传感器坐标系不再与屏幕坐标系相同。在这种情况下,旋转矩阵用于将A转换为C(B始终保持固定)。

这是一个代码片段,向您展示如何使用它。

SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE);

// Register this class as a listener for the accelerometer sensor
sm.registerListener(this, sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                    SensorManager.SENSOR_DELAY_NORMAL);
// ...and the orientation sensor
sm.registerListener(this, sm.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
                    SensorManager.SENSOR_DELAY_NORMAL);

//...
// The following code inside a class implementing a SensorEventListener
// ...

float[] inR = new float[16];
float[] I = new float[16];
float[] gravity = new float[3];
float[] geomag = new float[3];
float[] orientVals = new float[3];

double azimuth = 0;
double pitch = 0;
double roll = 0;

public void onSensorChanged(SensorEvent sensorEvent) {
    // If the sensor data is unreliable return
    if (sensorEvent.accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE)
        return;

    // Gets the value of the sensor that has been changed
    switch (sensorEvent.sensor.getType()) {  
        case Sensor.TYPE_ACCELEROMETER:
            gravity = sensorEvent.values.clone();
            break;
        case Sensor.TYPE_MAGNETIC_FIELD:
            geomag = sensorEvent.values.clone();
            break;
    }

    // If gravity and geomag have values then find rotation matrix
    if (gravity != null && geomag != null) {

        // checks that the rotation matrix is found
        boolean success = SensorManager.getRotationMatrix(inR, I,
                                                          gravity, geomag);
        if (success) {
            SensorManager.getOrientation(inR, orientVals);
            azimuth = Math.toDegrees(orientVals[0]);
            pitch = Math.toDegrees(orientVals[1]);
            roll = Math.toDegrees(orientVals[2]);
        }
    }
}

4
只需提及方位角,俯仰和横滚与从已弃用的OrientationSensor中得出的结果不同。orientation[0] = orientation[0] >= 0 ? orientation[0]: orientation[0] + 360;将规范化方位角if (orientation[1] <= -90) { orientation[1] += (-2*(90+orientation[1])); } else if(orientation[1] >= 90){ orientation[1] += (2*(90 - orientation[1])); }并将规范化音调
拉斐尔T

@RafaelT和归一化辊?还是没有意义?
马赛厄斯

@RafaelT:您对方位角的规范化似乎会产生影响:值从[-180,180]变为[0,360]。但是我得到的音高值已经在[-90,90]中,因此您建议的归一化无效。
马提亚斯

如果检查后(gravity!= null && geomag!= null)之后,无论我如何移动平板电脑,geomag的值始终为0,这是什么意思?平板电脑可能没有Geomag传感器吗?
先进的

3

滚动是重力的函数,90度滚动会将所有重力放入x寄存器。

节距相同,向上倾斜90度会将所有重力分量放入y寄存器。

偏航/航向/方位角对重力没有影响,它始终与重力成直角,因此无论您面对重力的哪种方式都可以测量。

这就是为什么您需要一个指南针进行评估的原因,也许这很有意义?



0

我遇到了这个问题,所以我规划了在不同方向发生的事情。如果设备以横向方式安装,例如在汽车上安装,则指南针的“度”似乎在269(在西和北之间)上方从0-275(顺时针方向)运行,则从-90倒数到0,然后从0转发到269。270变为-90

仍处于风景中,但将设备放在其背面,我的传感器给出0-360。在纵向模式下,它可以以0-360的速度运行,既可以仰卧,也可以纵向站立。

希望可以帮助某人

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.