一、罗德里格旋转公式

可以参考百度百科和维基百科进行了解。
概括来说就是罗德里格旋转公式就是用来求旋转后新向量的公式:
而这个公式可以转换成矩阵形式:
公式各部分的几何意义和推导原理参考下图

这个图证明:

1.Vrot = 可以由V+(kxv)sinθ+kx(kxv)(1-cosθ)这三个向量的和来表示
2.(kxv)sinθ和kx(kxv)(1-cosθ)可以用平移来求解,这两个向量这样表示是对的
3.kx(kxv)(1-cosθ)可以经由图中的公式三转换成点乘形式
进而证明
并可以参照图理解公式的几何意义

二、罗德里格旋转矩阵

我们前面推导和证明了罗德里格旋转公式,并知道罗德里格旋转公式也可以写成矩阵形式,这个矩阵就叫罗德里格旋转矩阵。矩阵 公式的推导可以参考:

http://www.cnblogs.com/xpvincent/archive/2013/02/15/2912836.html
两个向量叉乘即可得到旋转轴,然后求出旋转角度,由这两个因素便可以求出旋转矩阵。
求旋转矩阵的时候,先求得旋转轴模长,然后将旋转轴向量除以模长,便得到旋转轴的单位向量。
利用旋转轴的单位向量和旋转角度,通过公式便可以得到旋转矩阵:

下面我们将看下这个旋转公式在VR中的应用

三、罗德里格旋转矩阵公式在VR中的应用

在双眼绘制的时候,会求得左右眼的偏移矩阵mLeftEyeTranslate,与头部姿态矩阵mHeadTransform.getHeadView()相乘,就能得到左右眼的转换矩阵mLeftEye.getTransform().getEyeView()
Matrix.multiplyMM(mRightEye.getTransform().getEyeView(), 0,mRightEyeTranslate, 0, mHeadTransform.getHeadView(), 0);
而这个头部姿态矩阵mHeadTransform.getHeadView() 是需要预测的,这里就会利用到罗德里格公式。

下面是用来获取头部姿态矩阵的函数
// 获取头部姿态矩阵mHeadTracker.getLastHeadView(mHeadTransform.getHeadView(), 0);

其实现为:

      public void getLastHeadView(float[] headView, int offset){if (offset + 16 > headView.length) {throw new IllegalArgumentException("Not enough space to write the result");}//如果定义了卡尔曼滤波对象synchronized (mTracker) {//首先计算出当前时间距离上次预测时间的差值double secondsSinceLastGyroEvent = (System.nanoTime() - mLastGyroEventTimeNanos) * 1.E-09D;Log.d(TAG,"secondsSinceLastGyroEvent="+secondsSinceLastGyroEvent);//实际上有60帧每秒,30帧算的话,一帧33ms,所以这里是提前预测一帧//然后加上提前预测的时间33msdouble secondsToPredictForward = secondsSinceLastGyroEvent + 0.03333333333333333D;//这里返回的是一个double类型的数组,double[] mat = mTracker.getPredictedGLMatrix(secondsToPredictForward);for (int i = 0; i < headView.length; i++) {mTmpHeadView[i] = ((float)mat[i]);}}Matrix.multiplyMM(headView, offset, mTmpHeadView, 0, mEkfToHeadTracker, 0);}

可以看到这里计算了当前时间距离上次预测时间的差值,然后加了一个提前预测时间33ms,之后调用了getPredictedGLMatrix

      public double[] getPredictedGLMatrix(double secondsAfterLastGyroEvent){//deltaT,时间差double dT = secondsAfterLastGyroEvent;Log.d(TAG,"dT="+dT);Vector3d pmu = getPredictedGLMatrixTempV1;//应该理解为向量乘以一个常量,这个常量就是提前预测的时间,但为什么乘以一个负的呢?Log.d(TAG,"lastGyro=("+Float.toString(lastGyro[0])+","+Float.toString(lastGyro[1])+","+Float.toString(lastGyro[2])+")");pmu.set(lastGyro[0] * -dT, lastGyro[1] * -dT, lastGyro[2] * -dT);Matrix3x3d so3PredictedMotion = getPredictedGLMatrixTempM1;//Log.d(TAG,"so3PredictedMotion="+so3PredictedMotion.toString());//这里返回的so3PredictedMotion就是一个Rodrigues矩阵So3Util.sO3FromMu(pmu, so3PredictedMotion);Matrix3x3d so3PredictedState = getPredictedGLMatrixTempM2;//Log.d(TAG,"getPredictedGLMatrix,so3SensorFromWorld="+so3SensorFromWorld.toString());Matrix3x3d.mult(so3PredictedMotion,so3SensorFromWorld,so3PredictedState);//so3PredictedState还是一个3x3的矩阵,这里把它放到一个4x4矩阵中return glMatrixFromSo3(so3PredictedState);}
这里面先调用So3Util.sO3FromMu求得一个so3PredictedMotion,这就是一个Rodrigues矩阵。而向量pmu就是前面公式中说的V向量
看下sO3FromMu的实现
      //这个函数封装rodrigues算法,只不过这里根据角度的大小//来调节kA和kB的值。角度比较大的时候,和公式上比起来,KA多除了一个θ,kB多除了一个θ平方,不知道原因是什么根据。public static void sO3FromMu(Vector3d w, Matrix3x3d result){//向量点乘等于w向量模长的平方double thetaSq = Vector3d.dot(w, w);//如果这里是求theta,就要用点积再除以两者的模再求arccos//开平方,就是求得w向量的模长double theta = Math.sqrt(thetaSq);Log.d(TAG,"w=("+w.x+","+w.y+","+w.z+")"+",theta="+theta);double kA, kB;if (thetaSq < 1.0E-08D) {//Log.d(TAG,"thetaSq < 1.0E-08D");kA = 1.0D - 0.16666667163372D * thetaSq;kB = 0.5D;}else{if (thetaSq < 1.0E-06D) {//Log.d(TAG,"thetaSq < 1.0E-06D");kB = 0.5D - 0.0416666679084301D * thetaSq;kA = 1.0D - thetaSq * 0.16666667163372D * (1.0D - 0.16666667163372D * thetaSq);} else {//Log.d(TAG,"thetaSq > 1.0E-06D");double invTheta = 1.0D / theta;kA = Math.sin(theta) * invTheta;kB = (1.0D - Math.cos(theta)) * (invTheta * invTheta);}}//根据公式,w是一个单位向量,因为每一项都要除以一个模长,所以这里除以模长就放到了kA和kBrodriguesSo3Exp(w, kA, kB, result);}

函数sO3FromMu其实就是对罗德里格矩阵求法的一个封装,其中rodriguesSo3Exp函数就是罗德里格矩阵:

      private static void rodriguesSo3Exp(Vector3d w, double kA, double kB, Matrix3x3d result){//求得向量的平方double wx2 = w.x * w.x;double wy2 = w.y * w.y;double wz2 = w.z * w.z;//result.set(0, 0, 1.0D - kB * (wy2 + wz2));result.set(1, 1, 1.0D - kB * (wx2 + wz2));result.set(2, 2, 1.0D - kB * (wx2 + wy2));double a, b;a = kA * w.z;b = kB * (w.x * w.y);result.set(0, 1, b - a);result.set(1, 0, b + a);a = kA * w.y;b = kB * (w.x * w.z);result.set(0, 2, b + a);result.set(2, 0, b - a);a = kA * w.x;b = kB * (w.y * w.z);result.set(1, 2, b - a);result.set(2, 1, b + a);}
不过这部分程序好像是有问题的,说是theta,但实际求得的是模长

陀螺仪传感器叫做Gyro-sensor,返回x、y、z三轴的角加速度数据,因此这里计算出时间差
secondsAfterLastGyroEvent,求负后与加速度相乘得到向量pmu,通过向量pmu利用罗德里格旋转公式得到result,返回给
so3PredictedMotion,可见这个
so3PredictedMotion就是罗德里格旋转矩阵,这个矩阵后面与so3SensorFromWorld相乘得到预测后的状态矩阵so3PredictedState,
      public double[] getPredictedGLMatrix(double secondsAfterLastGyroEvent){//deltaT,时间差double dT = secondsAfterLastGyroEvent;Log.d(TAG,"dT="+dT);Vector3d pmu = getPredictedGLMatrixTempV1;//应该理解为向量乘以一个常量,这个常量就是提前预测的时间,但为什么乘以一个负的呢?Log.d(TAG,"lastGyro=("+Float.toString(lastGyro[0])+","+Float.toString(lastGyro[1])+","+Float.toString(lastGyro[2])+")");pmu.set(lastGyro[0] * -dT, lastGyro[1] * -dT, lastGyro[2] * -dT);Matrix3x3d so3PredictedMotion = getPredictedGLMatrixTempM1;//Log.d(TAG,"so3PredictedMotion="+so3PredictedMotion.toString());//这里返回的so3PredictedMotion就是一个Rodrigues矩阵So3Util.sO3FromMu(pmu, so3PredictedMotion);Matrix3x3d so3PredictedState = getPredictedGLMatrixTempM2;//Log.d(TAG,"getPredictedGLMatrix,so3SensorFromWorld="+so3SensorFromWorld.toString());Matrix3x3d.mult(so3PredictedMotion,so3SensorFromWorld,so3PredictedState);return glMatrixFromSo3(so3PredictedState);}

四、罗德里格矩阵与卡尔曼滤波的配合

分析前面对罗德里格旋转矩阵的使用,可以知道在getPredictedGLMatrix函数中用到了
so3SensorFromWorld与罗德里格旋转矩阵相乘得到预测后的状态矩阵so3PredictedState
这个so3SensorFromWorld在processGyro和processAcc以及processMag的时候都会被进行修改
具体配合方式参见另一片博文《Google VR开发-罗德里格旋转矩阵与卡尔曼滤波的配合使用》

Google VR开发-Cardboard VR SDK头部追踪实现(罗德里格旋转公式)相关推荐

  1. unity+Cardboard SDK VR开发Cardboard Unity SDK讲解

    Cardboard Unity SDK Reference中文翻译版,水平有限请以英文版为准. Plugin Reference Package 内容 Unity插件包包含以下内容: 脚本 ·     ...

  2. Google VR开发-Cardboard VR SDK反畸变实现

    上一篇文章分析了Cardboard SDK的生命周期设计. 这里我们看下畸变部分的实现. Cardboard中将畸变这部分封装成了一个Distortion类和DistortionRenderer类. ...

  3. VR开发基础—VR视频

    1.导入谷歌官方提供的库: commonwidget.common.panowidget(全景图).videowidget(视频) 或者添加依赖: dependencies { compile pro ...

  4. 【获奖公布】走进VR开发世界——我们离开发一款VR大作还有多远?

    此次征文比赛以分享VR开发经验为核心,在对所有参赛文章进行审核后,以"开发"为先,评选出一.二.三等奖,共9名. 获奖名单 奖项 文章 作者 评语 一等奖 <VR游戏交互开发 ...

  5. GOOGLE VR SDK开发VR游戏,VR播放器之一

    最近一年来,VR虚拟现实和AR增强现实技术的宣传甚嚣尘上.其实VR,AR技术很早就有了,一直没有流行开来,不可否认价格是影响技术推广的最大壁垒.谷歌对VR最大的贡献是提供了廉价的谷歌眼镜,按照GOOG ...

  6. GOOGLE VR SDK开发VR游戏,VR播放器之中的一个

    近期一年来,VR虚拟现实和AR增强现实技术的宣传甚嚣尘上.事实上VR,AR技术非常早就有了,一直没有流行开来.不可否认价格是影响技术推广的最大壁垒. 谷歌对VR最大的贡献是提供了便宜的谷歌眼镜,依照G ...

  7. 二、VR全景图显示器开发 ---- Android VR视频/Google VR for Android /VR Pano/VR Video

    原文地址: http://blog.csdn.net/qq_24889075/article/details/52128463 http://www.jianshu.com/p/104251a3153 ...

  8. 谷歌 Daydream VR 开发工具

    现在,谷歌的虚拟现实平台 Daydream 已经准备好迎接开发者的到来,这得益于新发布的 Googe VR SDK(开发者工具包),它在周四结束测试正式上线.谷歌表示,这套工具包将让开发者能够为支持 ...

  9. 走进VR开发世界(4)——走进VR游戏开发的世界

    注: 原文2016年2月发表于公司内部社区, 最近才由同事转载出来, 删去了文中引用的一些内部文章和视频. 在这里我也只是把外网版本转过来, 留做备份. 背景介绍 我们组在2014年下半年尝试开发了一 ...

最新文章

  1. print、printf、println在Java中的使用
  2. 蓝牙的自适应跳频技术
  3. SQL Server R2 地图报表制作(四)
  4. pytorch实现L2和L1正则化regularization的方法
  5. .NET内存性能分析指南
  6. 接口测试之HTTP协议详解
  7. 发一个多CPU中进程与CPU绑定的例子
  8. 大数据_Flink_数据处理_案例WorldCount_批处理版---Flink工作笔记0008
  9. 计算机应用基础课改期望,《计算机应用基础》课改总结.doc
  10. springboot配置手动提交_kafka教程-springboot消费者-手动提交offset
  11. 飞机器减震器的matlab建模和仿真模拟
  12. 人在职场,身不由己?
  13. 计算机怎么格式化电脑吗,电脑怎么格式化
  14. Camera 图像处理原理分析- 色彩篇 二
  15. 关于鸿蒙开发系统组件的摘抄及运用1
  16. html阅读封面代码,HTML5/SVG 书本封面设计
  17. 295-光纤数据收发 隔离卡 加速计算卡 基于 Kintex-7 XC7K325T的半高PCIe x4双路万兆光纤收发卡
  18. 如何下载IEEE论文
  19. 全球互联网排名_全球互联网的梦想已死
  20. Your anti-virus program might be impacting 防火墙

热门文章

  1. 瑞萨78K0单片的调试与使用(Minicube2)
  2. 这款文件传输软件每隔10秒就会发起文件同步——镭速传输
  3. 2023年节假日JSON
  4. 中科大脑知识图谱平台建设及业务实践
  5. android:手机与BLE-CC41-A蓝牙模块通信
  6. 构建DCT过完备字典
  7. 浅谈iOS中关于app的优化
  8. PHOTOSHOP给MM去斑的最简单方法
  9. Java数字转中文大写,数字转英文
  10. 小目标检测的相关挑战与问题