前言
可視化的智慧樓宇在 21 世紀是有急迫需求的,中國被世界稱為“基建狂魔”,全球高層建築數量位居首位,所以對於樓宇的監控是必不可少。智慧樓宇可視化系統更多突出的是管理方面的功能,即如何的全面實現優化控制和管理,節能降耗、高效、舒適、環境安全這樣一個目的,可以這樣說,判斷一個建築物是否具有智能建築特點,要看它是否具有 IBMS 的系統集成,這是很重要的判定條件。IBMS 系統的建立必不可少需要硬件采集信息到后台,隨着工業互聯網,物聯網概念的推廣應用,讓硬件與軟件的結合變得系統化,過程化,使得智慧樓宇可視化成為可能。
本文所概述的智慧樓宇是基於一棟真實建築可視化簡化建模之后得到的效果,樓層的輪廓在系統中抽象成線條,整體拼湊起來就形成了一棟簡化版的樓宇,當然其中的樓層地板也是抽象成簡單的立方體包裹在對應樓層線框的里面。對應的電梯,閘機,扶梯等物體也簡化抽象成六面體,線條。抽象的物體全部采用 HT 的 API 即可創建完成,所以抽象之后具有復用性,同樣的方法可以采用到其它所有樓宇。
可視化監控功能
預覽地址:基於 HTML5 WebGL 的智慧樓宇三維可視化監控 https://hightopo.com/demo/IBMS/
- 工單監測 -- 統計樓宇收到的工單信息
- 客流監測 -- 顯示當日進出樓宇的人流量信息
- 消防報警 -- 統計當日樓宇的消防報警信息
- 重點區域監控 -- 顯示重點區域的攝像頭信息
- 停車場車位統計 -- 統計停車場車位信息
- 樓宇控制 -- 可單獨控制不同類型樓層的閘機,電梯,扶梯,警報和客流的顯示與隱藏,監控此時電梯,扶梯等物體的運行情況
- 告警信息 -- 實時滾動顯示當前樓宇的告警信息
可視化監控預覽
樓宇效果

樓層效果

樓層控制效果

可視化實現
UI 以及模型實現
2D 可視化
2d 部分的 UI 都是采用 HT 暴漏的 API 進行描繪實現,UI 部分都繪制在一個 canvas 節點上,並且全都為矢量,所以放大不會失真,兩側的實時數據通過 數據綁定 實現實時刷新,HT 在處理實時刷新的時候只會刷新當前需要刷新的區域,所以不會出現例如修改一個頁面上的某個文字而導致整個 canvas 重繪。

上圖所示的攝像頭監控畫面則是通過將 video 標簽疊加在 2d 對應區域的 UI 上面,HT 對於第三方需要嵌入的 dom 節點可以通過疊加 dom 的方式在對應區域進行疊加來實現。目前攝像頭采用的第三方插件多為 video.js 或者 Aliplayer 來實現嵌入,通過將對應的 iframe 嵌入到指定區域來實現,以下為嵌入的 rtmp 實時視頻流的圖標參考代碼:
if (!cache.view) {
// 創建 iframe 節點 並且設置 style 屬性
var iframe = cache.htmlView = document.createElement('iframe');
iframe.setAttribute('style', 'margin: 0; padding: 0; border: 0;');
iframe.setAttribute('id', 'cameraIframe');
iframe.src = './rtmp/rtmp.html';
cache.view = iframe;
// 調用 graphView 上的 layoutHTML 布局方法,會自動布局到上面所說的對應區域
cache.htmlView.layoutHTML = function() {
gv.layoutHTML(data, iframe, true)
}
}
// 讀取 rtmp 視頻流地址
var cameraURL = data.a('cameraURL');
if (cache.cameraURL !== cameraURL) {
cache.cameraURL = cameraURL;
// 獲取 iframe 頁面暴漏的 playVideoByUrl 方法,來執行對應的播放操作
if (cache.view.contentWindow) {
if (cache.view.contentWindow.playVideoByUrl) {
cache.view.contentWindow.playVideoByUrl(cameraURL, cache);
}
} else {
// 起定時器的目的是為了防止 contentWindow 在第一次還沒有加載完成導致 contentWindow 為 null
if (window.loadFrameInterval) {
clearInterval(window.loadFrameInterval);
window.loadFrameInterval = null;
}
window.loadFrameInterval = setInterval(function() {
// 此時 contentWindow 已經加載完成,所以執行 playVideoByUrl 操作
if (cache.view.contentWindow.playVideoByUrl) {
cache.view.contentWindow.playVideoByUrl(cameraURL, cache);
clearInterval(window.loadFrameInterval);
window.loadFrameInterval = null;
}
},
100);
}
}
以下為 iframe 頁面中的部分部分代碼:
// 記錄此時的視頻播放器對象
var player = null;
// 記錄此時的圖標節點 cache 對象
var nodeCache = null;
// 創建視頻播放器 dom 節點
function createVideoContainer() {
var div = document.createElement('div');
div.classList.add('prism-player');
div.id = 'player-con';
document.body.appendChild(div);
}
// 通過視頻流地址初始化 Aliplayer 播放器
function playVideoByUrl(cameraURL, cache) {
// 如果已經初始化過 player 則需要先銷毀
pauseVideo();
// 創建視頻播放器 dom 節點
createVideoContainer();
// 初始化 player 播放器
player = new Aliplayer({
"id": "player-con",
"source": cameraURL,
"width": "100%",
"height": "100%",
"autoplay": true,
"isLive": true,
"rePlay": false,
"playsinline": true,
"preload": true,
"controlBarVisibility": "hover",
"useFlashPrism": true,
});
nodeCache = cache;
}
// 銷毀 player 對象
function pauseVideo() {
if (player) {
player.dispose();
player = null;
document.body.removeChild(document.getElementById('player-con'));
if (nodeCache) {
nodeCache.cameraURL = null;
nodeCache = null;
}
}
}
3D 可視化
3d 部分系統采用極簡的方式來表示樓宇,用到了 HT 中的 ht.Shape 節點類型,可以參考 形狀手冊 ,該節點類型可以指定 points 以及 segments,所以可以用來動態生成管道或者牆面,通過簡化之后我們的樓宇就可以通過管道和牆面兩種類型來構造,並且構造的時候如果有許多層結構相同,或者有規律的從下到上,或從上到下的形狀,則只需要構造第一層以及最后一層之后其它所有樓層都可以通過計算的方式動態生成,以下為示意圖:

通過上圖可以知道中間所有的樓層的對應點的信息我們都可以通過計算獲取,具體的實現的方式在下一節會通過偽代碼闡述。
3d 部分還有個電梯的簡單上下移動,該部分暫時通過模擬的方式進行移動,沒有具體對接真實的電梯運行數據,其中所有的電梯都是對應的電梯線的子節點,所以電梯線部分存儲了自己所能到達的樓層,則對應電梯線上的電梯也可以知道自己在垂直空間的運動范圍,之后可以通過 requestAnimationFrame 來實現動畫效果,如下偽代碼:
// 每幀移動的距離
var moveStep = 5;
function frameFunc() {
// 電梯的移動方向 1 為向上,-1 為向下
var direction = moveNode.a('direction') || 1;
// 電梯下一幀的位置
var nextPosY = moveNode.getElevation() + moveStep * direction;
// 如果下一幀位置大於最高層的位置則改變 direction 的方向
if (nextPosY >= maxElevation) {
direction = -1;
}
// 如果下一幀位置小於最低層的位置則改變 direction 的方向
if (nextPosY <= minElevation) {
direction = 1;
}
moveNode.a('direction', direction);
moveNode.setElevation(nextPosY);
window.requestAnimationFrame(frameFunc);
}
window.requestAnimationFrame(frameFunc);
樓宇可視化實現
為了方便講解,我們以如下圖錯開的簡單樓層來講解:

上圖第一層和最后一層都由 6 個點組成,如下圖:

從上圖可以看出第一層和最后一層都是由六個點組成,所以第一層和最后一層中間的所有層可以根據這兩層點的數據進行計算得出,例如上面圖中如果第一層和最后一層中間還有兩層的話,則可以計算出對應每層的平面坐標,垂直空間的坐標可以根據第一層和最后一層的垂直距離進行平分得到,描述圖如下:

若中間有兩層則生成的效果圖如下:

通過以上幾個可視化的步驟我們可以了解到生成一棟簡單的樓層所需要經歷的步驟,如下為步驟圖:

通過如上操作我們就可以繪制出某些樓層,如果建築由不同的樓層結構構成,則同理可以按照如上順序分開分別繪制,最后拼湊起來。
以下為構造樓宇可視化部分偽代碼:
// maxFloorIndex 為最高樓層索引號
for (let i = 0; i < maxFloorIndex; i++) {
let percent = i / (maxFloorIndex - 1), // 當前樓層在首層和末層之間的占比
curFloorPoints = []; // 記錄當前層的所有點信息
// floorPointLength 記錄每一層點的數量
for (let j = 0; j < floorPointLength; j++) {
let v3 = new Vector3();
// firstFloorVector3 指的第一層點的集合
// lastFloorVector3 最后一層點的集合
// lerpVectors 根據 percent 百分比計算出中間的點
v3.lerpVectors(firstFloorVector3[j], lastFloorVector3[j], percent);
curFloorPoints.push(v3);
}
// 只需要 xz 平面的點來構成樓層
let points = [],
elevation = curFloorPoints[0].y;
curFloorPoints.forEach((point) = >{
points.push({
x: point.x,
y: point.z
});
});
// 構造樓層邊框的可視化節點
let wireNode = getWire(points, elevation, floorNum);
dm3d.add(wireNode);
// 構造樓層地板的可視化節點
let floorNode = getFloor(points, elevation, floorNum);
dm3d.add(floorNode);
}
總結
傳統的 智慧樓宇/樓宇自動化/樓宇安防/智慧園區 常會采用 BIM(建築信息模型 Building information modeling) 軟件,如 Autodesk 的 Revit 或 Bentley 這類建築和工程軟件,但這些 BIM 建模模型的數據往往過於龐大臃腫,絕大部分細節信息對樓宇自控意義不大,反而影響拖累了行業 Web SCADA 或 Web 組態監控的趨勢,所以我們采用以 Hightopo 的 HT for Web 產品輕量化 HTML5/WebGL 建模的方案,實現快速建模、運行時輕量化到甚至手機終端瀏覽器即可 3D 可視化運維的良好效果。
以下為移動端的程序運行截圖:

