如何获得表示设备背面相对于地球坐标指向方向的方向向量?
例如,如果放在桌子上(屏幕朝上),它应该读取[0,0,-1],如果垂直朝北,则应该读取[1,0,0]等。
我知道如何从航向,俯仰和滚转计算它,只要它们相对于地球坐标。要清楚这里,我不是在寻找角速度,而是寻找相对于与地球相切的平面的实际当前角度。因此,如果设备垂直保持朝北,角度“alpha”应为0或360,角度“beta”应为90,“gamma”应为0.我无法弄清楚如何获取这些值任
我整天都在阅读API,但我仍然无法找到如何获得这些内容。
public void onSensorChanged(SensorEvent event) {
// ?
}
感谢您的任何见解。
答案 0 :(得分:9)
SensorManager.getRotationMatrix()
执行下面列出的内容,在我发现之前编写。我将留下额外的解释,因为如果你想纠正磁性和真北之间的差异,你仍然需要它。
粗略算法是获取旋转矩阵,乘以它的向量[0,0,-1]
,然后将其调整到您的坐标系。为什么? Android文档为设备和世界提供坐标系
注意Android设备中的[0,0,-1]
坐标垂直向后远离屏幕。如果将旋转矩阵R乘以此向量,当设备在桌面上朝向您时,您将获得[0,0,-1]
世界坐标。当它直立朝北时,你会得到[0,-1,0]
,这表示你已经选择了一个坐标系,其中x
和y
相对于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()
正在做什么的其他信息如下。
您需要两个加速度计数据来确定方向“向下”和磁力计数据以确定方向“北”。您将不得不假设加速度计仅感测重力(设备静止或以稳定速度移动)。然后,您需要将磁力计矢量投影到垂直于重力矢量的平面上(因为磁场通常不与地球表面相切)。这给你两个轴。第三个是正交的,因此可以通过叉积计算。这为您提供了设备系统中的地球坐标向量。看起来你想要地球坐标中的逆:设备坐标。为此,只需构造方向余弦矩阵并反转。
我将补充说,上面的讨论假设磁力计矢量指向北方。我认为(从高中科学!)它实际上是朝向磁性南方,但手头没有设备所以无法尝试。当然,根据你在地球上的位置,磁北/南与真实不同,为0到180度。您可以检索GPS坐标并计算实际偏移量。
如果您不熟悉完成这些操作所需的数学,我可以进一步解释,但必须稍后解释。
答案 1 :(得分:8)
阅读本页: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;
}
}