前言

现代工业化的推进在极大加速现代化进程的同时也带来的相应的安全隐患,在传统的可视化监控领域,一般都是基于 Web SCADA 的前端技术来实现 2D 可视化监控,本系统采用 Hightopo 的 HT for Web 产品来构造轻量化的 3D 可视化场景,该 3D 场景从正面展示了一个现代化工厂的现实场景,室内定位包括工厂工人的实时位置、电子围栏的范围、现场的安全情况等等,帮助我们直观的了解当前工厂人员的安全状况。

本篇文章通过对工厂可视化场景的搭建和模型的加载,人物实时室内定位代码的实现、电子围栏和轨迹图的实现进行阐述,帮助我们了解如何通过使用HT实现一个简单的 3D 室内定位及电子围栏可视化。

以下是项目地址:

轨迹图效果图

轨迹图

代码实现

人物模型及场景

项目中使用的人物模型是通过 3dMax 建模生成的,该建模工具可以导出 obj 与 mtl 文件,在 HT 中可以通过解析 obj 与 mtl 文件来生成 3d 场景中的摄像头模型。

项目中场景通过 HT 的 3d 编辑器进行搭建,场景中的模型有些是通过 HT 建模,有些通过 3dMax 建模,之后导入 HT 中。

绘制电子围栏

场景中的电子围栏并不是使用3dMax搭建的模型,HT提供了多种基础形体类型供用户建模使用,不同于传统的3D建模方式,HT的建模核心都是基于API的接口方式, 通过预定义的图元类型和参数接口,进行设置达到三维模型的构建。根据形状,我将电子围栏分成圆柱、长方体和底部为多边形的棱柱。

以下是我绘制电子围栏的相关伪代码:

复制代码
复制代码
  1 G.makeShapes = function (data, typeName, color, lastColor, g3dDm) {  2     //data是包含电子围栏图形信息的json对象数组  3     let shapes = data;  4     for (let i = 0; i < shapes.length; i++) {  5         let shape = shapes[i];  6         let type = Number(shape['type']);  7         let x = Number(shape['x']);  8         let y = Number(shape['y']);  9         let z = Number(shape['z']); 10         let width = Number(shape['width']); 11         let height = Number(shape['height']); 12         let tall = Number(shape['tall']); 13         let radius = Number(shape['radius']); 14         let vertexX = shape['vertexX']; 15         let vertexY = shape['vertexY']; 16         let nodePoints = []; 17         let p3 = []; 18         let s3 = []; 19         let centerX = 0; 20         let centerY = 0; 21         let centerZ = 0; 22         let node = new ht.Node(); 23         node.setTag(typeName + i); 24         switch (type) { 25             //第一种形状:圆柱 26             case 1: 27                 p3 = [-x, tall / 2, -y]; 28                 s3 = [radius, tall, radius]; 29                 //定义电子围栏样式 30                 node.s({ 31                     "shape3d": "cylinder", 32                     "shape3d.color": color, 33                     "shape3d.transparent": true, 34                     "shape3d.reverse.color": color, 35                     "shape3d.top.color": color, 36                     "shape3d.top.visible": false, 37                     "shape3d.bottom.color": color, 38                     "shape3d.from.color": color, 39                     "shape3d.to.color": color 40                 }); 41                 node.p3(p3);    //设置三维坐标 42                 node.s3(s3);    //设置形状信息 43                 break; 44             //第二种形状:长方体 45             case 2: 46                 centerX = x - width / 2; 47                 centerY = y - height / 2; 48                 centerZ = z + tall / 2; 49                 p3 = [-Number(centerX) - width, Number(centerZ), -Number(centerY) - height]; 50                 s3 = [width, tall, height]; 51                 node.s({ 52                     "all.color": color, 53                     "all.reverse.color": color, 54                     "top.visible": false, 55                     "all.transparent": true 56                 }); 57                 node.p3(p3); 58                 node.s3(s3); 59                 break; 60             //第三种形状:底部为不规则形状的等高体 61             case 3: 62                 let segments = []; 63                 for (let i = 0; i < vertexX.length; i++) { 64                     let x = -vertexX[i]; 65                     let y = -vertexY[i]; 66                     let newPoint = { x: x, y: y }; 67                     nodePoints.push(newPoint); 68                     //1: moveTo,占用1个点信息,代表一个新路径的起点 69                     if (i === 0) { 70                         segments.push(1); 71                     } 72                     else { 73                         //2: lineTo,占用1个点信息,代表从上次最后点连接到该点 74                         segments.push(2); 75                         if (i === vertexX.length - 1) { 76                             //5: closePath,不占用点信息,代表本次路径绘制结束,并闭合到路径的起始点 77                             segments.push(5); 78                         } 79                     } 80                 } 81                 node = new ht.Shape(); 82                 node.setTag(typeName + i); 83                 node.s({ 84                     'shape.background': lastColor, 85                     'shape.border.width': 10, 86                     'shape.border.color': lastColor, 87                     'all.color': lastColor, 88                     "all.transparent": true, 89                     'all.opacity': 0.3, 90                 }); 91                 p3 = [nodePoints[0]['x'], tall / 2, nodePoints[0]['y']]; 92                 node.p3(p3); 93                 node.setTall(tall); 94                 node.setThickness(5); 95                 node.setPoints(nodePoints); //node设置点集位置信息 96                 node.setSegments(segments); //node设置点集连接规则 97                 break; 98         } 99         g3dDm.add(node);100     }101 }