智慧城市交通的要素:路口監管視覺化系統的解決方案
整合地理資訊系統、視訊監控系統、交管部門各業務系統資料,對交通路況車流量、事故處理報告等要素進行綜合監測,並支援點選檢視具體警力、機動目標、交通事件、監控視訊等詳細資訊,幫助管理者實時掌握交通整體執行態勢。 2、交通基礎資源監測 支援對攝像頭、流量檢測裝置、交通訊號燈等交通基礎資源的數量、空間位置分佈、實時狀態等資訊進行監測和視覺化管理,支援裝置詳細資訊查詢,支援對未正常工作的裝置進行告警,加強管理者對裝置狀態的監測與感知,提升交通基礎設施的運維管理效率。 3、視訊巡檢監測
支援整合前端視訊巡檢系統,有效結合視訊智慧分析、智慧定位、智慧研判技術,對道路擁堵點位、隱患點位、事故點位等情況進行視覺化監測,實現異常事件的實時告警、快速顯示,並可智慧化調取異常點位周邊監控視訊,有效提升接處警效率。 4、路口訊號燈監測
支援整合路口訊號燈、視訊監控等系統資料,對路口交通流量、流速、車輛及道路異常事件、訊號燈狀態等資訊進行實時監測,並可結合專業的模型演算法,比對歷史最佳通行速度及最佳通行量,對路口交通態勢進行視覺化分析研判,為訊號配時調優和路口交通組織優化提供科學的決策依據,有效提升交通執行效率。 5、違法違章案件分析
充分整合交管部門現有資料資源,提供多種視覺化分析、互動手段,對海量歷史違法違章案件資料進行視覺化串並分析,深度挖掘案件時空分佈規律,為交管部門進行原因分析、主動防範等業務應用提供支援。 程式碼實現 一、車輛生成以及在紅綠燈控制下的執行 在路口的模擬的執行系統下,場景中有這許許多多來往執行的車輛,是通過動態載入車輛模型並且設定管道執行動畫,而這些車輛當然需要符合訊號燈的執行規則,需要運用一些控制手段,讓這些車輛遵紀守法。 以下是通過設定一些車輛的基礎屬性以及根據 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 的動畫函式是節點沿著路徑平滑移動的封裝函式,主要引數為:
- node:動畫節點;
- path:執行路徑;
- duration:動畫執行排程時間;
- animParams:動畫引數;
通過繪製一條執行路線的管道,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 (Real Time Messaging Protocol):實時訊息傳輸協議,RTMP 協議中,視訊必須是 H264 編碼,音訊必須是 AAC 或 MP3 編碼,且多以 flv 格式封包。因為 RTMP 協議傳輸的基本是 FLV 格式的流檔案,必須使用 flash 播放器才能播放。
- RTSP (Real-Time Stream Protocol):RTSP 實時效果非常好,適合視訊聊天、視訊監控等方向。
- HLS(Http Live Streaming):由 Apple 公司定義的基於 HTTP 的流媒體實時傳輸協議。傳輸內容包括兩部分:1.M3U8 描述檔案,2.TS 媒體檔案。TS 媒體檔案中的視訊必須是H264編碼,音訊必須是 AAC 或 MP3 編碼。資料通過 HTTP 協議傳輸。目前 video.js 庫支援該格式檔案的播放。
- HTTP-FLV:本協議就是 http+flv,將音視訊資料封裝成FLV格式,然後通過http協議傳輸到客戶端,這個協議大大方便了瀏覽器客戶端播放直播視訊流.目前 flv.js 庫支援該格式的檔案播放。
例如通過一個簡單的 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:使用 JavaScript 向伺服器提出請求並處理響應而不阻塞使用者核心物件 XMLHttpRequest;
- axios:基於 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中;
- WebSocket:HTML5 提供的一種在單個 TCP 連線上進行全雙工通訊的協議;
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