OpenGL: Rotation vector sensor of Android and Device motion of iOS

为了实现一个全景图片展示的功能,需要借助手机的姿态传感器,实现一个这样的功能:当手机旋转时,视角也跟着旋转(读者若理解不能,可以参考下现在流行的 VR 应用,使用陀螺仪模式时的效果,亦可称作“单目 VR 效果”)。这个功能的实现原理为:利用手机传感器得到手机的当前的姿态的信息(可以是用各种方式来描述的),然后调整投影的参数,实现最终的图像跟着手机旋转的效果。

手机姿态获取

Android

Android 平台有各种各样的传感器(Sensor),不止一个 sensor 可以实现前文提到的目的。在 Android SDK API Guides 中可以找到所有可用的 sensors。

1 gyroscope

这个就是陀螺仪了,是硬件传感器(hardware sensor),可以提供设备绕 xyz 三个轴旋转的角速度。注意这里的坐标系就是设备自身的,短边 x 轴,长边 y 轴,z 轴穿越手机屏幕。为实现旋转效果需要的数据是旋转的角度(用以计算旋转矩阵),所以如果要用 gyroscope 则需要对数据进行积分,最好加上滤波使效果平稳。

若欲得到角度变化量而非角速度,那麽,官方提供一个例子,可以通过乘上时间差并通过计算得到一个四元数,来代表角度的变化量。实际上使用发现的问题是,该传感器得到的数据完全就是以设备为参考坐标的,当设备竖直朝向和横向的时候,其表现是不变的,也就是说没有将真實世界作为参考,这个对我的需求来说是不满足的。

2 orientation

这是一个软件传感器(software sensor),返回的数据不是传感器硬件直接采集得到的,而是根据硬件的数据进行合适的计算得到的。

阅读文档即可得知,此用于获取设备在 Yaw,Pitch and Roll 三个方向的旋转的角度,根据这三个角度也就可以确定其姿态了。但是官方文档不推荐这样使用,而是推荐 rotation vector sensor 并配合 getRotationMatrix() 来计算出这三个参数,至于原因,官方文档只说现在留着这个传感器类型是历史原因,让大家不要用了,没解释其他的。具体原因后面解释。

3 rotation vector

这个 sensor 和上面的 orientation 一样,都是软件传感器。这个表示的仍然是设备的姿态,但是与上面的不同,这个用一个四元数来表示设备的旋转,旋转的参考坐标系是真实的物理世界。

在应用該传感器提供的数据时,值得注意的是,有可能传感器返回的数据不一定是四个数据,也有可能是三个参数,比较安全的做法是这样做:

        @Override
        public void onSensorChanged(SensorEvent event) {
            float[] quat = new float[4];
            SensorManager.getQuaternionFromVector(quat, event.values);
			// use the quaternion
            // ...
        }

可参考:TYPE_ROTATION_VECTOR

比较

那么这两个传感器似乎都能用,到底用哪个呢?显而易见,第一个用起来不太方便。那么后两者区别在哪?

实际上,官方就不推荐使用 orientation,已经将其标记为 deprecated,并且提供了另一个方法叫做 getOrientation(),用法较复杂一点,从加速度计和地磁传感器获取数据,根据此计算得旋转矩阵,然后在根据旋转矩阵计算三个方向的旋转角度。个人感觉就是放弃了帮助开发者去计算这个数据,而是告诉大家应该怎么样算,自己去算吧。为什么要废弃呢,根据这里的解释,旧版传感器的问题主要是万向节锁,当某一个轴转动 90 度,就很难准确描述另外两个轴的转动了,对万向节锁的理解可以看我之前的文章:万向节锁(Gimbal Lock)的理解。Android API 文档这样说:

The orientation sensor derives its data by processing the raw sensor data from the accelerometer and the geomagnetic field sensor. Because of the heavy processing that is involved, the accuracy and precision of the orientation sensor is diminished.

Specifically, this sensor is reliable only when the roll angle is 0. As a result, the orientation sensor was deprecated in Android 2.2 (API level 8), and the orientation sensor type was deprecated in Android 4.4W (API level 20).

相比较,rotation vector 用四元数来描述旋转,避免了这个问题,所以我采用这个传感器来采集设备姿态的信息。

iOS

iOS 平台稍微简单一些,没有那么多具体的传感器。deviceMotion 就很好用,获取 deviceMotion.attitude 这是一个CMAttitude对象,可以直接获取用四元数表示的设备的姿态信息。

其实 CoreMotion 这个 Framework 也是可以提供裸陀螺仪数据等等,但是 CMDeviceMotion 下面的 attitude 这个属性返回的数据是更加准确的,如果需要精确的数据,推荐使用这个方法。

实现旋转

这里我先简单阐述一下一个旋转的意义:当我们看着一张图片的时候,如果要实现旋转这张图的效果,有两个办法:

  1. 将图片转一下(调整物体的模型的参数)
  2. 我的脑袋转一下(调整摄像机的参数,也就是改变观察矩阵(即 look at matrix)的参数)

实际上这两种方法都可以实现,实际应用的时候随便选一个更合适自己的就可以了。我这里要说明的是如果采用后一个办法,不能简单地将两个矩阵相乘就算数了,我们要从实际情况去考虑这个问题,考虑 OpenGL 的观察矩阵的计算:

// GLU
gluLookAt(x0, y0, z0, xref, yref, zref, Vx, Vy, Vz)

// glm
GLM_FUNC_DECL tmat4x4<T, P> glm::lookAt (   tvec3< T, P > const &   eye,
tvec3< T, P > const &   center,
tvec3< T, P > const &   up 
)

两种是一样的,分别指定观察坐标系的原点(eye),参考点(即视点,相机瞄准的点,center),和向上向量(up)。当旋转相机時,需要将 center 和 up 同时应用这个相应的旋转。我之前犯过的一个错误是:我仅仅把旋转应用到了 center 上面,而没有应用到 up vector,up vector 一直设置的是(0, 1, 0)。直观点说就是:视点转了,但是头顶朝向没有变化,这个是不符合逻辑的,我们需要让视线N:P_0 - P_ref和 up vector 保持垂直。

问题解决

在实际实践中,Android 的坑还真是特别的多,我发现使用 rotation vector 计算出旋转矩阵,会根据手机当前和真实世界的关系产生一定偏移,也就是说手机朝向北方和朝向南方得到的数据是不一样的,而我希望的是得到一个与手机初始的朝向无关(也就是手机绕真实世界的南北极组成的轴旋转的角度),而与手机与地平线的夹角有关的这么一个数据。总的来说就是 rotation vector 做了一些我不想要的校正。同时我找到另一个 sensor:GAME_ROTATION_VECTOR,其介绍如下:

Identical to TYPE_ROTATION_VECTOR except that it doesn't use the geomagnetic field. Therefore the Y axis doesn't point north, but instead to some other reference, that reference is allowed to drift by the same order of magnitude as the gyroscope drift around the Z axis.

In the ideal case, a phone rotated and returning to the same real-world orientation will report the same game rotation vector (without using the earth's geomagnetic field). However, the orientation may drift somewhat over time. See TYPE_ROTATION_VECTOR for a detailed description of the values. This sensor will not have the estimated heading accuracy value.

使用这个我基本上实现我想要的效果,所以以后要用这个 Rotation vector 的时候,要考虑清楚到底要用哪一个。另外还有一个 Geomagnetic Rotation Vector 实际使用效果较差,精度较低,但是省电。

原文地址:https://www.cnblogs.com/psklf/p/6890885.html