Android 手机方向概览,包括指南针

Posted

技术标签:

【中文标题】Android 手机方向概览,包括指南针【英文标题】:Android phone orientation overview including compass 【发布时间】:2011-01-27 17:20:33 【问题描述】:

一段时间以来,我一直试图了解 android 方向传感器。 我以为我明白了。然后我意识到我没有。现在我想(希望)我再次对它有更好的感觉,但我仍然不是 100%。我将尝试解释我对它的不完整理解,如果我有部分错误或填写任何空白,希望人们能够纠正我。

我想我站在经度 0 度(本初子午线)和纬度 0 度(赤道)。这个位置实际上是在非洲海岸附近的海域,但请耐心等待。我把手机放在脸前,让手机底部指向我的脚;我面向北方(朝向格林威治),因此电话的右侧指向东方,指向非洲。在这个方向上(参考下图)我的 X 轴指向东方,Z 轴指向南方,Y 轴指向天空。

现在,手机上的传感器可让您在这种情况下计算出设备的方向(而不是位置)。这部分一直让我感到困惑,可能是因为在我接受它确实有效之前,我想了解它是如何工作的。手机似乎使用两种不同技术的组合来确定其方向。

在我开始之前,想象一下站在那块假想的 0 度纬度和经度的土地上,站在上面提到的方向。还想象一下,你被蒙上眼睛,你的鞋子被固定在操场上的环形交叉路口。如果有人从背后推你,你会向前(朝北)摔倒并伸出双手来阻止摔倒。同样,如果有人推你的左肩,你会用右手摔倒。你的内耳有“重力传感器”(youtube clip),它可以让你检测到你是向前/向后跌倒,还是向左/向右跌倒或跌倒(或向上!!)。因此,人类可以检测到与手机相同的 X 轴和 Z 轴周围的对齐和旋转。

现在想象有人现在在环形交叉路口将您旋转 90 度,这样您现在面向东方。您正在绕 Y 轴旋转。这个轴是不同的,因为我们无法从生物学上检测到它。我们知道我们有一定的角度,但我们不知道相对于地球磁北极的方向。 相反,我们需要使用外部工具……磁罗盘。这使我们能够确定我们所面对的方向。我们的手机也是如此。

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

手机还有一个电子罗盘,其行为类似于普通罗盘——它的“虚拟指针”指向磁北。 Android 会合并来自这两个传感器的信息,以便每当生成 TYPE_ORIENTATIONSensorEvent 时,values[3] 数组就有 values[0]: 方位角 - (指南针方位在磁北以东) values[1]:俯仰、绕 x 轴旋转(手机是向前还是向后倾斜) values[2]:滚动,绕y轴旋转(手机是靠左还是靠右)

所以我认为(即我不知道)Android 给出方位角(罗盘方位)而不是第三个加速度计读数的原因是罗盘方位更有用。我不确定他们为什么不推荐使用这种类型的传感器,因为现在看来您需要在系统中为SensorEvents 类型为TYPE_MAGNETIC_FIELD 注册一个侦听器。事件的value[] 数组需要传递给SensorManger.getRotationMatrix(..) 方法以获取旋转矩阵(见下文),然后将其传递给SensorManager.getOrientation(..) 方法。 有谁知道为什么 Android 团队不推荐使用Sensor.TYPE_ORIENTATION?是效率问题吗?这就是其中一个 cmets 与类似的 question 所暗示的内容,但您仍然需要在 development/samples/Compass/src/com/example/android/compass/CompassActivity.java 示例中注册不同类型的侦听器。

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

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

B = Coordinate system used by the SensorEvent API.

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

所以我的理解是 A 代表“世界坐标系”,我认为它是指地球上的位置以(纬度,经度)和可选(高度)的形式给出的方式。 X 是"easting" 坐标,Y 是"northing" 坐标。 Z 指向天空,代表高度。

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

好的。我想我现在最好停下来。就像我之前说的,我希望人们会告诉我我在哪里搞砸或帮助了人们(或者让人们更加困惑!)

【问题讨论】:

我真的很喜欢这个问题。我无法回答,但我喜欢它。 蒂姆,你得到答案了吗?我也一直在挠头。这是我见过的文档记录最差的 API 之一。 恐怕不是。我不得不继续前进。总有一天我会回到这个问题。 Here 我也有同样的问题,差不多吧?还有Response, solution。在 Github 上制作 public my code。 我想知道同样的事情,我已经在 android 设备上实现了一个指南针,如果我从互联网上获得帮助,它工作正常,但令人困惑的是......假设我的设备放在地面上,面向我,它指向北方,现在我拿起​​手机,垂直放在头顶上方,脸仍然朝向我。首先,针是否应该改变它的方向以及为什么。根据我的想法,它不应该因为我没有改变我的方向,但它正在我的应用程序和我下载的所有其他应用程序中发生变化。谁能解释一下为什么? 【参考方案1】:

您可能想查看One Screen Turn Deserves Another 文章。它解释了为什么需要旋转矩阵。

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

在未锁定为单一方向的应用程序中,屏幕坐标系会在您旋转设备时发生变化。因此,当设备从其默认视图模式旋转时,传感器坐标系不再与屏幕坐标系相同。本例中的旋转矩阵用于将 A 转换为 C(B 始终保持固定)。

这是一个代码 sn-p 向您展示如何使用它。

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]);
        
    

【讨论】:

只是提到方位角、俯仰角和滚动角与从已弃用的方向传感器中出来的不同。 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])); 将归一化俯仰 @RafaelT 并标准化滚动?还是说没有意义? @RafaelT:您的方位角标准化似乎有影响:值从 [-180,180] 变为 [0, 360]。但是我得到的音高值已经在 [-90,90] 所以你建议的标准化没有效果。 无论我如何移动平板电脑,如果检查 (gravity != null && geomag != null) 后,geomag 的值始终为 0 是什么意思?可能是没有 geomag 传感器的平板电脑?【参考方案2】:

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

俯仰是相同的,向上 90 度俯仰会将所有重力分量放入 y 寄存器中。

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

这就是为什么你需要一个指南针来评估,也许这有道理?

【讨论】:

【参考方案3】:

看看这个:***.com: Q.5202147

在 3 个图表 A、B、C 之前,您似乎大多是正确的。 之后你就搞糊涂了。

【讨论】:

【参考方案4】:

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

仍然在横向,但设备背面放置,我的传感器给出 0-360。 在纵向模式下,它可以在 0-360 度范围内平躺和竖直站立。

希望对某人有所帮助

【讨论】:

以上是关于Android 手机方向概览,包括指南针的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Xamarin.Android 中获取指南针方向

在 Android 上查找指南针方向

我可以使用 Javascript 获取 iOS 和 Android 的指南针方向吗?

快速查找 iPhone 的指南针方向

快速查找iPhone的指南针方向

手机指北针 + Python绘制徒步路线图