前言html
在工業互聯網以及物聯網的影響下,人們對於機械的管理,機械的可視化,機械的操做可視化提出了更高的要求。如何在一個系統中完整的顯示機械的運行狀況,機械的運行軌跡,或者機械的機械動做顯得尤其的重要,由於這會幫助一個不瞭解這個機械的小白能夠直觀的瞭解機械的運行狀況,以及機械的全部可能發生的動做,對於三一或者其它國內國外重工機械的公司可以有一個更好的展現或者推廣。
挖掘機,又稱挖掘機械(excavating machinery),從近幾年工程機械的發展來看,挖掘機的發展相對較快,挖掘機已經成爲工程建設中最主要的工程機械之一。因此該系統實現了對挖掘機的 3D 可視化,在傳統行業通常都是基於 Web SCADA 的前端技術來實現 2D 可視化監控,並且都是 2D 面板部分數據的監控,從後臺獲取數據前臺顯示數據,可是對於挖掘機自己來講,挖掘機的模型,挖掘機的動做,挖掘機的運行可視化倒是更讓人眼前一亮的,因此該系統對於挖機的 3D 模型作出了動做的可視化,大致包括如下幾個方面:前端
本篇文章經過對挖掘機可視化場景的搭建,挖機機械動做代碼的實現進行闡述,幫助咱們瞭解如何使用 HT 實現一個挖掘機的可視化。node
預覽地址:基於 HTML5 WebGL 的挖掘機 3D 可視化應用 http://www.hightopo.com/demo/ht-excavator/算法
界面效果預覽網絡
挖機機械運動效果app
經過上面 gif 圖片能夠看出挖掘機的幾個主要動做。ide
挖機挖鬥運動效果函數
滑動頁面的第三個滑桿控制挖斗的旋轉挖掘。工具
挖機機身運動性能
經過上面 gif 圖片能夠看出挖掘機的前進後退以及機身旋轉幾個運動。
場景搭建
該 3D 場景中全部形狀都是用 HT 內部的牆面工具進行構建,經過設置牆面透明屬性 shape3d.transparent 爲 true 以及對構建出的牆面進行貼圖來構造出場景中的相似建築的顯示效果,具體的樣式能夠參考 HT 的 風格手冊,場景效果:
經過上圖咱們能夠看到場景中有許許多多的牆面建築,因此它們有許多相同的地方,例如樣式以及貼圖都是同樣的,因此在 HT 中能夠經過批量的操做對這些牆面進行處理,批量的意思指的是在當前未處理的狀況下的牆面圖元是一個個獨立繪製的模型,因此性能會比較差,而當一批圖元聚合成一個大模型進行一次性的繪製時,則會極大提升 WebGL 刷新性能,這就是批量因此要作的事情,具體能夠參考 HT 的 批量手冊。
該系統 2d 面板部分則也是經過 HT 的矢量進行繪製,面板部分主要包括當前挖機的做業狀況,工做時間,保修信息,故障信息等,經過二維的方式展現這些數據信息,面板截圖:
機械運動代碼分析
該系統中挖機的動做是十分的重要和關鍵的,大小臂運動時液壓槓該如何運動,挖鬥運動時液壓桿,旋轉點零件,以及鏈接到挖鬥上的零部件如何聯動起來是關鍵點,機械動畫中用到大部分數學知識進行點面位置的計算,如下是幾個關鍵的數學知識點做爲基礎:
在數學中,向量(也稱爲幾何向量、矢量),指具備大小和方向的量。它能夠形象化地表示爲帶箭頭的線段。系統中會經過向量的叉乘算出與某個面垂直的向量即法向量,在計算挖鬥旋轉時須要計算出與挖鬥面垂直的法向量來進行點的計算,HT 中封裝了 ht.Math 的數學函數,裏面的 ht.Math.Vector2 指的即爲二維向量,ht.Math.Vector3 則爲三維的向量,能夠傳入三個數值進行初始化向量,向量的原型中有 cross 方法用來計算兩個向量的法向量,例如如下僞代碼:
1 var Vector3 = ht.Math.Vector3; 2 var a = new Vector3([10, 10, 0]); 3 var b = new Vector3([10, 0, 0]); 4 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 的相對座標,如下爲代碼實現:
1 var Matrix4 = ht.Math.Matrix4, 2 Vector3 = ht.Math.Vector3; 3 var mat = new Matrix4().fromArray(this.getNodeMat(g3d, node)), 4 matInverse = new Matrix4().getInverse(mat), 5 position = new Vector3(worldPosition).applyMatrix4(matInverse); 6 return position.toArray();
該函數的返回值即爲相對座標,挖機中須要轉化的座標爲鏈接着挖鬥以及小臂的兩個零部件,系統中用 armHinge 以及 bucketHinge 來分別表示小臂樞紐以及挖鬥樞紐這兩個零部件,能夠從側面來看挖斗的動做,從下圖能夠看出,關鍵點是算出交點 P 的座標,交點 P 的座標則是以 armHinge 與 bucketHinge位置爲圓心,armHinge 與 bucketHinge 的長度爲半徑的兩個圓的交點,並且這兩個圓的圓心在挖鬥旋轉的過程當中是不斷變化的,因此須要經過數學計算不斷算出交點的位置,如下爲示意圖:
經過上圖能夠知道交點的位置有兩個 p1 以及 p2,程序中經過計算圓心 1 與圓心 2 構成的向量 c2ToC1,如下爲僞代碼:
1 var Vector2 = ht.Math.Vector2; 2 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,如下爲僞代碼:
1 var Vector2 = ht.Math.Vector2; 2 var c2ToP1 = new Vector2({ x: p1.x, y: p1.y }).sub(new Vector2({ x: c2.x, y: c2.y })); 3 var c2ToP2 = new Vector2({ x: p2.x, y: p2.y }).sub(new Vector2({ x: c2.x, y: c2.y }));
經過上述操做咱們能夠得到三個向量 c2ToC1,c2ToP1,c2ToP2 因此咱們能夠用到我上述講的向量叉乘的概念進行 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 函數的做用爲讓某個物體看向某一點,使用方式以下:
1 node.lookAtX(position, 'bottom');
node 即爲須要看向某一個點的節點,position 爲看向的點的座標,第二個參數有六個枚舉值能夠選擇,分別爲 'bottom','back','front','top','right','left',第二個參數的做用是當咱們須要把某個物體看向某一個點的時候咱們也要指定該物體的哪個面看向該點,因此須要提供第二個參數來明確,獲取到該函數以後咱們能夠經過將 bucketHinge 看向點 P,armHinge 看向點 P,就能夠保持這兩個鏈接的設備永遠朝向該點,如下爲部分僞代碼:
1 bucketHinge.lookAtX(P, 'front'); 2 armHinge.lookAtX(P, 'bottom');
因此經過上述操做以後咱們已經把挖鬥部分的兩個關鍵零件的位置已經擺放正確,接下來是要正確的擺放與挖鬥鏈接的小臂上液壓部分的位置,下一部分爲介紹該節點如何進行擺放。
液壓聯動分析
在場景中咱們能夠看到液壓主要分爲兩個部分,一部分爲白色的較細的液壓桿,一部分爲黑色的較厚的液壓桿,白色的液壓桿插在黑色的液壓桿中,因此在小臂或者挖鬥旋轉的過程當中咱們要保持兩個節點始終保持相對的位置,經過上一步驟中咱們能夠知道 lookAtX 這個函數的做用,因此在液壓桿部分咱們也是照樣用該函數來實現。
在上一步咱們獲取到了挖鬥旋轉過程當中的關鍵點 P,因此在挖鬥旋轉的過程咱們小臂上的液壓桿也要相應的進行變化,具體的操做就是將小臂的白色液壓桿的位置設置爲上步中計算出來的點 P 的位置,固然須要把白色液壓桿的錨點進行相應的設置,以後讓白色液壓桿 lookAt 黑色液壓桿,同時讓黑色液壓桿 lookAt 白色液壓桿,這樣下來兩個液壓桿都在互相看着對方,因此它們呈現出來的效果就是白色液壓桿在黑色液壓桿中進行伸縮,如下爲僞代碼:
1 bucketWhite.p3(P); 2 bucketWhite.lookAtX(bucketBlack.p3(), 'top'); 3 bucketBlack.lookAtX(P, 'bottom');
代碼中 bucketWhite 節點即爲小臂上白色液壓桿,bucketBlack 節點爲小臂上黑色液壓桿,經過以上的設置就能夠實現伸縮的動畫效果,如下爲液壓的運行圖:
同理挖機身上的大臂的液壓動做以及機身與大臂鏈接部分的液壓動做都是使用上面的方法來實現,如下爲這兩部分的代碼:
1 rotateBoom: (rotateVal) = >{ 2 excavatorBoomNode.setRotationX(dr * rotateVal); 3 let archorVector = [0.5 - 0.5, 0.56 - 0.5, 0.22 - 0.5]; 4 let pos = projectUtil.toWorldPosition(g3d, excavatorBoomNode, archorVector); 5 boomWhite.lookAtX(boomBlack.p3(), 'bottom'); 6 boomBlack.lookAtX(pos, 'top'); 7 }, 8 rotateArm: (rotateVal) = >{ 9 projectUtil.applyRelativeRotation(excavatorArmNode, excavatorBoomNode, -rotateVal); 10 let archorVector = [0.585 - 0.5, 0.985 - 0.5, 0.17 - 0.5]; 11 let pos = projectUtil.toWorldPosition(g3d, excavatorArmNode, archorVector); 12 armWhite.lookAtX(armBlack.p3(), 'bottom'); 13 armBlack.lookAtX(pos, 'top'); 14 }
我將兩部分的運動封裝爲兩個函數 rotateBoom 以及 rotateArm 分別是大臂與機身鏈接處的液壓運動與大臂上的液壓運動,在該部分中爲了精確的獲取看向的點,我經過 toWorldPosition 方法將相對座標轉化爲世界座標,相對座標爲黑白液壓桿的錨點座標,轉化爲相對大臂或者機身的世界座標。
基本運動分析
挖機的基本運動包括前進後退,機身旋轉,這一部分會相對上面的運動簡單許多,在 HT 的三維座標系中,不斷修改挖機機身的 x,y,z 的座標值就能夠實現挖機的前進後退,經過修改機身的 y 軸旋轉角度則能夠控制機身的旋轉,固然挖機身體上的全部其它零部件須要吸附在機身身上,當機身進行旋轉時其它零部件則會進行相應的旋轉,在進行前進的時候挖機底部的履帶會進行對應的滾動,固然履帶咱們這邊是用了一個履帶的貼圖貼在上面,當挖機前進的時候修改貼圖的偏移值就能夠實現履帶的滾動,修改偏移值的僞代碼以下:
1 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 的時候則執行相應的動做,不然不執行,如下爲對應的部分關鍵代碼:
1 let key_pressed = { 2 65 : { 3 status: false, 4 action: turnLeft 5 }, 6 87 : { 7 status: false, 8 action: goAhead 9 }, 10 68 : { 11 status: false, 12 action: turnRight 13 }, 14 83 : { 15 status: false, 16 action: back 17 }, 18 37 : { 19 status: false, 20 action: bodyTurnLeft 21 }, 22 39 : { 23 status: false, 24 action: bodyTurnRight 25 } 26 }; 27 setInterval(() = >{ 28 for (let key in key_pressed) { 29 let { 30 status, 31 action 32 } = key_pressed[key]; 33 if (status) { 34 action(); 35 } 36 } 37 }, 38 50); 39 document.addEventListener('keydown', (event) = >{ 40 let keyCode = event.keyCode; 41 key_pressed[keyCode] && (key_pressed[keyCode].status = true); 42 event.stopPropagation(); 43 }, 44 true); 45 document.addEventListener('keyup', (event) = >{ 46 let keyCode = event.keyCode; 47 key_pressed[keyCode] && (key_pressed[keyCode].status = false); 48 event.stopPropagation(); 49 }, 50 true);
從上面代碼能夠看出我在 key_pressed 變量中記錄對應按鍵以及按鍵對應的 action 動做,在 keydown 與 keyup 的時候對應修改當前 key 的 status 的狀態值,因此能夠在 Interval 中根據 key_pressed 這個變量的 status 值執行對應的 action 動做,如下爲執行流程圖:
HT 的輕量化,自適應讓當前系統在手機端也能流暢的運行,固然目前移動端與電腦端的 2D 圖紙部分是加載不一樣的圖紙,在移動端的 2D 部分只留下操做挖機的操做部分,其它部分進行了相應的捨棄,否則在移動端小屏幕下沒法展現如此多的數據,在 3D 場景部分都是共用同一個場景,經過場景搭建部分的批量操做使得 3D 在手機端也十分流暢的運行,如下爲手機端運行截圖:
總結
物聯網已經融入了現代生活,經過內嵌到機械設備中的電子設備,咱們可以完成對機械設備的運轉、性能的監控,以及對機械設備出現的問題進行及時的預警。在該系統 2D 面板監控部分就是對採集過來的數據進行可視化的展現,並且咱們能夠藉助大數據和物聯網技術,將一臺臺機械經過機載控制器、傳感器和無線通信模塊,與一個龐大的網絡鏈接,每揮動一鏟、行動一步,都造成數據痕跡。大數據精準描繪出基礎建設開工率等狀況,成爲觀察固定資產投資等經濟變化的風向標。因此在實現上述挖機動做以後,經過與挖機傳感器進行鏈接以後,能夠將挖掘機此時的真實動做經過數據傳遞到系統,系統則會根據動做進行相應的真實操做,真正實現了挖機與網絡的互聯互通。
程序運行截圖: