为了实现一个全景图片展示的功能,需要借助手机的姿态传感器,实现一个这样的功能:当手机旋转时,视角也跟着旋转(读者若理解不能,可以参考下现在流行的 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 实际使用效果较差,精度较低,但是省电。

OpenGL: Rotation vector sensor of Android and Device motion of iOS的更多相关文章

  1. 打开android虚拟机时出现a repairable android virtual device

    打开android虚拟机时出现a repairable android virtual device,虚拟机可以打开但是一直处于开机状态,具体解决方案如下: 解决方案1:换个版本,不要选 CPU/AB ...

  2. Create new Android Virtual Device时不能创建

    在Create new Android Virtual Device时不能创建... 因为之前有重装过系统,ADT和java都没有更换,不知道是不是有哪里的环境(C盘中的配置)出错了... LOG在下 ...

  3. eclipse安装ADT插件重启后不显示Android SDK Manager和Android Virtual Device Manager图标的一种解决办法

    通常安装,搭建安卓环境后,不显示Android SDK Manager和Android Virtual Device Manager ize解决方法:Eclipse ->window->c ...

  4. eclipse中调出android sdk manager和android virtual device manager图标

    有时候在安装ADT插件后,eclipse菜单栏上不会显示android sdk manager和android virtual device manager两个图标, 这个时候,如果安装ADT插件的步 ...

  5. ADT后windows菜单未找到Android SDK Manager和Android Virtual Device Manager该解决方案的选择

    打开今天凌晨ADT准备编译androidproject的时候,突然发现windows菜单下的Android SDK Manager和Android Virtual Device Manager选项不见 ...

  6. AVD启动报错:Running an x86 based Android Virtual Device (AVD) is 10x faster

    1.cmd窗口中输入emulator -avd test 启动AVD时报错: Running an x86 based Android Virtual Device (AVD) is 10x fast ...

  7. No compatible targets were found Do you wish to a add new Android Virtual Device ?

    运行一个Android小程序时提示: No compatible targets were found Do you wish to a add new Android Virtual Device ...

  8. Xamarin.Android 4.10.01068 & Xamarin.iOS 1.8.361

    Xamarin.Android 4.10.01068 & Xamarin.iOS 1.8.361 NEW support for Visual Studio 2013 & Portab ...

  9. Android 4.3实现类似iOS在音乐播放过程中如果有来电则音乐声音渐小铃声渐大的效果

    目前Android的实现是:有来电时,音乐声音直接停止,铃声直接直接使用设置的铃声音量进行铃声播放. Android 4.3实现类似iOS在音乐播放过程中如果有来电则音乐声音渐小铃声渐大的效果. 如果 ...

随机推荐

  1. 程序员的基本功之Java集合的实现细节

    1.Set集合与Map 仔细对比观察上面Set下和Map下的接口名,不难发现它们如此的相似,必有原因 如果只考察Map的Key会发现,它们不可以重复,没有顺序,也就是说把Map的所有的Key集中起来就 ...

  2. jdk源码剖析:Synchronized

    开启正文之前,先说一下源码剖析这一系列,就以"死磕到底"的精神贯彻始终,最少追踪到JVM指令(再往下C语言实现了). =========正文分割线===========  Sync ...

  3. C#非泛型集合和泛型集合的超级详解

    C# 泛型集合之非泛型集合类与泛型集合类的对应: ArrayList对应List HashTable对应Dictionary Queue对应Queue Stack对应Stack SortedList对 ...

  4. JAVA加密算法系列-AES

    package ***; import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; ...

  5. PCIE_DMA实例三:Xilinx 7系列(KC705/VC709)FPGA的EDK仿真

    一:前言 好久没写博客了,前段时间有个朋友加微信请教关于PCIe的一些学习方法.本人也不是专家,只是略知一些皮毛.对于大家反馈的问题未必能一一解答,但一定知无不言.以后我会常来博客园看看,大家可以把问 ...

  6. 在IntelliJ IDEA中添加repository模板

    可以用于快速新建一个repository类,减少开发时间 在IntelliJ IDEA settings设置中(ctrl+alt+s)--Editor--File and Code Templates ...

  7. JS为网页添加文字水印【原创】

    最近需要实现为网页添加水印的功能,由于水印的信息是动态生成的,而百度谷歌上的方法往往都是为网页添加图片水印或为图片添加水印,而为网页添加文字水印相关资料较少,于是就自己动手写了这个代码. 通常加动态水 ...

  8. angular directive

    1.restrict (字符串)可选参数,指明指令在DOM里面以什么形式被声明: 取值有:E(元素),A(属性),C(类),M(注释),其中默认值为A: E(元素):<directiveName ...

  9. h5 实现调用系统拍照或者选择照片并预览

    这次又来分享个好东西! 调用手机相机拍照或者是调用手机相册选择照片,这个功能在 手机端页面 或者 webApp 应该是常用到的,就拿个人或会员资料录入那块来说就已经是经常会碰到的, 每当看到这块功能的 ...

  10. stm32中断学习总结

    经过了两天,终于差不多能看懂32的中断了,由于是用的库函数操作的,所以有些内部知识并没有求甚解,只是理解知道是这样的.但对于要做简单开发的我来说这些已经够了. 我学习喜欢从一个例程来看,下面的程序是我 ...