Android手机定位概述,包括指南针

Tim*_*Tim 106 android orientation bearing device-orientation compass-geolocation

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

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

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

在我做到这一点之前,想象一下,回到原来在上面提到的方向上经纬度为0度的那片想象中的土地上.想象一下,你被蒙住眼睛,你的鞋子固定在游乐场环形交叉路口.如果有人在后面推你,你会向前(向北)前进,然后伸出双手来打破你的摔倒.同样地,如果有人将你的左肩推开,你的右手就会摔倒.你的内耳有"引力传感器" (youtube clip),它可以让你检测你是向前/向后,向左/向下或向下(向上!!).因此,人类可以检测与手机相同的X和Z轴周围的对齐和旋转.

现在想象有人现在在环形交叉口旋转90度,这样你现在面向东方.您正在围绕Y轴旋转.这个轴是不同的,因为我们无法在生物学上检测它.我们知道我们有一定的角度,但我们不知道与行星的磁北极有关的方向.相反,我们需要使用外部工具......磁罗盘.这使我们能够确定我们面临的方向.我们的手机也是如此.

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

这款手机还有一个电子罗盘,其行为类似于普通罗盘 - 它的"虚拟针"指向磁北.的Android合并来自这两个传感器的信息,以使得每当一个SensorEventTYPE_ORIENTATION产生values[3]阵列具有
值[0]:方位角- (磁北的罗盘方位东)
的值[1]:节距,绕x轴旋转(是电话向前或向后倾斜)
值[2]:滚动,围绕y轴旋转(手机在其左侧或右侧倾斜)

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

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

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

B = SensorEvent API使用的坐标系.

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

所以我的理解是A代表"世界的坐标系",我认为它指的是地球上的位置作为(纬度,经度)夫妇与可选的(海拔)给出的方式.X是"东向"坐标,Y是"北向"坐标.Z指向天空并代表高度.

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

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

ano*_*non 26

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

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

在未锁定到单个方向的应用程序中,旋转设备时屏幕坐标系会发生变化.因此,当设备从其默认视图模式旋转时,传感器坐标系不再与屏幕坐标系相同.在这种情况下,旋转矩阵用于将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]);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 只是提到方位角,俯仰和滚动与不推荐使用的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])); }将标准化音高 (4认同)