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

    这些数组的操作方法会改变原来的数组.在使用 Vue 或者 Angular 等框架的时候会非常实用,使用这些方法修改数组会触发视图的更新. Array.prototype.push 该方法可以在数组末尾 ...

  2. C语言求最小公倍数和最大公约数三种算法(经典)

    把以前写的一些经验总结汇个总,方便给未来的学弟学妹们做个参考! --------------------------永远爱你们的:Sakura 最小公倍数:数论中的一种概念,两个整数公有的倍数成为他们 ...

  3. Ranklib源码剖析--LambdaMart

    Ranklib是一套优秀的Learning to Rank领域的开源实现,其中有实现了MART,RankNet,RankBoost,LambdaMart,Random Forest等模型.其中由微软发 ...

  4. Spring-data-redis操作redis知识汇总

    什么是spring-data-redis spring-data-redis是spring-data模块的一部分,专门用来支持在spring管理项目对redis的操作,使用java操作redis最常用 ...

  5. C++中的类继承(4)继承种类之单继承&多继承&菱形继承

    单继承是一般的单一继承,一个子类只 有一个直接父类时称这个继承关系为单继承.这种关系比较简单是一对一的关系: 多继承是指 一个子类有两个或以上直接父类时称这个继承关系为多继承.这种继承方式使一个子类可 ...

  6. 百度UEditor图片上传或文件上传路径自定义

    最近在项目中使用到百度UEditor的图片以及文件上传功能,但在上传的时候路径总是按照预设规则来自动生成,不方便一些特殊文件的维护.于是开始查看文档和源代码,其实操作还是比较简单的,具体如下: 1.百 ...

  7. OpenCV 玩九宫格数独(二):knn 数字识别

    欢迎大家关注腾讯云技术社区-博客园官方主页,我们将持续在博客园为大家推荐技术精品文章哦~ 作者:刘潇龙 前言 首先需要说明,这里所说的数字识别不是手写数字识别! 但凡对机器学习有所了解的人,相信看到数 ...

  8. Struts2框架的基本使用

         前面已经介绍过了MVC思想,Struts2是一个优秀的MVC框架,大大降低了各个层之间的耦合度,具有很好的扩展性.从本篇开始我们学习Struts2的基本用法,本篇主要包括以下内容: Stru ...

  9. HNOI2017 滚粗记

    这次HNOI,感觉自己收获了很多啊,高一的蒟蒻,也就是去历练一番,长长见识吧.. $day0$ 上午做了一道斜率优化的题,下午好像在颓??晚上也不想复习了,看了会电视,$12$点才睡.. $day1$ ...

  10. java线程(一)

    java线程基础 什么是线程? 这里引用百度百科的一句话:"线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元.一个标准的线程由线程ID,当 ...