随着信息时代的发展变迁,荧幕里呈现的 智慧城市 慢慢出现了在现实生活中,很大程度上便利了日常的管理和维护。在智慧城市的大背景下,智慧交通监管可视化系统 是其重要的组成部分,通过一条条道路监控的串联,引申出一座智慧城市的管控,而在众多数据的维护中,实时数据、设备状态 以及视频监控 是极为重要的。其中视频监控一直是作为主体的部分,而在 互联网 和 物联网 齐头并进的形式下,“中国天网” 应运而生,这其实是一项城市监控系统,但它不是个仅一台摄像头的设备,而是足足有1.7亿个监控摄像头,而在未来三年内,还将再安装4亿个摄像头。交通作为城市发展的动脉,与人们下日常息息相关,而在这一系列的监管作用下,成为了一个“公安治安视频监控系统”,关乎人们日常的安全治安管理。
城市交通的主要方式体现在城市道路、公交、轨道交通等设施上,但随着城市化进程的加快和经济社会发展的推动下,机动车保有量迅速增加,城市交通问题日益严峻。面对这一现状,为了缓和城市交通的各种问题,采取了多种解决方案,例如建设一系列信号灯控制,路口卡口监控、视频监控等多种方法的系统维护,有着一定程度上的效果,但是各个系统都独立着解决其对应的问题,无法从整体的交通态势上进行综合掌控,而实现城市化智慧交通的管理系统可以很好地应对这一问题。介于 2D 组态和 3D 组态上,Hightopo(以下简称 HT )的 HT for Web 产品上的有着丰富的组态化可供选择,本文将介绍如何运用 HT 丰富的 2/3D 组态 搭建出一个路口监控系统的解决方案。
本文将从一下几个方面介绍智慧交通可视化监控系统的实现过程:
1、车辆生成以及在红绿灯控制下的运行;
2、摄像头的仿真和实景监控;
3、路口监控信息的实时数据以及维护;
在智慧交通监控系统里,呈现了拟真的红绿灯控制下实时车辆行驶的场景,搭配上可控制的天气环境切换面板,可以模拟在各种天气下的路口运行状态;还实现了路口摄像头的监控状态,点击路口的每个摄像头可以查看此摄像头监控范围内路口的实时运行,提供仿真和实景两种状态的切换;其次还添加了许多实时数据的监控面板,对接真实接口数据起到实时路口监控的最大效益化。
HT 通过丰富的 2D 组态和 3D 组态的交融结合使用上,整理出许多工业互联网上的解决方案,在智慧城市推动的背景下,智慧交通管理系统可视化决策系统也是极为重要的部分,对于道路以及路口的监测中,面向交管智慧中心大屏环境,其三维场景上可通过 HT for Web 产品轻量化 HTML5/WebGL 建模的方案,实现快速建模、运行时轻量化到甚至手机终端浏览器即可 3D 可视化运维的良好效果;二维图纸上拥有矢量图标在放大后图像不会失真,支持大屏、多屏、超大分辨率等显示情景。
集成地理信息系统、视频监控系统、交管部门各业务系统数据,对交通路况车流量、事故处理报告等要素进行综合监测,并支持点选查看具体警力、机动目标、交通事件、监控视频等详细信息,帮助管理者实时掌握交通整体运行态势。
支持对摄像头、流量检测设备、交通信号灯等交通基础资源的数量、空间位置分布、实时状态等信息进行监测和可视化管理,支持设备详细信息查询,支持对未正常工作的设备进行告警,加强管理者对设备状态的监测与感知,提升交通基础设施的运维管理效率。
支持集成前端视频巡检系统,有效结合视频智能分析、智能定位、智能研判技术,对道路拥堵点位、隐患点位、事故点位等情况进行可视化监测,实现异常事件的实时告警、快速显示,并可智能化调取异常点位周边监控视频,有效提升接处警效率。
支持集成路口信号灯、视频监控等系统数据,对路口交通流量、流速、车辆及道路异常事件、信号灯状态等信息进行实时监测,并可结合专业的模型算法,比对历史最佳通行速度及最佳通行量,对路口交通态势进行可视化分析研判,为信号配时调优和路口交通组织优化提供科学的决策依据,有效提升交通运行效率。
充分整合交管部门现有数据资源,提供多种可视化分析、交互手段,对海量历史违法违章案件数据进行可视化串并分析,深度挖掘案件时空分布规律,为交管部门进行原因分析、主动防范等业务应用提供支持。
在路口的仿真的运行系统下,场景中有这许许多多来往运行的车辆,是通过动态加载车辆模型并且设置管道运行动画,而这些车辆当然需要符合信号灯的运行规则,需要运用一些控制手段,让这些车辆遵纪守法。
以下是通过设置一些车辆的基础属性以及根据 type 类型判断加载对应的车辆模型的伪代码:
loadCar(type) { // 创建车辆新节点 let car = new ht.Node(); // 根据车辆类型创建加载对应车辆模型 switch (type) { case 'familyCar': car.s('shape3d', 'models/HT模型库/交通/车辆/家用车.json'); break; case 'truck': car.s('shape3d', 'models/HT模型库/交通/车辆/卡车.json'); break; case 'jeep': car.s('shape3d', 'models/HT模型库/交通/车辆/吉普车.json'); break; ... default: console.log('NO THIS TYPE CAR!'); break; } // 设置车辆不可选择和不可移动 car.s({ '3d.selectable': false, '3d.movable': false }); // 设置锚点 --- 车的头部 car.setAnchor3d(1, 0, 0.5); // 设置初始位置 car.setPosition3d(0, 100000, 0); let typeIndex = 1; // 判断是否此前生成了这种类型的车辆 this.g3dDm.each(data => { if (data.getTag() === type + typeIndex) { typeIndex++; } }) // 设置车辆节点标签 car.setTag(type + typeIndex); // 设置车辆节点的名字 car.setDisplayName(type); // 将车辆节点添加到数据模型中 this.g3dDm.add(car); }
通过管道动画的驱使,生成的车辆在信号灯的控制下行驶着,首先要通过 ht.Polyline 绘制出一条车辆的运行管道,其实现上有二维形式与三维的形式,是继承于 ht.Shape,当 shape3d 设置为 cylinder 时则显示为立体的三维管线效果。而当车辆加载以及管道绘制的完成,我们就可以通过 HT 封装的动画函数 ht.Default.startAnim() 来趋势车辆沿着管道运行,实现管道动画的效果。
而关于管道动画的实现上,基于 ht.Default.startAnim() 封装了一个 move 的动画函数是节点沿着路径平滑移动的封装函数,主要参数为:
通过绘制一条运行路线的管道,ht.Default.getLineCacheInfo() 得到这条管道的点位和分割信息 cache,然后管道信息通过 ht.Default.getLineLength() 得到管道的长度,并且通过 ht.Default.getLineOffset() 来获取连线或者管道指定比例的偏移信息,从而达到移动的效果,是为了通过 node.lookAtX() 来获取节点下一个面对的朝向的位置信息,并设置节点此时的位置,从而达到节点沿着路径平滑移动的效果。
move(node, path, duration = 20000, animParams) { // path._cache_ 里面存着管道的节点信息 let cache = path._cache_; // 如果没有缓存信息,则获取 path._cache_ 里面存着管道的节点信息 if (!cache) { cache = path._cache_ = ht.Default.getLineCacheInfo(path.getPoints(), path.getSegments()); } // 获取管道缓存信息的长度 const len = ht.Default.getLineLength(cache); // 设置动画对象初始化 animParams = animParams || {}; // 设置 action 为 animParams 的动画执行函数 const action = animParams.action; // 动画执行部分 animParams.action = (v, t) => { // 获取管道运动的偏移信息 const offset = ht.Default.getLineOffset(cache, len * v); // 获取偏移位置上的点 const point = offset.point; // 设置节点看向的下一个位置 node.lookAtX([point.x, point.y, point.z], "forward "); // 设置节点的位置 node.p3(point.x, point.y, point.z); // 判断动画是否执行完 if (action) action(); }; // 循环调用动画执行函数 return loop(animParams.action, duration); } // 循环动画函数 loop(action, duration) { return ht.Default.startAnim({ duration: duration, action: action }); }
视频监控作为本系统重要的解决方案之一,提供了仿真和实景的两套风格实现,仿真的意义是通过简单的车辆模型模拟出交通流向然后通过绘制场景并共用一个数据模型来体现,实质意义上可以通过科幻风格突出监控的重点信息,例如设备维护以及一些违法的场景重现;而对于实景风格是通过对接实时的视频数据流,然后展示出路口的运行状态,以真实的原貌重现路口的每一个动态信息。
2.1 拟真摄像头的实现原理
作为拟真方案的摄像头实现上,通过 HT 的渲染元素 renderHTML 加载出一个弹窗的三维小场景,并且与路口主场景共享一个数据模型 dataModel,实现数据变化以及动画的互通,而现在只需要去获取所点击摄像头的真实视角信息,通过全局事件派发将获取的真实视角,传到摄像头弹窗场景去改变对应视角的 眼睛 eye 和 中心点 center,就能达到获取摄像头在主场景中拟真的视角。为了使摄像头活动的时候具有动画的辨识度,在每个摄像头前绘制了一个锥形的监控区域吸附在摄像头上,界定出摄像头的监控范围,达到智慧监控的效果。
在交互实现上,通过点击选中摄像头后,使这个摄像头的锥形区域变为直线,表示为选中状态同时标记选中的摄像头的选中前后顺序,并且通过派发事件驱使 2D 图纸上显示摄像头弹窗,在弹窗显示的同时,通过计算得到实时变动的中心点位置信息(center),只要实时通过全局派发事件把位置信息传输到摄像头弹窗场景,就能起到摄像头场景视角与主场景中所点击摄像头的视角同步;取消弹窗显示的交互方式是通过双击场景背景,恢复摄像头锥形区域并且派发事件去隐藏 2D图纸上的摄像头弹窗:
// 全局事件派发器 var G = {} window.G = G; G.event = new ht.Notifier(); handleInteractive(e) { const {kind, data} = e; if(kind === 'clickData') { // 判断点击节点是否带有标签,没有标签则 return let tag = data.getTag(); if(!tag) return; // 判断标签名为摄像头 if(tag.indexOf('camera') >= 0) { // 设置指定上一个点击的摄像头和当前点击的摄像头 this.lastClickCamera = this.nowClickCamera; this.nowClickCamera = data; // 如果之前有点击摄像头,则初始化摄像头锥体的大小 if (this.lastClickCamera !== null) { let clickRangeNode = this.lastClickCamera.getChildren()._as[0]; clickRangeNode.s3(300, 150, 500); } // 如果有点击摄像头,则设定所点击摄像头锥体的大小 if (this.nowClickCamera !== null) { let clickRangeNode = this.nowClickCamera.getChildren()._as[0]; clickRangeNode.s3(5, 5, 500); } // 获取点击摄像头的位置信息 var cameraP3 = nowClickCamera.p3(); // 获取点击摄像头的旋转信息 var cameraR3 = nowClickCamera.r3(); // 获取点击摄像头的大小信息 var cameraS3 = nowClickCamera.s3(); // 当前锥体起始位置 var realP3 = [cameraP3[0], cameraP3[1] + cameraS3[1] / 2, cameraP3[2] + cameraS3[2] / 2]; // 将当前眼睛位置绕着摄像头起始位置旋转得到正确眼睛位置 var realEye = getCenter(cameraP3, realP3, cameraR3); // 全局事件派发至摄像头场景改变视角的眼睛 eye 和中心点 center G.event.fire({ type: 'videoCreated', eye: realEye, center: getCenter(realEye, [realEye[0], realEye[1] ,realEye[2] + 5], cameraR3) }); // 视频弹窗显示派发 event.fire(SHOW_VIDEO, {g3dDm: this.g3dDm, cameraName:tag}); } } // 双击背景隐藏摄像头场景窗口,并初始化摄像头锥体的大小 if(kind === 'doubleClickBackground') { // 视频弹窗隐藏派发 event.fire(HIDE_VIDEO); // 如果之前有点击摄像头,则初始化摄像头锥体的大小 if (this.nowClickCamera !== null) { let clickRangeNode = this.nowClickCamera.getChildren()._as[0]; clickRangeNode.s3(300, 150, 500) } // 设置当前点击摄像头为空 this.nowClickCamera = null; } }
以上所涉及到方法 getCenter(),实际上是通过去获取每个摄像头节点在场景中对应的旋转角度,简化理解就是一个点 A 围绕着另外一个点 B 旋转,即中心点位置(center)围绕着眼睛位置(eye)旋转,而我们则需要去计算点 A 的位置(中心点位置 center),这里通过封装一个 getCenter 方法用于获取 3d 场景中点 A 绕着点 B 旋转 angle 角度之后得到的点 A 在 3d 场景中的位置,方法中采用了 HT 封装的 ht.Math 下面的方法,以下为实现的代码:
实现代码如下:
// pointA 为 pointB 围绕的旋转点 // pointB 为需要旋转的点 // r3 为旋转的角度数组 [xAngle, yAngle, zAngle] 为绕着 x, y, z 轴分别旋转的角度 const getCenter = function(pointA, pointB, r3) { const mtrx = new ht.Math.Matrix4(); const euler = new ht.Math.Euler(); const v1 = new ht.Math.Vector3(); const v2 = new ht.Math.Vector3(); mtrx.makeRotationFromEuler(euler.set(r3[0], r3[1], r3[2])); v1.fromArray(pointB).sub(v2.fromArray(pointA)); v2.copy(v1).applyMatrix4(mtrx); v2.sub(v1); return [pointB[0] + v2.x, pointB[1] + v2.y, pointB[2] + v2.z]; };
2.2 实景摄像头的实现原理
对于实景的实现上,我们可以通过对接实时的视频数据流,现在主要常用的流媒体传输协议有:RTMP、RTSP、HLS 和 HTTP-FLV。
例如通过一个简单的 RTMP 视频流的对接就可以明白其实现的原理。对于的视频的载入,需要用到 video.js 的插件进行展示,所以先引入插件,然后对接视频流后,也是同样通过全局事件派发到 HT 的渲染元素 renderHTML 将视频流渲染到场景图纸中,以下是实现的伪代码:
// 引入 video.js 插件 <script src="./js/video.js"></script> // 通过全局事件派发到渲染元素 renderHTML 去渲染视频到场景图纸中 G.event.add(function(e){ if(e.type==='videoCreated'){ var div=e.div; div.innerHTML='<video id="video" class="video-js vjs-default-skin"><source src="rtmp://10.10.70.57/live/test" type="rtmp/flv"></video>'; window.player = videojs('video'); } });
对于一些路口的关键数据可以通过接口对接的形式展示出来,通过实时数据变动的监控,即时反馈道路路口的信息数据,包含一些事故统计、车流量分析、设备维护状态以及车辆违章。这些数据依赖展示的载体是通过 HT 的 2D 组态矢量图来实现的,矢量图适用于很多场合,其特点是放大后图像不会失真,可以适应不同分辨率的屏幕都不会模糊,使得整个系统适用与不同的屏幕下,包括在大屏的监控系统上也得心应手。而通过矢量图的信息展示上,对比以往一些单纯数据展示的页面,本系统则是通过一些自定义的动画交互,可以使得整个页面的上效果呈现上有一种沉浸式的体验,整体的客户体验可以大大地提升。
数据的展示形式有着多种多样的方式,可以通过一些图表插件,例如 eEcharts,HT 也有机制可以让我们使用它们,当然我们也可以自定义封装一些组件,示例中的表格和圆环进度条就是一种很好地呈现方式,而在许多效果呈现上,HT也是拥有自己一套功能丰富的 UI 组态,如果感兴趣的话可以通过 HT主页 上来了解使用。
对于数据接口的获取,可以运用一些主流的方法:
ajax 和 axios 要实时获取接口数据得通过轮询调用接口的形式进行传输,而 WebSocket 可以双向进行数据传输,在选择运用上可以匹配自己的实现需求。本系统是采用通过 axios 调用接口获取实时数据。
示例中的柱状图和折线图,是通过 HT 里的机制下去使用 eEcharts 上一些图表进行自定义配置而实现的,继而通过对 axios 接口轮询调用载入数据,展现了实时的路口监控数据信息:
loadData() { // 获取图纸的数据模型 let dm = this.g2d.dm(); // 获取车流量接口的数据 axios.get('/traffic').then(res => { // 接入日车流量折线图的数据 this.lineChart1.a({ 'seriesData1': res.lineChartData1, 'axisData' : res.axisData }); // 接入车辆运行高峰折线图的数据 this.lineChart2.a({ 'seriesData1': res.lineChartData2, 'axisData' : res.axisData }) // 采用数字跳动的方式载入一些数据内容 setBindingDatasWithAnim(dm, res, 800, v => Math.round(v)); // 接入运行峰值的时刻 this.peakTime.s('text', res.peakTime); }); // 载入设备运行状态的数据 axios.get('/equipmentStatus').then(res => { setBindingDatasWithAnim(dm, res, 800, v => Math.round(v)); }); // 载入事故统计的数据 axios.get('/accident').then(res => { setBindingDatasWithAnim(dm, res, 800, v => Math.round(v)); // 接入每月事故柱状图的数据 this.accidentBar.a({ axisData: res.axisData, seriesData1: res.seriesData1 }) }); }
关于表格的绘制,实际上是封装了一个组件来实现的,而其中的交互动画,主要还是运用了 HT 自带的动画函数 ht.Default.startAnim(),横向通过滚动 100 宽度并数据透明度慢慢浮现,纵向采用向下偏移一行表格行高 54 来添加新的警报信息。
addTableRow() { // 获取表格节点 let table = this.table; // 通过 axios 的 promise 请求接口数据 axios.get('getEvent').then(res => { // 获取表格节点滚动信息的数据绑定 let tableData = table.a('dataSource'); // 通过向 unshift() 方法可向滚动信息数组的开头添加一个或更多元素 tableData.unshift(res); // 初始化表格的纵向偏移 table.a('ty', -54); // 开启表格滚动动画 ht.Default.startAnim({ duration: 600, // 动画执行函数 action action: (v, t) => { table.a({ // 通过添加数据后,横向滚动 100 'firstRowTx': 100 * (1 - v), // 第一行行高出现的透明度渐变效果 'firstRowOpacity': v, // 纵向偏移 54 的高度 'ty': (v - 1) * 54 }); } }); }); }
城市大脑被炒的火热,红绿灯调控解决拥堵问题只是智慧交通的冰山一角,常态下的监测监管、分析研判,才是交通管理者真正得以运筹帷幄的有利保障,而这些,离不开让交通数据可见可感的“智慧交通可视化决策平台”。
智慧交通可视化系统通过实时数据的载入,可以有效即时地反映出路口所处的状态,使得以往散乱的各个系统串联起来形成一个完整的智慧交通系统,而在这个系统下串联出许多条道路的监控,这其实是一个智慧城市的缩影,而在以往的经验积累上,HT 也完成了一套完整的智慧城市系统的解决方案,串联了许多城市数据以及设施建设的记录,可以起到合并许多个功能子系统进行实时的数据监控和展示:HT 智慧城市
2019 我们也更新了数百个工业互联网 2D/3D 可视化案例集,在这里你能发现许多新奇的实例,也能发掘出不一样的工业互联网:https://mp.weixin.qq.com/s/ZbhB6LO2kBRPrRIfHlKGQA
同时,你也可以查看更多案例及效果:https://www.hightopo.com/demos/index.html