如何使Android手机方向与人的方向匹配?

31
我正在制作一款地图应用程序,其中包括显示您面向方向的位置箭头,如下所示:

Map with a blue arrow

我直接从SensorManager.getOrientation()获取方向,使用第一个返回值:方位角。 当手机的屏幕指向地平线以上并且处于纵向时,箭头正常工作。 但是:
  • 当手机被持续,以至于屏幕指向地平线以下时,箭头指向与用户朝向相反的方向。
  • 当手机的屏幕与地平线水平时,箭头无法确定其指向的方向。 方位角根本不返回有意义的结果。
  • 当手机向左或向右倾斜(或保持横向模式)时,箭头向左或向右倾斜。
下面仔细构造和科学的图片显示了我的意思(其中蓝色是用户的面向方向,红色是箭头方向,屏幕大致面向用户的脸,Google地图完全符合我的要求):

Graph of what happens vs what I want

(请注意,对于Google地图,如果关闭自动旋转,则不会成功执行列表中的最后两个操作。但是我甚至还没到那个阶段。一次一个问题。) 似乎它只是使用此处所示的Y轴指向方向:http://developer.android.com/reference/android/hardware/SensorEvent.html,但大多数情况下我希望它使用Z轴指向方向的相反方式,当手机水平时使用Y轴。 但是,鉴于getOrientation()返回的值,我必须编写复杂的用例来解决一些问题,并且无法解决手机面向地平线的用例。我确信有更简单的方法。 这是我的代码(其中lastAcceleration和lastMagneticField都来自内部传感器):
float[] rotationMatrix = new float[9];
if(SensorManager.getRotationMatrix(rotationMatrix, null, lastAcceleration, lastMagneticField)){
    float[] orientMatrix = new float[3];
    SensorManager.getOrientation(rotationMatrix, orientMatrix);

    orientation = orientMat[0]*180/(float)Math.PI;
}

我做错了什么?有更简单的方法吗?

编辑:仅澄清,我假设用户将设备拿在面前,屏幕朝向他们。除此之外,我显然无法确定它们中是否只有一个旋转。此外,我正在使用用户移动时的动作,但这是针对他们静止时的情况。


20
+1 对于科学形象的认可 - P.T.
你的问题解决了吗?你是如何获取lastAcceleration和lastMagneticField的? - Nayanesh Gupte
不。从来没有解决过。也许它从始至终都是不可解决的问题,似乎当时的谷歌地图并不像我想象的那么智能。而且我早就把它从我的任务列表中删除了,因为我已经不再为同一个公司工作,更别提同一个项目了。现在的Android可能完全不同了。 - AlbeyAmakiir
5个回答

7
你是否调用了remapCoordinateSystem?否则,只有当手机垂直持有时才能获得正确的前值。对于手机保持屏幕水平的情况,你无法获取用户面对的方向,因为要获取面对的方向,必须将传感器读数的z值投影到世界坐标系中的xy平面上,而当设备水平持有时该值为零。
更准确地说,如果你想获取手机的朝向,则手机必须倾斜至少25度,并且需要调用remapCoordinateSystem。以下代码将提供您所需的最后两张图片。
代码:
float[] rotationMatrix = new float[9];  

if(SensorManager.getRotationMatrix(rotationMatrix, null, lastAcceleration, lastMagneticField)){
    float[] orientMatrix = new float[3];
    float remapMatrix = new float[9];
    SensorManager.remapCoordinateSystem(rotationMatrix, SensorManager.AXIS_X, SensorManager.AXIS_Z, remapMatrix);
    SensorManager.getOrientation(remapMatrix, orientMatrix);

    orientation = orientMat[0]*180/(float)Math.PI;
}  

在手机平放的情况下,getOrientation提供给你正确的值。因此,如果手机是竖直的,则必须重新映射坐标以获得平面位置。从几何角度来看,您将手机-z轴投影到世界xy平面上,然后计算该投影向量与世界y轴之间的角度。


我不确定我是否理解了。或者也许你没有。当屏幕水平时,获取方向并不是不可能的,因为这是Google所做的,也是我现在拥有的。我想在手机垂直时获取面向。而且,没有,我还没有调用remapCoordinateSystem。文档并不完全清楚如何使用它。 - AlbeyAmakiir
如果手机屏幕朝向水平线,你可以获取指南针方向,但无法获取手机的朝向。也就是说,手机面朝地面,所以谈论北、南等没有意义...你可以获取“指南针方向”,但这个方向取决于手机的方向。也就是说,如果手机在你面前以纵向模式放置,并且指南针显示为0度,那么当你旋转到横向时,指南针将显示90或270度,具体取决于你旋转的方向。 - Hoan Nguyen

1

你应该获取手机的旋转角度,并确定用户是否将手机竖直向上。

我选择在手机与水平桌面成45度倾斜后,重新映射坐标系。

if (Math.round(Math.toDegrees(-orientation[1])) < 45 && Math.round(Math.toDegrees(-orientation[1])) > -45) {
//do something while the phone is horizontal
}else{
//R below is the original rotation matrix
float remapOut[] = new float[9];
SensorManager.remapCoordinateSystem(R, SensorManager.AXIS_X, SensorManager.AXIS_Z, remapOut);
//get the orientation with remapOut
float remapOrientation[] = new float[3];
SensorManager.getOrientation(remapOut, remapOrientation);

它运行得相当不错。 如果有人能提出改进意见,请告诉我。谢谢。


1

我建议应用以下重新映射。在我的情况下,它适用于前三种情况:

SensorManager.remapCoordinateSystem(rMat,SensorManager.AXIS_Z,SensorManager.AXIS_Y,rMatRemapped);

1

当用户竖直地握住手机时,获取方位角的适当方法似乎是使用类似于以下内容的东西:

// after calling getRotationMatrix pass the rotationMatix below:
SensorManager.remapCoordinateSystem(inR, AXIS_X, AXIS_Z, outR);

如果您想处理垂直和横向两种方式,您可能需要检测并在垂直时执行此重新映射。

请参阅API文档这里


0

这看起来相当棘手。我正在为Android开发PNS,面临一个类似的问题,仍然需要一些帮助:如何获取加速度计轴和运动向量之间的旋转?

问题在于,如果用户没有移动,对我来说似乎绝对不可能找到他面对的方向(而不是设备)。也就是说,人体上没有传感器,那么如果设备保持绝对相同的位置,但用户旋转了90°呢?我看不出有任何方法可以找到这个。

我能提出的建议(实际上适用于我的问题)是,您可以(我不知道您在代码中实际做什么)使用用户的运动来确定他的方向。让我解释一下。假设您有第一个位置A。用户去B。然后,您可以构建AB向量并在他停留在B时获取用户的方向。然后,您必须将代码限制为他到达目的地时所面对的方向。

我知道这不像Google Maps得到的那样好,但是您知道Google用于此的是什么吗?我的意思是他们只使用加速度计和磁场传感器吗?


我假设用户将设备拿在面前,屏幕朝向他们。除此之外,我就无能为力了。另外,我会考虑用户的动作。一旦他们停止,加速度计和磁场就会接管。 - AlbeyAmakiir

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