基於 HTML5 WebGL 的挖掘機 3D 可視化應用

前言

在工業互聯網以及物聯網的影響下,人們對於機械的管理,機械的可視化,機械的操作可視化提出了更高的要求。如何在一個系統中完整的顯示機械的運行情況,機械的運行軌跡,或者機械的機械動作顯得尤爲的重要,因爲這會幫助一個不瞭解這個機械的小白可以直觀的瞭解機械的運行情況,以及機械的所有可能發生的動作,對於三一或者其它國內國外重工機械的公司能夠有一個更好的展示或者推廣。
挖掘機,又稱挖掘機械(excavating machinery),從近幾年工程機械的發展來看,挖掘機的發展相對較快,挖掘機已經成爲工程建設中最主要的工程機械之一。所以該系統實現了對挖掘機的 3D 可視化,在傳統行業一般都是基於 Web SCADA 的前端技術來實現 2D 可視化監控,而且都是 2D 面板部分數據的監控,從後臺獲取數據前臺顯示數據,但是對於挖掘機本身來說,挖掘機的模型,挖掘機的動作,挖掘機的運行可視化卻是更讓人眼前一亮的,所以該系統對於挖機的 3D 模型做出了動作的可視化,大體包括以下幾個方面:

  • 前進後退 -- 用戶可以通過鍵盤 wasd 實現前後左右,或者點擊 2D 界面 WASD 來實現挖機的前進後退。
  • 機身旋轉 -- 用戶可以通過鍵盤左右鍵實現機身的旋轉,或者點擊 2D 界面 < > 來實現挖機機身的旋轉。
  • 大臂旋轉 -- 用戶可點擊 2D 界面第一個滑塊部分實現大臂的旋轉。
  • 小臂旋轉 -- 用戶可點擊 2D 界面第二個滑塊部分實現小臂的旋轉。
  • 挖鬥挖掘 -- 用戶可點擊 2D 界面第三個滑塊部分實現挖鬥部分的旋轉挖掘。
  • 挖機動畫 -- 用戶可點擊 2D 界面鏟子圖標,點擊之後系統會把挖機本身幾個動畫做一個串聯展示。

本篇文章通過對挖掘機可視化場景的搭建,挖機機械動作代碼的實現進行闡述,幫助我們瞭解如何使用 HT 實現一個挖掘機的可視化。

預覽地址:基於 HTML5 WebGL 的挖掘機 3D 可視化應用 http://www.hightopo.com/demo/...

界面效果預覽

挖機機械運動效果

圖片描述

通過上面 gif 圖片可以看出挖掘機的幾個主要動作。

挖機挖鬥運動效果

圖片描述

滑動頁面的第三個滑桿控制挖斗的旋轉挖掘。

挖機機身運動

圖片描述

通過上面 gif 圖片可以看出挖掘機的前進後退以及機身旋轉幾個運動。

場景搭建

該 3D 場景中所有形狀都是用 HT 內部的牆面工具進行構建,通過設置牆面透明屬性 shape3d.transparent 爲 true 以及對構建出的牆面進行貼圖來構造出場景中的類似建築的顯示效果,具體的樣式可以參考 HT 的 風格手冊,場景效果:

圖片描述

通過上圖我們可以看到場景中有許許多多的牆面建築,所以它們有許多相同的地方,例如樣式以及貼圖都是一樣的,所以在 HT 中可以通過批量的操作對這些牆面進行處理,批量的意思指的是在當前未處理的情況下的牆面圖元是一個個獨立繪製的模型,所以性能會比較差,而當一批圖元聚合成一個大模型進行一次性的繪製時,則會極大提高 WebGL 刷新性能,這就是批量所以要做的事情,具體可以參考 HT批量手冊

該系統 2d 面板部分則也是通過 HT矢量進行繪製,面板部分主要包括當前挖機的作業情況,工作時間,保修信息,故障信息等,通過二維的方式展示這些數據信息,面板截圖:

圖片描述

機械運動代碼分析

該系統中挖機的動作是十分的重要和關鍵的,大小臂運動時液壓槓該如何運動,挖鬥運動時液壓桿,旋轉點零件,以及連接到挖鬥上的零部件如何聯動起來是關鍵點,機械動畫中用到大部分數學知識進行點面位置的計算,以下是幾個關鍵的數學知識點作爲基礎:

在數學中,向量(也稱爲幾何向量、矢量),指具有大小和方向的量。它可以形象化地表示爲帶箭頭的線段。系統中會通過向量的叉乘算出與某個面垂直的向量即法向量,在計算挖鬥旋轉時需要計算出與挖鬥面垂直的法向量來進行點的計算,HT 中封裝了 ht.Math 的數學函數,裏面的 ht.Math.Vector2 指的即爲二維向量,ht.Math.Vector3 則爲三維的向量,可以傳入三個數值進行初始化向量,向量的原型中有 cross 方法用來計算兩個向量的法向量,例如以下僞代碼:

var Vector3 = ht.Math.Vector3;
var a = new Vector3([10, 10, 0]);
var b = new Vector3([10, 0, 0]);
var ab = a.clone().cross(b);

以上代碼中 ab 即爲計算法向量,a.clone 是爲了避免 cross 運算會修改原本的 a 內容,所以克隆出一個新的向量進行叉乘,以下爲示意圖:

圖片描述

挖鬥機械運動分析

在進行挖鬥部分的機械代碼時會將挖斗的位置以及挖鬥所有連接點的設備轉化爲相對於某個節點的相對位置,例如節點 A 在世界中的座標爲 [100, 100, 100],世界中還有一個節點 B,而且節點 B 的座標爲 [10, 10, 10] 則節點 A 相對於節點 B 的相對位置即爲 [90, 90, 90],因爲在計算挖斗的位置時,挖機可能此時已經運動到某一點或者旋轉到某一個軸,所以此時不能使用相對世界的座標,需要使用相對挖機機身的相對座標來進行計算,代碼中提供了 toLocalPostion(node, worldPosition) 用來將世界的座標 worldPosition 轉化爲相對 node 的相對座標,以下爲代碼實現:

var Matrix4 = ht.Math.Matrix4,
    Vector3 = ht.Math.Vector3;
var mat = new Matrix4().fromArray(this.getNodeMat(g3d, node)),
matInverse = new Matrix4().getInverse(mat),
position = new Vector3(worldPosition).applyMatrix4(matInverse);
return position.toArray();

該函數的返回值即爲相對座標,挖機中需要轉化的座標爲連接着挖鬥以及小臂的兩個零部件,系統中用 armHinge 以及 bucketHinge 來分別表示小臂樞紐以及挖鬥樞紐這兩個零部件,可以從側面來看挖斗的動作,從下圖可以看出,關鍵點是算出交點 P 的座標,交點 P 的座標則是以 armHinge 與 bucketHinge位置爲圓心,armHinge 與 bucketHinge 的長度爲半徑的兩個圓的交點,而且這兩個圓的圓心在挖鬥旋轉的過程中是不斷變化的,所以需要通過數學計算不斷算出交點的位置,以下爲示意圖:

圖片描述

通過上圖可以知道交點的位置有兩個 p1 以及 p2,程序中通過計算圓心 1 與圓心 2 構成的向量 c2ToC1,以下爲僞代碼:

var Vector2 = ht.Math.Vector2;
var c2ToC1 = new Vector2({ x: c1.x, y: c1.y }).sub(new Vector2({ x: c2.x, y: c2.y }));

c1 和 c2 爲 armHinge 以及 bucketHinge 的圓心座標,接下來是計算圓心 2 與點 p1 以及 p2 構成的向量 c2ToP1 以及 c2ToP2,以下爲僞代碼:

var Vector2 = ht.Math.Vector2;
var c2ToP1 = new Vector2({ x: p1.x, y: p1.y }).sub(new Vector2({ x: c2.x, y: c2.y }));
var c2ToP2 = new Vector2({ x: p2.x, y: p2.y }).sub(new Vector2({ x: c2.x, y: c2.y }));

通過上述操作我們可以獲得三個向量 c2ToC1c2ToP1c2ToP2 所以我們可以用到我上述講的向量叉乘的概念進行 p1 與 p2 點的選取,通過向量 c2ToC1 與 c2ToP1,以及向量 c2ToC1 與 c2ToP2 分別進行叉乘得到的結果肯定一個是大於 0 一個小於 0,二維向量的叉乘可以直接把它們視爲 3d 向量,z軸補 0 的三維向量,不過二維向量叉乘的結果 result 不是向量而是數值,如果 result > 0 時,那麼 a 正旋轉到 b 的角度爲 <180°,如果 k < 0,那麼 a 正旋轉到 b 的角度爲 >180°,如果 k = 0 那麼a,b向量平行,所以通過上面的理論知識我們可以知道結果肯定是一個大於 0 一個小於 0,我們可以在程序中測下可以知道我們需要獲取的是大於 0 的那個點 P1,所以每次可以通過上述的方法進行兩個交點的選擇。

以下爲挖鬥部分動畫的執行流程圖:

圖片描述

通過上述運算之後我們可以獲取到最終需要的點 P 座標,點 P 座標即爲挖鬥與小臂連接部分的一個重要點,獲取該點之後我們可以通過 HT 中提供的 lookAtX 函數來實現接下來的操作,lookAtX 函數的作用爲讓某個物體看向某一點,使用方式如下:

node.lookAtX(position, 'bottom');

node 即爲需要看向某一個點的節點,position 爲看向的點的座標,第二個參數有六個枚舉值可以選擇,分別爲 'bottom','back','front','top','right','left',第二個參數的作用是當我們需要把某個物體看向某一個點的時候我們也要指定該物體的哪一個面看向該點,所以需要提供第二個參數來明確,獲取到該函數之後我們可以通過將 bucketHinge 看向點 P,armHinge 看向點 P,就可以保持這兩個連接的設備永遠朝向該點,以下爲部分僞代碼:

bucketHinge.lookAtX(P, 'front');
armHinge.lookAtX(P, 'bottom');

所以通過上述操作之後我們已經把挖鬥部分的兩個關鍵零件的位置已經擺放正確,接下來是要正確的擺放與挖鬥連接的小臂上液壓部分的位置,下一部分爲介紹該節點如何進行擺放。

液壓聯動分析

在場景中我們可以看到液壓主要分爲兩個部分,一部分爲白色的較細的液壓桿,一部分爲黑色的較厚的液壓桿,白色的液壓桿插在黑色的液壓桿中,所以在小臂或者挖鬥旋轉的過程中我們要保持兩個節點始終保持相對的位置,通過上一步驟中我們可以知道 lookAtX 這個函數的作用,所以在液壓桿部分我們也是照樣用該函數來實現。

在上一步我們獲取到了挖鬥旋轉過程中的關鍵點 P,所以在挖鬥旋轉的過程我們小臂上的液壓桿也要相應的進行變化,具體的操作就是將小臂的白色液壓桿的位置設置爲上步中計算出來的點 P 的位置,當然需要把白色液壓桿的錨點進行相應的設置,之後讓白色液壓桿 lookAt 黑色液壓桿,同時讓黑色液壓桿 lookAt 白色液壓桿,這樣下來兩個液壓桿都在互相看着對方,所以它們呈現出來的效果就是白色液壓桿在黑色液壓桿中進行伸縮,以下爲僞代碼:

bucketWhite.p3(P);
bucketWhite.lookAtX(bucketBlack.p3(), 'top');
bucketBlack.lookAtX(P, 'bottom');

代碼中 bucketWhite 節點即爲小臂上白色液壓桿,bucketBlack 節點爲小臂上黑色液壓桿,通過以上的設置就可以實現伸縮的動畫效果,以下爲液壓的運行圖:

圖片描述

同理挖機身上的大臂的液壓動作以及機身與大臂連接部分的液壓動作都是使用上面的方法來實現,以下爲這兩部分的代碼:

rotateBoom: (rotateVal) = >{
    excavatorBoomNode.setRotationX(dr * rotateVal);
    let archorVector = [0.5 - 0.5, 0.56 - 0.5, 0.22 - 0.5];
    let pos = projectUtil.toWorldPosition(g3d, excavatorBoomNode, archorVector);
    boomWhite.lookAtX(boomBlack.p3(), 'bottom');
    boomBlack.lookAtX(pos, 'top');
},
rotateArm: (rotateVal) = >{
    projectUtil.applyRelativeRotation(excavatorArmNode, excavatorBoomNode, -rotateVal);
    let archorVector = [0.585 - 0.5, 0.985 - 0.5, 0.17 - 0.5];
    let pos = projectUtil.toWorldPosition(g3d, excavatorArmNode, archorVector);
    armWhite.lookAtX(armBlack.p3(), 'bottom');
    armBlack.lookAtX(pos, 'top');
}

我將兩部分的運動封裝爲兩個函數 rotateBoom 以及 rotateArm 分別是大臂與機身連接處的液壓運動與大臂上的液壓運動,在該部分中爲了精確的獲取看向的點,我通過 toWorldPosition 方法將相對座標轉化爲世界座標,相對座標爲黑白液壓桿的錨點座標,轉化爲相對大臂或者機身的世界座標。

基本運動分析

挖機的基本運動包括前進後退,機身旋轉,這一部分會相對上面的運動簡單許多,在 HT 的三維座標系中,不斷修改挖機機身的 x,y,z 的座標值就可以實現挖機的前進後退,通過修改機身的 y 軸旋轉角度則可以控制機身的旋轉,當然挖機身體上的所有其它零部件需要吸附在機身身上,當機身進行旋轉時其它零部件則會進行相應的旋轉,在進行前進的時候挖機底部的履帶會進行對應的滾動,當然履帶我們這邊是用了一個履帶的貼圖貼在上面,當挖機前進的時候修改貼圖的偏移值就可以實現履帶的滾動,修改偏移值的僞代碼如下:

node.s('shape3d.uv.offset', [x, y]);

上面的 x,y 分別爲 x 軸與 y 軸方向的偏移值,在挖機前進後退的過程中不斷修改 y 的值可以實現履帶的滾動效果,具體的文檔說明可以查看 3D手冊

在挖機前進後退的過程中我們可以 wasd 四個鍵同時按下,並且可以對按鍵進行一直的響應,在 js 中可以通過 document.addEventListener('keydown', (e) => {}) 以及 document.addEventListener('keyup', (e) => {}) 進行監聽,但是這隻能每次執行一次需要執行的動作,所以我們可以在外部起一個定時器,來執行 keydown 時候需要不斷執行的動作,可以用一個 keyMap 來記錄當前已經點擊的按鍵,在 keydown 的時候紀錄爲 true 在 keyup 的時候記錄爲 false,所以我們可以在定時器中判斷這個 bool 值,當爲 true 的時候則執行相應的動作,否則不執行,以下爲對應的部分關鍵代碼:

let key_pressed = {
    65 : {
        status: false,
        action: turnLeft
    },
    87 : {
        status: false,
        action: goAhead
    },
    68 : {
        status: false,
        action: turnRight
    },
    83 : {
        status: false,
        action: back
    },
    37 : {
        status: false,
        action: bodyTurnLeft
    },
    39 : {
        status: false,
        action: bodyTurnRight
    }
};
setInterval(() = >{
    for (let key in key_pressed) {
        let {
            status,
            action
        } = key_pressed[key];
        if (status) {
            action();
        }
    }
},
50);
document.addEventListener('keydown', (event) = >{
    let keyCode = event.keyCode;
    key_pressed[keyCode] && (key_pressed[keyCode].status = true);
    event.stopPropagation();
},
true);
document.addEventListener('keyup', (event) = >{
    let keyCode = event.keyCode;
    key_pressed[keyCode] && (key_pressed[keyCode].status = false);
    event.stopPropagation();
},
true);

從上面代碼可以看出我在 key_pressed 變量中記錄對應按鍵以及按鍵對應的 action 動作,在 keydown 與 keyup 的時候對應修改當前 key 的 status 的狀態值,所以可以在 Interval 中根據 key_pressed 這個變量的 status 值執行對應的 action 動作,以下爲執行流程圖:

圖片描述

HT 的輕量化,自適應讓當前系統在手機端也能流暢的運行,當然目前移動端與電腦端的 2D 圖紙部分是加載不同的圖紙,在移動端的 2D 部分只留下操作挖機的操作部分,其它部分進行了相應的捨棄,不然在移動端小屏幕下無法展示如此多的數據,在 3D 場景部分都是共用同一個場景,通過場景搭建部分的批量操作使得 3D 在手機端也十分流暢的運行,以下爲手機端運行截圖:

圖片描述

總結

物聯網已經融入了現代生活,通過內嵌到機械設備中的電子設備,我們能夠完成對機械設備的運轉、性能的監控,以及對機械設備出現的問題進行及時的預警。在該系統 2D 面板監控部分就是對採集過來的數據進行可視化的展示,而且我們可以藉助大數據和物聯網技術,將一臺臺機械通過機載控制器、傳感器和無線通訊模塊,與一個龐大的網絡連接,每揮動一鏟、行動一步,都形成數據痕跡。大數據精準描繪出基礎建設開工率等情況,成爲觀察固定資產投資等經濟變化的風向標。所以在實現上述挖機動作之後,通過與挖機傳感器進行連接之後,可以將挖掘機此時的真實動作通過數據傳遞到系統,系統則會根據動作進行相應的真實操作,真正實現了挖機與網絡的互聯互通。

程序運行截圖:

圖片描述

圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章