为了实现一个全景图片展示的功能,需要借助手机的姿态传感器,实现一个这样的功能:当手机旋转时,视角也跟着旋转(读者若理解不能,可以参考下现在流行的 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. 老李分享: JSON

    老李分享: JSON    poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:908821478, ...

  2. 请不要把‘通知的观察者注册’放在-viewWillAppear:中

    接手项目二次开发的吐槽: 接手别人的代码的悲哀之一就是,我反复的把流程走了一遍又一遍,却始终无法发现原来是这个问题. 之前这个人把通知的观察者注册放在了-viewWillAppear:中,导致,我发送 ...

  3. 用Gradle构建Spring Boot项目

    相比起Maven的XML配置方式,Gradle提供了一套简明的DSL用于构建Java项目,使我们就像编写程序一样编写项目构建脚本.本文将从无到有创建一个用Gradle构建的Spring Boot项目, ...

  4. 关于Form表单一些基础知识

    1.两个重要属性: action:表单需要提交的服务器地址 method:表单提交数据使用的方法,get/post >>>get和post的区别 ①get传参使用URL传递,所有参数 ...

  5. web开发与IC卡读卡器

    前段时间有个项目在客户端web下使用IC卡读卡器,试了很多种方案都觉得麻烦,最后在网上找了个现成的方案,采用了YW-605HA读卡器,厂家就不说了,免得说做广告.开发起来也挺简单. 他们将IC卡读卡器 ...

  6. 《Python自然语言处理》第二章-习题解答-练习6

    问题描述:在比较词表的讨论中,创建一个对象叫做translate,通过它你可以使用德语和意大利语词汇查找对应的英语词汇.这种方法可能会出现什么问题,你能提出一个办法来避免这个问题吗? 虽然这是一道初级 ...

  7. 解决failed to push some refs to

    由于github我使用了dev和feature分支,团队合作合并到dev,个人开发都是feature....今天在本地feature中git pull origin dev 出现 在使用git 对源代 ...

  8. lua 数据类型

    lua 数据类型 8 种数据类型 类型 说明 nil 空类型 boolean 布尔类型 number 数值型, 浮点型 string 字符串 function 函数 userdata 用户自定义结构 ...

  9. 【从无到有】HTML的初识——part1

    Ⅰ.HTML的初识 1.HTML:超文本标签语言(网页源代码) 2.html的基本结构: <html> <head> <meta charset="utf-8& ...

  10. [Oracle]LogMiner工具小结

    (一)LogMiner工具的作用Logminer工具主要用来分析redo log和archive log文件.通过该工具,可以轻松获得Oracle redo log和archive log文件的具体内 ...