前言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 面板监控部分就是对采集过来的数据进行可视化的展现,并且咱们能够借助大数据和物联网技术,将一台台机械经过机载控制器、传感器和无线通信模块,与一个庞大的网络链接,每挥动一铲、行动一步,都造成数据痕迹。大数据精准描绘出基础建设开工率等状况,成为观察固定资产投资等经济变化的风向标。因此在实现上述挖机动做以后,经过与挖机传感器进行链接以后,能够将挖掘机此时的真实动做经过数据传递到系统,系统则会根据动做进行相应的真实操做,真正实现了挖机与网络的互联互通。
程序运行截图: