博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Cardboard Talk01 HeadTracker
阅读量:4577 次
发布时间:2019-06-08

本文共 7159 字,大约阅读时间需要 23 分钟。

操作系统:Windows8.1

显卡:Nivida GTX965M

开发工具:Android studio 3.0.0 | Cardboard 1.0


使用 Google 的 开发VR应用,会用到其中的几个功能,其中一个便是头部跟踪,即HeadTracker。接下来几个章节将会逐一分析 Cardboard 头部跟踪的具体设计和实现。考虑新版本的SDK已经不再提供源代码的支持,故采用比较老的版本对源代码进行分析说明。

 

Overview Of HeadTracker


 有关头部跟踪的代码均在 com.google.vrtoolkit.cardboard.sensors 包下。SDK通过 HeadTracker 对外提供服务,该类内部通过 DeviceSensorLooper 完成对Android系统的 SensorManager 的绑定、监听工作。通过 OrientationEKF 完成旋转角度的转换工作,其中涉及到应用扩展卡尔曼滤波及罗德里格旋转公式,该部分会在后面章节用到的时候逐一介绍。首先通过一张图,看一下 HeadTracker DeviceSensorLooper 的关系结构。

从类结构来看 HeadTracker 实现了Android系统的 接口,该接口提供了必要的功能定义,用于监听系统对应的传感器的回调。换句话说 HeadTracker 将会作为监听器间接的或者直接的用以注册到系统的 中,获取源源不断的传感器数据。下面分别介绍几个主要的 PublicAPI 。

 

1. createFromContext(Context) 根据传递的Context创建 HeadTracker Instance:

public static HeadTracker createFromContext(final Context context) {        final SensorManager sensorManager = (SensorManager)context.getSystemService("sensor");        final Display display = ((WindowManager)context.getSystemService("window")).getDefaultDisplay();        return new HeadTracker(new DeviceSensorLooper(sensorManager), new SystemClock(), display);    }

该函数获取系统的 ,如之前所述用于注册监听器获取传感器数据。接下来通过 WindowManager 获取默认的 对象,用于判断当前设备屏幕的旋转朝向。接着创建 DeviceSensorLooper 作为一个中间层内部启用了一个线程维护 与 的交互操作及外部控制逻辑,并将之前获取的 sesorManager 作为构造参数使用。最后实例化的是 ,该实例用于获得系统精确的 nano time 参与计算旋转角度。

 

2. onSensorChanged(SensorEvent) 将获取的传感器数据缓存,并传递给 OrientationEKF 进行处理以获得更准确的旋转数据。值得注意的是头部跟踪相关的传感器类型为应加速度计 Acc 和陀螺仪 Gyro。

public void onSensorChanged(final SensorEvent event) {        if (event.sensor.getType() == 1) {            this.mLatestAcc.set(event.values[0], event.values[1], event.values[2]);            this.mTracker.processAcc(this.mLatestAcc, event.timestamp);        }        else if (event.sensor.getType() == 4) {            this.mLatestGyroEventClockTimeNs = this.mClock.nanoTime();            this.mLatestGyro.set(event.values[0], event.values[1], event.values[2]);            Vector3d.sub(this.mLatestGyro, this.mGyroBias, this.mLatestGyro);            this.mTracker.processGyro(this.mLatestGyro, event.timestamp);        }    }

 

3. startTracking() 开启头部跟踪功能。

public void startTracking() {        if (this.mTracking) {            return;        }        this.mTracker.reset();        this.mSensorEventProvider.registerListener((SensorEventListener)this);        this.mSensorEventProvider.start();        this.mTracking = true;    }

重置 OrientationEKF 对象,向 SensorEventProvider 接口的实现类 DeviceSensorLooper 对象注册 HeadTracker 实例本身。除此之外开启 DeviceSensorLooper 线程,最后设置上下文 mIsRunning true

 

4. stopTracking() 关闭头部跟踪功能。

@Override    public void stop() {        if (!this.mIsRunning) {            return;        }        this.mSensorManager.unregisterListener(this.mSensorEventListener);        this.mSensorEventListener = null;        this.mSensorLooper.quit();        this.mSensorLooper = null;        this.mIsRunning = false;    }

从传感器管理器移除所有的监听器,停止监听。并结束传感器线程,设置上下文 mIsRunning false

 

5. setGyroBias(float[]) 设置陀螺仪Gyro的偏好数据,对于头部跟踪的最小原型,该函数是非必要的。

public void setGyroBias(final float[] gyroBias) {        if (gyroBias == null) {            this.mGyroBias.setZero();            return;        }        if (gyroBias.length != 3) {            throw new IllegalArgumentException("Gyro bias should be an array of 3 values");        }        this.mGyroBias.set(gyroBias[0], gyroBias[1], gyroBias[2]);    }

 

6. setNeckModelEnabled(boolean) 开启颈部模型偏移数据。

public void setNeckModelEnabled(final boolean enabled) {        this.mNeckModelEnabled = enabled;    }

关于该函数没什么好说的,该功能默认是关闭的,如果开启需要留意类中定义了颈部模型的offset补偿的参数:

private static final float DEFAULT_NECK_HORIZONTAL_OFFSET = 0.08f;private static final float DEFAULT_NECK_VERTICAL_OFFSET = 0.075f;private static final boolean DEFAULT_NECK_MODEL_ENABLED = false;

Details of DeviceSensorLooper


 在介绍 getLastHeadView(float[], int) 函数之前,先看一下 DeviceSensorLooper 的设计与实现,以便更好的理解后续的逻辑。该类的初始化阶段定义了感兴趣的传感器类型:

static {        INPUT_SENSORS = new int[] { 1, 4 };    }

 

1. constructor(SensorManager) 现在看一下构造函数的定义实现:保存 HeadTracker 传递进来的系统 SensorManager 实例引用,用于注册监听器所使用。

public DeviceSensorLooper(final SensorManager sensorManager) {        super();        this.mRegisteredListeners = new ArrayList
(); this.mSensorManager = sensorManager; }

 

2. start() 开启独立工作线程,监听传感器传递的数据,并向注册的监听器回调获取到的数据,可以看到在内部类中引用了监听器集合 mRegisteredListeners,会遍历所有的监听器分发数据。

@Override    public void start() {        if (this.mIsRunning) {            return;        }        this.mSensorEventListener = (SensorEventListener)new SensorEventListener() {            public void onSensorChanged(final SensorEvent event) {                for (final SensorEventListener listener : DeviceSensorLooper.this.mRegisteredListeners) {                    synchronized (listener) {                        listener.onSensorChanged(event);                    }                }            }                        public void onAccuracyChanged(final Sensor sensor, final int accuracy) {                for (final SensorEventListener listener : DeviceSensorLooper.this.mRegisteredListeners) {                    synchronized (listener) {                        listener.onAccuracyChanged(sensor, accuracy);                    }                }            }        };        final HandlerThread sensorThread = new HandlerThread("sensor") {            protected void onLooperPrepared() {                final Handler handler = new Handler(Looper.myLooper());                for (final int sensorType : DeviceSensorLooper.INPUT_SENSORS) {                    final Sensor sensor = DeviceSensorLooper.this.mSensorManager.getDefaultSensor(sensorType);                    DeviceSensorLooper.this.mSensorManager.registerListener(DeviceSensorLooper.this.mSensorEventListener, sensor, 0, handler);                }            }        };        sensorThread.start();        this.mSensorLooper = sensorThread.getLooper();        this.mIsRunning = true;    }

函数的后半部分,会实例化线程的Handler,并在Handler初始化的时候获取感兴趣的传感器对象,并最终注册代理 SensorEventListener 对象。完成后启动线程,并修改标志位为已运行状态。

 

2. stop() 停传感器工作线程,移除代理中间监听器对象,退出线程并修改标志位为停止运行状态。

@Override    public void stop() {        if (!this.mIsRunning) {            return;        }        this.mSensorManager.unregisterListener(this.mSensorEventListener);        this.mSensorEventListener = null;        this.mSensorLooper.quit();        this.mSensorLooper = null;        this.mIsRunning = false;    }

 

3. registerListener(SensorEventListener) 向外部提供接口,用以注册外部Sensor监听器对象。

@Override    public void registerListener(final SensorEventListener listener) {        synchronized (this.mRegisteredListeners) {            this.mRegisteredListeners.add(listener);        }    }

 

4. unregisterListener(SensorEventListener) 向外部提供接口,用以注销外部Sensor监听器对象。

@Override    public void unregisterListener(final SensorEventListener listener) {        synchronized (this.mRegisteredListeners) {            this.mRegisteredListeners.remove(listener);        }    }

Brief summary


Cardboard SDK 为获取系统的传感器数据,定义了 DeviceSensorLooper 对象维护系统内部 SensorManager 与外部 SensorEventListener 监听器的联系,其内部开启了一个独立的worker工作线程专门负责传感器数据的获取与分发。而 HeadTracker 通过注册自身到 DeviceSensorLooper 中最终获得了有效的传感器原始数据,Acc Gyro

目前位置,拿到传感器原始数据后,如何计算精准的旋转矩阵并未提及。该部分将会在接下来的章节逐一讨论。

转载于:https://www.cnblogs.com/heitao/p/7750553.html

你可能感兴趣的文章
贴心的vs 备注提醒功能
查看>>
SQL语句操作文件
查看>>
自我介绍以及关于软件工程的问题
查看>>
struts (一)
查看>>
【新番推荐】工作细胞
查看>>
开发过程中遇到的崩溃信息及原因,崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃崩溃...
查看>>
C#简单的tcpserver
查看>>
提高(Android)开发效率的工具与网站
查看>>
web.xml里welcome-file欢迎页面配置及web.xml简介
查看>>
[转]Fedora8 Linux下安装mapguide
查看>>
你在用MapGuide/Map 3D做什么?
查看>>
bzoj1296 [SCOI2009]粉刷匠
查看>>
Freemarker网页静态化
查看>>
大话重构连载18:最常见的问题
查看>>
使用charles过滤网络请求
查看>>
C# WinForm实现Windows 7 Aero磨砂玻璃效果
查看>>
Java SpringMVC框架学习(一)入门
查看>>
JAVA 多线程和并发学习笔记(四)
查看>>
redis学习笔记(一)
查看>>
将Form置入splitContainer的panel中
查看>>