以下内容转载自多多洛爱学习的文章《WebAR技术探索-导航中的应用》
作者:多多洛爱学习
链接:https://juejin.im/post/5c24252b6fb9a049d975411a
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文探索在Web前端实现AR导航效果的前沿技术和难点。

1. AR简介

增强现实(Augmented Reality,简称AR):是一种实时地计算摄影机影像的位置及角度并加上相应图像、视频、3D模型的技术,这种技术的目标是在屏幕上把虚拟世界套在现实世界并进行互动。

一般在web中实现AR效果的主要步骤如下:

  1. 获取视频源
  2. 识别marker
  3. 叠加虚拟物体
  4. 显示最终画面

AR导航比较特殊的地方是,它并非通过识别marker来确定虚拟物体的叠加位置,而是通过定位将虚拟和现实联系在一起,主要步骤如下:

  1. 获取视频源
  2. 坐标系转换:
    1. 获取设备和路径的绝对定位
    2. 计算路径中各标记点与设备间的相对定位
    3. 在设备坐标系中绘制标记点
  3. 3D图像与视频叠加
  4. 更新定位和设备方向,控制Three.js中的相机移动

2. 技术难点

如上文所述AR导航的主要步骤,其中难点在于:

  1. 兼容性问题
  2. WebGL三维作图
  3. 定位的精确度和轨迹优化
  4. 虚拟和现实单位尺度的映射

2.1 兼容性问题:

不同设备不同操作系统以及不同浏览器带来的兼容性问题主要体现在对获取视频流和获取设备陀螺仪信息的支持上。

2.1.1 获取视频流

  1. Navigator API兼容处理

    navigator.getUserMedia()已不推荐使用,目前新标准采用navigator.mediaDevices.getUserMedia()。可是不同浏览器对新方法的支持程度不同,需要进行判断和处理。同时,如果采用旧方法,在不同浏览器中方法名称也不尽相同,比如webkitGetUserMedia

    //不支持mediaDevices属性
    if (navigator.mediaDevices === undefined) {navigator.mediaDevices = {};
    }//不支持mediaDevices.getUserMedia
    if (navigator.mediaDevices.getUserMedia === undefined) {navigator.mediaDevices.getUserMedia = function(constraints) {var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;if(!getUserMedia) {return Promise.reject(new Error('getUserMedia is not implemented in this browser'));}return new Promise(function(resolve, reject) {getUserMedia.call(navigator, constraints, resolve, reject);});}
    }
  2. 参数兼容处理

    getUserMedia接收一个MediaStreamConstraints类型的参数,该参数包含两个成员videoaudio

    var constraints = {audio: true,video: {width: { min: 1024,ideal: 1280,max: 1920},height: 720,frameRate: {ideal: 10,max: 15},facingMode: "user" // user/environment,设置前后摄像头}
    }

    在使用WebAR导航时,需要调取后置摄像头,然而facingMode参数目前只有Firefox和Chrome部分支持,对于其他浏览器(微信、手Q、QQ浏览器)需要另一个参数optional.sourceId,传入设备媒体源的id。经测试,该方法在不同设备不同版本号的微信和手Q上表现有差异。

    if(MediaStreamTrack.getSources) {MediaStreamTrack.getSources(function (sourceInfos) {for (var i = 0; i != sourceInfos.length; ++i) {var sourceInfo = sourceInfos[i];//这里会遍历audio,video,所以要加以区分  if (sourceInfo.kind === 'video') {  exArray.push(sourceInfo.id);  }  }constraints = { video: {  optional: [{  sourceId: exArray[1] //0为前置摄像头,1为后置}]  }};});
    } else {constraints = { video: {facingMode: {exact: 'environment'}}});
    }
     
  3. 操作系统的兼容性问题

    由于苹果的安全机制问题,iOS设备任何浏览器都不支持getUserMedia()。所以无法在iOS系统上实现WebAR导航。

  4. 协议

    出于安全考虑,Chrome47之后只支持HTTPS页面获取视频源。

2.1.2 获取设备转动角度

设备的转动角度代表了用户的视角,也是连接虚拟和现实的重要参数。HTML5提供DeviceOrientation API可以实时获取设备的旋转角度参数。通过监听deviceorientation事件,返回DeviceOrientationEvent对象。

{absolute: [boolean] 是否为绝对转动值alpha: [0-360]beta: [-180-180]gamma: [-90-90]
}

其中alpha、beta、gamma是我们想要获取的角度,它们各自的意义可以参照下图和参考文章:

陀螺仪的基本知识

然而iOS系统的webkit内核浏览器中,该对象还包括webkitCompassHeading成员,其值为设备与正北方向的偏离角度。同时iOS系统的浏览器中,alpha并非绝对角度,而是以开始监听事件时的角度为零点。

Android系统中,我们可以使用-alpha得到设备与正北方的角度,但是就目前的测试情况看来,该值并不稳定。所以在测试Demo中加入了手动校正alpha值的过程,在导航开始前将设备朝向正北方来获取绝对0度,虽然不严谨但效果还不错。

2.2 WebGL三维作图

WebGL是在浏览器中实现三维效果的一套规范,AR导航需要绘制出不同距离不同角度的标记点,就需要三维效果以适应真实场景视频流。然而WebGL原生的接口非常复杂,Three.js是一个基于WebGL的库,它对一些原生的方法进行了简化封装,使我们能够更方便地进行编程。

Three.js中有三个主要概念:

  1. 场景(scene):物体的容器,我们要绘制标记点就是在场景中添加指定坐标和大小的球体
  2. 相机(camera):模拟人的眼睛,决定了呈现哪个角度哪个部分的场景,在AR导航中,我们主要通过相机的移动和转动来模拟设备的移动和转动
  3. 渲染器(renderer):设置画布,将相机拍摄的场景呈现在web页面上

在AR导航的代码中,我对Three.js的创建过程进行了封装,只需传入DOM元素(一般为<div>,作为容器)和参数,自动创建三大组件,并提供了Three.addObjectThree.renderThree等接口方法用于在场景中添加/删除物体或更新渲染等。

function Three(cSelector, options) {var container = document.querySelector(cSelector);// 创建场景var scene = new THREE.Scene();// 创建相机var camera = new THREE.PerspectiveCamera(options.camera.fov, options.camera.aspect, options.camera.near, options.camera.far);// 创建渲染器var renderer = new THREE.WebGLRenderer({alpha: true});// 设置相机转动控制器var oriControls = new THREE.DeviceOrientationControls(camera);// 设置场景大小,并添加到页面中renderer.setSize(container.clientWidth, container.clientHeight);renderer.setClearColor(0xFFFFFF, 0.0);container.appendChild(renderer.domElement);// 暴露在外的成员this.main = {scene: scene,camera: camera,renderer: renderer,oriControls: oriControls,}this.objects = [];this.options = options;
}
Three.prototype.addObject = function(type, options) {...} // 向场景中添加物体,type支持sphere/cube/cone
Three.prototype.popObject = function() {...} // 删除场景中的物体
Three.prototype.setCameraPos = function(position) {...} // 设置相机位置
Three.prototype.renderThree = function(render) {...} // 渲染更新,render为回调函数
Three.prototype.setAlphaOffset = function(offset) {..} // 设置校正alpha的偏离角度

在控制相机的转动上,我使用了DeviceOrientationControls,它是Three.js官方提供的相机跟随设备转动的控制器,实现对deviceorientation的侦听和对DeviceOrientationEvent的欧拉角处理,并控制相机的转动角度。只需在渲染更新时调用一下update方法:

three.renderThree(function(objects, main) {animate();function animate() {window.requestAnimationFrame(animate);main.oriControls.update();main.renderer.render(main.scene, main.camera);}
});

2.3 定位的精确度和轨迹优化

我们的调研中目前有三种获取定位的方案:原生navigator.geolocation接口,腾讯前端定位组件,微信JS-SDK地理位置接口:

  1. 原生接口

    navigator.geolocation接口提供了getCurrentPositionwatchPosition两个方法用于获取当前定位和监听位置改变。经过测试,Android系统中watchPosition更新频率低,而iOS中更新频率高,但抖动严重。

  2. 前端定位组件

    使用前端定位组件需要引入JS模块(https://3gimg.qq.com/lightmap/components/geolocation/geolocation.min.js),通过 qq.maps.Geolocation(key, referer)构造对象,也提供getLocationwatchPosition两个方法。经过测试,在X5内核的浏览器(包括微信、手Q)中,定位组件比原生接口定位更加准确,更新频率较高。

  3. 微信JS-SDK地理位置接口

    使用微信JS-SDK接口,我们可以调用室内定位达到更高的精度,但是需要绑定公众号,只能在微信中使用,仅提供getLocation方法,暂时不考虑。

    综上所述,我们主要考虑在X5内核浏览器中的实现,所以选用腾讯前端定位组件获取定位。但是在测试中仍然暴露出了定位不准确的问题:

    1. 定位不准导致虚拟物体与现实无法准确叠加
    2. 定位的抖动导致虚拟标记点跟随抖动,移动视觉效果不够平稳

针对该问题,我设计了优化轨迹的方法,进行定位去噪、确定初始中心点、根据路径吸附等操作,以实现移动时的变化效果更加平稳且准确。

2.3.1 定位去噪

我们通过getLocationwatchPosition方法获取到的定位数据包含如下信息:

{accuracy: 65,lat: 39.98333,lng: 116.30133...
}

其中accuracy表示定位精度,该值越低表示定位越精确。假设定位精度在固定的设备上服从正态分布(准确来说应该是正偏态分布),统计整条轨迹点定位精度的均值mean和标准差stdev,将轨迹中定位精度大于mean + (1~2) * stdev的点过滤掉。或者采用箱型图的方法去除噪声点。

2.3.2 初始点确定

初始点非常重要,若初始点偏离,则路线不准确、虚拟现实无法重叠、无法获取到正确的移动路线。测试中我发现定位开始时获得的定位点大多不太准确,所以需要一段时间来确定初始点。

定位开始,设置N秒用以获取初始定位。N秒钟获取到的定位去噪之后形成一个序列track_denoise = [ loc0, loc1, loc2...],对该序列中的每一个点计算其到其他点的距离之和,并加上自身的定位精度,得到一个中心衡量值,然后取衡量值最小的点为起始点。

2.3.3 基于路线的定位校正

基于设备始终跟随规划路线进行移动的假设,可以将定位点吸附到规划路线上以防止3D图像的抖动。

如下图所示,以定位点到线段的映射点作为校正点。路线线段的选择依据如下:

  1. 初始状态:以起始点与第二路线点之间的线段为当前线段,cur = 0; P_cur = P[cur];
  2. 在第N条线段上移动时,若映射长度(映射点与线段起点的距离)为负,校正点取当前线段的起点,线路回退至上一线段,cur = N - 1; P_cur = P[cur];;若映射长度大于线段长度,则校正点取当前线段的终点,线路前进至下一线段,cur = N + 1; P_cur = P[cur];
  3. 若当前线段与下一线段的有效范围有重叠区域(如下图绿色阴影区),则需判断定位点到两条线段的距离,以较短的为准,确定校正点和线路选择。

2.4 虚拟和现实的单位长度映射

WebGL中的单位长度与现实世界的单位长度并没有确定的映射关系,暂时还无法准确进行映射。通过测试,暂且选择1(米):15(WebGL单位长度)。

3. demo演示

演示视频:WebAR技术探索-导航中的应用

对地图感兴趣的开发者,欢迎登录腾讯位置服务体验~

原来地图导航结合WebAR技术还能这么玩相关推荐

  1. 高精地图中地面标识识别技术历程与实践

    导读:本文将主要介绍高德在高精地图地面标识识别上的技术演进,这些技术手段在不同时期服务了高精地图产线需求,为高德地图构建高精度地图提供了基础的技术保证. 1.面标识识别 地面标识识别,指在地图道路中识 ...

  2. 一种基于地图导航的语音识别管理系统的制作方法

    本发明涉及语音识别技术领域,具体为一种基于地图导航的语音识别管理系统. 背景技术: 随着GPS技术的不断发展,给人们的出行带来了很大的便利,人们可以根据GPS导航到达指定的地方. 现有的在对地图资源的 ...

  3. 全球首推语音定制产品,百度地图背后的语音技术到底有多强大?

    文|李永华 来源|智能相对论(aixdlun) 地图APP的竞争日趋激烈,但往往都是你来我往,各大产品互有来回. 但现在,一些基于AI技术的颠覆性创意功能正在冒出,它们将成为竞争"杀招&qu ...

  4. vue 高德地图 不同区域显示不同颜色_老司机频繁掉沟里,高德百度腾讯地图导航到底该怎么选?...

    导航类app发展至今,基本形成了三分天下的局面:高德.百度.腾讯,然而事实真的是三家平分天下么?三款不同的地图导航软件各有优缺点,至于什么路况选择哪个软件导航似乎更是一门玄学? 很多人想知道高德地图. ...

  5. marker 头像 高德地图_手机地图导航软件高德地图1.如何下载高德地图

    大家在使用手机地图的时候必须开启GPS定位服务,如不开启会定位失败 点击我查看如何开启手机GPS定位服务 如果会开启GPS定位服务的可直接点击观看以下文章 高德地图是国内一流的免费地图导航产品,也是基 ...

  6. 地图导航业下半场,高德与百度地图各缺一子?

    百度地图和高德地图的份额之争一直没有平息,二者的用户量高低在业内一直是个谜. 有说高德超过百度的:在2017年8月,国内第三方数据研究机构Talking Data移动观象台发布的月度APP覆盖率排行榜 ...

  7. 谷歌地图标注商户_谷歌AR地图导航标注3D显示精确定位?

    在2018年5月,谷歌在Google I/ O 2018主题讲演中展现了增强实践版谷歌地图标注,而其时只要概念雏形,但是谷歌并没有给出断定的发布日期.而在本周末,<华尔街日报>能够测验这个 ...

  8. 离线地图导航解决方案

     Bigemap GIS大数据 数据处理与开发应用一站式解决方案 技术服务:数据中心 + 桌面端 + 移动端(APP) + WEB端 关 键 词:地图  导航  定位  编辑  开发  分析  智慧 ...

  9. 室内导航应用程序,基于自研引擎开发3D地图+导航应用-采用蓝牙定位方案

    1.基于蓝牙技术的定位方案 目前室外的全球卫星导航系统(GNSS)应用已经大规模普及,通过应用程序可以为用户提供全天候的3维坐标和速度以及时间信息服务,同时中国自行研制的北斗卫星导航系统也成为了继GP ...

最新文章

  1. java正则表达式及api_Java魔法堂:深入正则表达式API
  2. 探测Windows2K/XP/2003本机系统信息
  3. 【数据结构与算法】之深入解析“奇偶链表”的求解思路与算法示例
  4. Centos7 安装gitlab 8.7.5
  5. LeetCode 1456. 定长子串中元音的最大数目(滑动窗口)
  6. centos7安装redmine3,并升级redmine1.8到3
  7. # android开发:4-1、Activity启动方式、生命周期、不同activity的数据传递
  8. golang | 变量-字符串练习
  9. 2021年,Web前端还好找工作吗?
  10. 划重点!Android 11 首个开发者预览版新功能抢先看
  11. Python函数式编程,map/reduce,filter和sorted
  12. [转载] python中字典copy_python深度复制字典,copy方法与deepcopy方法
  13. C语言随机读写数据文件(二)
  14. 关系数据库中常用的数据结构
  15. 利用duplicity与金山快盘 for UbuntuKylin 实现文件云备份
  16. Winodws 7 专业番茄花园版 v 1.0
  17. 使用Linux训练LoRA模型
  18. D.E. Shaw:“股涨债跌”的幕后操手竟是各国央行?
  19. 责任链模式实现方式以及在业务中的运用
  20. Case study: IIoT effectiveness on the plant floor

热门文章

  1. 知识付费时代对内容为王的反思
  2. 孙悟空与秦始皇是什么关系?
  3. Java正则表达式(一)、抓取网页email地址实例
  4. .NET整合及绿色软件工具VMThinApp使用
  5. Jetson Nano【13】关于torch2trt报错:AttributeError: ‘Tensor‘ object has no attribute ‘_trt‘的一种可能性
  6. xss-labs通关,xss漏洞详解
  7. 抽奖活动设计 php,如何设计高并发下的抽奖?
  8. java 朗读_java下载安装 用Java实现简单的语音朗读
  9. 2021年安全员-C证(山东省-2020版)考试试卷及安全员-C证(山东省-2020版)操作证考试
  10. 强化学习用于金融时序问题(Q,DQN,AC)