我将给出几何解释getOrientation
并解释如何仅使用计算所有类型的旋转RotationMatrix
。如果你明白什么getOrientation
那么就没有必要使用它,如果你不这样做,那么使用它会给你带来各种麻烦。
具体来说,它将回答 Stack Exchange 上发布的许多有关方向的问题,例如
- 在纵向模式下为平板电脑编写指南针。为什么打电话后
getRotation
需要加上90度才能得到正确答案。
- 为什么
RemapCoordinateSystem
必须在调用之前调用getOrientation
获取后置摄像头的方向(设备 z 轴的负方向)。
- 为什么打电话后
RemapCoordinateSystem
进而getOrientation
为了正确获取设备 z 轴的方向,当设备平坦时,结果不再有意义。
- 如何计算绕任何设备轴的旋转,与设备位置无关,即平坦或不平坦。
首先我需要解释一下RotationMatrix
在解释之前更详细地说明它可以做什么getOrientation
这里需要考虑 2 个坐标系。
一种是世界坐标系,x轴指向东方,y轴指向北方,z轴指向天空。
另一种是设备坐标系,其中 x 轴是手机上的短边(平板电脑上的长边),y 轴是手机上的长边(平板电脑上的短边),z 轴是正交向量到屏幕上指着你。
从数学上讲,我们的对象是 3 维实向量空间,我们对该向量空间使用以下基数。
世界基础W = {E, N, SKY}
where
E
是位于东方向的单位向量
N
是位于北方向的单位向量
SKY
是位于天空方向的单位向量
设备基础D = {X, Y, Z}
where
X
是位于手机短边方向的单位向量(平板电脑为长边)
Y
是位于手机长边方向(平板电脑为短边)的单位向量
Z
是一个单位向量,位于与指向您的屏幕正交的方向上
对于那些忘记什么是基的人来说,它意味着每个向量v在3个空间中可以写成
以世界为基础
v = a_1 E + a_2 N + a_3 SKY其中 a_1、a_2、a_3 是实数
通常写为 (a_1, a_2, a_3)_W 或只是 (a_1, a_2, a_3)。这 3 个元组称为坐标v相对于世界基础或坐标v隐含的基础被理解为世界基础
在设备基础上
v = b_1 X + b_2 Y + b_3 Z其中 b_1、b_2、b_3 是实数
通常写为 (b_1, b_2, b_3)_D 或只是 (b_1, b_2, b_3)。这 3 个元组称为坐标v相对于设备基础或只是坐标v基础隐含地理解为设备基础。传感器返回的值以设备为基础,例如以设备为基础编写的加速度计返回为值[0]、值[1]和值[2]
acc= 值[0]X+ 价值观[1]Y+ 价值观[2]Z
尤其
X = 1 X + 0 Y + 0 Z
Y = 0 X + 1 Y + 0 Z
Z = 0 X + 0 Y + 1 Z
这是坐标X, Y and Z相对于设备基础分别为 (1, 0, 0)、(0, 1, 0) 和 (0, 0, 1)。您将看到如何使用这些向量来解释计算getOrientation
later.
请注意,世界基准是固定的,但设备基准会随着手机位置的变化而变化。这就是单位向量X, Y, Z随着设备位置的变化而变化。它们的定义方式相同,但当位置改变时它们是不同的向量。你仍然把它们写成X, Y, Z但他们不同X, Y, Z.
因此,如果手机静止不动,作用在手机上的唯一力就是重力,因此理论上加速度计矢量是位于世界天空轴上的矢量,即在世界基础上的坐标为 (0, 0, g)。在设备基础上,对于某些 a_1、a_2 和 a_3 来说,它是 (a_1, a_2, a_3)。如果设备以不同的方向静止,则加速度计仍然相同,即世界基准中的 (0, 0, g),但现在设备基准中的 (b_1, b_2, b_3),其中至少有一个 a 与b 的。
其中参数为getRotationMatrix
是gravity
and geomagnetic
. gravity
被假定为真实的gravity
也就是说,它的坐标是世界基准中的 (0, 0, k),并且假设地磁位于世界 N-Sky 平面上,也就是说,它的坐标是世界基准中的 (0, a, b)。
有了这些假设,让我们看看如何Rotation Matrix
计算为getRotationMatrix
。这个方法尝试的是获得一个矩阵M这样,给定设备基础上坐标为 (a_1, a_2, a_3) 的任何向量的乘积M(a_1, a_2, a_3)_T(转置)给出世界基础上的坐标(b_1, b_2, b_3)。从数学上来说getRotationMatrix
计算基矩阵的变化。
传入的参数为gravity
g应该是从获得的值onSensorChanged
for TYPE_GRAVITY
或低通滤波器TYPE_ACCELEROMETER
并为geomagnetic
m应该是来自的值TYPE_MAGNETIC_FIELD
g = a_1 X + a_2 Y + a_3 Z
m = m_1 X + m_2 Y + m_3 Z
不失一般性假设g and m已经正常化了,这是常态g and m等于 1。
我们现在要做的是编写World基础{E, N, SKY}
在设备基础上。
Since g假设为重力,即世界基础中的 (0, 0, 1) 这正是向量SKY.
SKY = g = a_1 X + a_2 Y + a_3 Z
这就是坐标SKY在设备基础中是(a_1,a_2,a_3)
现在自从m假设位于世界 N-SKY 平面上的叉积m and g是与世界 N-SKY 平面正交并指向右侧的单位向量,因此这是 E。
E = m x g = b_1 X + b_2 Y + b_3 Z
这就是坐标E相对于设备基础是 (b_1, b_2, b_3) b 是由叉积产生的值m and g
最后得到叉积SKY and E是与 E-SKY 平面正交的向量,并且是N
N = SKY x E = c_1 X + c_2 Y + c_3 Z
放在一起我们有
E = b_1 X + b_2 Y + b_3 Z
N = c_1 X + c_2 Y + c_3 Z
SKY = a_1 X + a_2 Y + a_3 Z
因此,给定世界基础上的任何坐标,我们都可以找到设备基础上的坐标。例如 (1, 2, 3) 是一个向量v
v = E + 2 N + 3 SKY
写入设备坐标将通过替换并相乘
v = b_1 X + b_2 Y + b_3 Z+ 2 (c_1X + c_2 Y + c_3 Z) + 3 (a_1X + a_2 Y + a_3 Z)
v= (b_1 + 2 c_1 + 3 a_1)X+ (b_2 + 2 c_2 + 3 a_2)Y+ (b_3 + 2 c_3 + 3 a_3)Z
但我们真正感兴趣的是相反的情况,即给定设备基础中的任何坐标,找到世界基础中的坐标。对于那些不记得线性代数的人来说,在上面我们有 3 个未知数的 3 个方程,因此我们应该能够解出这个方程X, Y and Z在...方面E, N and SKY.
从数学上讲,将上面的a、b、c放在列中得到的矩阵是基矩阵从World基到Device基的变化,因此我们需要找到它的逆矩阵。但这个矩阵是一个正交矩阵,因此它的逆矩阵只是它的转置。
用代码写的
a[0] a[1] a[2]
a[3] a[4] a[5]
a[6] a[7] a[8]
给定设备基础中的任何坐标 (a_1, a_2, a_3),我们可以通过上面矩阵与 (a_1, a_2, a_3) 转置的乘积找到世界基础中的坐标 (b_1, b_2, b_3)。
尤其
a[0] a[1] a[2] 1 a[0]
a[3] a[4] a[5] x 0 = a[3]
a[6] a[7] a[8] 0 a[6]
因此坐标为X在世界基础上是 (a[0], a[3], a[6])
a[0] a[1] a[2] 0 a[1]
a[3] a[4] a[5] x 1 = a[4]
a[6] a[7] a[8] 0 a[7]
因此坐标为Y在世界基础上是 (a[1], a[4], a[7])
a[0] a[1] a[2] 0 a[2]
a[3] a[4] a[5] x 0 = a[5]
a[6] a[7] a[8] 1 a[8]
因此坐标为Z在世界基础上是 (a[2], a[5], a[8])
现在让我们看看是什么getOrientation
does.
它的代码是
azimuth = (float)Math.atan2(R[1], R[4]);
pitch = (float)Math.asin(-R[7]);
roll = (float)Math.atan2(-R[6], R[8]);
文件说
value[0]:方位角,绕-Z轴旋转,即相反
Z轴方向。
值[1]:俯仰角,绕-X轴的旋转,
即X轴的相反方向。
value[2]: 滚动、旋转
Y 轴。
从字面意思看上面的文档,不考虑设备的位置是错误的。该文件的真实性是azimuth
仅当设备平坦时。
在我们继续之前,请注意,所有计算都必须使用相对于同一基础的坐标来完成。例如,如果您想找到两个向量之间的角度,这两个向量的坐标必须相对于相同的基础。
现在,当您计算旋转时,您会隐含地理解旋转是与固定位置的偏差。也就是说,如果答案是 90 度,那么它与什么成 90 度?在这种情况下azimuth
它与磁北呈90度,也就是说你计算出与磁北的偏差N向量。如果你不能阐明这个隐含的固定位置,你就会遇到很多麻烦。例如,音高的固定矢量是什么?卷?设备方向(纵向-横向)?
让我们看一下每个计算,看看它到底计算了什么。
For the azimuth
官方计算是
azimuth = (float)Math.atan2(R[1], R[4]);
让我们回到原路Y写在设备坐标中。如前所述,坐标Y设备基础中的坐标为 (0, 1, 0)Y在世界基础上是 (a[1], a[4], a[7])
投影在世界基础上的坐标Y进入世界 XY 平面的是 (a[1], a[4])。该投影向量与N向量是(绘制投影向量的图片并N
如果你不明白)
Math.atan2(R[1], R[4]);
这正是计算azimuth
.
Thus getOrientation
计算设备 y 轴到世界 XY 平面的投影与世界北轴之间的角度。因此,如果垂直握住设备,则此计算没有意义,因为投影的 y 坐标始终为 0。从几何角度来看,如果您指向天空,则计算正北方向是没有意义的。此外,在这种情况下,绕 -Z 轴旋转意味着远离纵向旋转,因此文档不正确。
对于音高,官方计算是
pitch = (float)Math.asin(-R[7]);
投影在世界基础上的坐标Y进入世界 N-SKY 平面的向量为 (a[4], a[7]) 并且该投影向量与 的投影之间的角度Y向量进入世界 E-N 平面,该平面与 E-N 平面投影之间的角度相同Z向量进入Y-Z平面,重力向量为(再次自己画图)
Math.asin(-R[7])
因此,节距是设备 y 轴在世界 N-SKY 平面中的投影与Y进入E-N平面世界
同样地Roll
是设备 x 轴在世界 X-SKY 平面中的投影与X进入世界E-N平面。
例如我之前指出的。
- 在纵向模式下为平板电脑编写指南针。为什么打电话后
getRotation
需要加上90度才能得到正确答案。
在这种情况下,我们想要计算 x 轴到世界 XY 平面的投影之间的角度,因此需要添加 90 度,因为 x 轴和 y 轴之间的角度始终为 90 度。人们可以取而代之的是在世界基础上获得坐标X
向量为 (a[0], a[3], a[6]) ,然后投影到世界 XY 平面以获得 (a[0], a[3]) 并进行计算 Math.atan2( R[0]、R[3])。
- 为什么
RemapCoordinateSystem
必须在调用之前调用getOrientation
获取后置摄像头的方向(设备 z 轴的负方向)。
在这种情况下,您想要计算 z 轴的方向,而不是 y 轴的方向。getOrientation
计算。因此你必须打电话remapCoordinateSystem
它将 Z 轴映射到 Y 轴。从几何角度来说,您所做的是将 z 轴旋转到 y 轴,现在 y 轴变为 -z 轴。因此,您需要对旋转矩阵中的 y 列取反以返回到原始定义的坐标系。您只需获取世界基础上的坐标即可-Z即(-a[2], -a[5], -a[8]),然后投影到世界XY平面得到(-a[2], -a[5])并进行计算。
Note:我第一次使用 Sensor 的课程是在 2011 年,在了解了getRotationMatrix
and getOrientation
为此,我创建了一个库来仅使用旋转矩阵进行计算。我没那么想remapCoordinateSystem
and getOrientation
是需要的,但我没有意识到它们不好用,直到我写下这个答案,因为它会给出正确的azymuth
但随后给出了错误的值pitch
. What remapCoordinateSystem
所做的就是计算azymuth
in getOrientation
根据你想要计算的方向进行修正。但随后pitch
值不正确,您必须向其添加 -90 度才能获得后置摄像头方向的正确值以及其他情况下的正确值。
如果我要写这个类,我会创建
float getPitch(float[] rotMatrix)
float getRoll(float[] rotMatrix)
float getOrientation(float[] rotMatrix, int axis_you_want_to_calculate)
这样就不会有混乱,所有的答案都是正确的。
- 为什么打电话后
RemapCoordinateSystem
进而getOrientation
为了正确获取设备 z 轴的方向,当设备平坦时,结果不再有意义。
您正在尝试计算 -z 轴的正北方向,但现在它指向天空,因此计算不再有意义。
- 如何计算绕任何设备轴的旋转,与设备位置无关,即平坦或不平坦。
明天我将在以下位置发布关于绕 z 轴旋转情况的答案获取 Android 设备的欧拉偏航角 https://stackoverflow.com/questions/35303111/get-euler-yaw-angle-of-an-android-device.
注意:如果您只想编写一个指南针应用程序并且设备始终是平坦的,那么 TYPE_ORIENTATION 仍然不错。
注2:我的绘画能力很差,因此无法发布任何图片来说明。我什至无法使用 Gimp 画一条直线(我按照说明按住 Shift 键,但我的线是参差不齐的)。如果有人擅长绘画并且愿意提供帮助,请发表评论,我会指导我想到的图片插图以及将它们放在哪里。