4. 数据建模与物模型设计

在 ThingWorx 中,物模型(Thing Template / Thing Shape)是数字孪生体的骨架。它决定了平台如何理解一台真实设备:哪些量需要采集、每个量是什么类型、用什么单位表达、是否要落库、变化时是否触发事件。如果物模型设计得粗糙,后续的服务、混搭、分析都会被迫在应用层反复做类型转换和单位换算,维护成本会迅速放大。本节以一台典型的工业空压机为例,完整走一遍从真实设备到物模型定义的建模过程。

一、从真实设备出发梳理属性清单

建模的第一步不是打开 ThingWorx,而是回到设备本身。假设现场有一台螺杆式空压机,其铭牌与本地控制器可提供以下可读量:排气压力、排气温度、运行电流、累计运行小时、加载/卸载状态、急停报警。这些量在物理上性质不同,不能一律当作“数值”处理。我们需要先区分三类属性:

  • 测量量:连续变化的模拟量,如排气压力、排气温度、运行电流,适合用 NUMBER 并绑定单位。
  • 计数量:单调递增的累计值,如累计运行小时,适合用 NUMBER 但持久化策略不同。
  • 状态量:离散枚举,如加载/卸载、急停报警,适合用 STRING 或 BOOLEAN,并配合状态映射。

这一步的产出是一张属性清单,而不是直接建 Thing。清单中每个属性都要写清来源(PLC 寄存器、Modbus 地址、OPC UA 节点)、采样周期和物理含义。例如排气压力来自 Modbus 保持寄存器 40001,采样周期 1 秒;累计运行小时来自 40010,采样周期 60 秒。采样周期直接决定了后续持久化策略的选择。

二、数据类型、单位与量程约束

ThingWorx 的基元类型包括 NUMBER、STRING、BOOLEAN、DATETIME、LOCATION、JSON 等。选择类型时遵循“最小且够用”原则:能用 BOOLEAN 表达的状态不要用 STRING,能用 NUMBER 表达的测量量不要用 STRING。对于测量量,建议在属性上附加单位与量程元数据,常见做法是使用属性描述或自定义标签记录,例如:

属性名: dischargePressure
baseType: NUMBER
单位: bar
量程: 0 ~ 16
采样周期: 1000 ms
死区: 0.05 bar

单位必须在物模型层统一,避免同一台设备在不同页面出现 bar 与 MPa 混用。量程用于后续做越限判断和混搭量程条。死区(deadband)是容易被忽略但很关键的参数:当压力在 0.05 bar 内波动时不产生新值,可以显著降低存储和事件压力。对于排气温度,量程设为 0~120 ℃,死区 0.2 ℃;运行电流量程 0~200 A,死区 0.5 A。这些示例参数需要根据实际设备手册和现场噪声水平调整,不能照搬。

三、持久化策略:什么该存、存多久、怎么存

ThingWorx 通过 Value Stream(值流)实现属性历史持久化。并非所有属性都值得落库。持久化策略要回答三个问题:是否持久化、以什么频率持久化、保留多久。常用策略如下:

属性是否持久化持久化方式保留周期
排气压力是变化时写入(带死区)90 天
排气温度是变化时写入(带死区)90 天
运行电流是定时 5 秒写入30 天
累计运行小时是定时 60 秒写入长期
加载/卸载状态是变化时写入30 天
急停报警是变化时写入长期

“变化时写入”依赖死区过滤,适合压力、温度这类稳态波动量;“定时写入”适合电流这类需要连续趋势但变化频繁的量。累计运行小时属于计数量,即使值不变也应定期写入,否则历史曲线会出现长时间断点。急停报警属于安全相关事件,必须长期保留,且不应被死区过滤掉。

四、数据变更事件与订阅

属性值变化后,ThingWorx 会触发 DataChange 事件。事件是物模型与业务逻辑之间的桥梁。设计时要明确:哪些属性变化需要触发事件、事件里携带什么、由谁订阅。例如排气压力超过 12 bar 触发高压预警,急停报警变为 true 触发停机事件。事件订阅方可以是 Thing 自身的服务、混搭中的绑定,也可以是外部系统通过 REST 或 WebSocket 订阅。

需要特别注意的是事件风暴问题。如果对一个高频变化的属性不加死区地订阅 DataChange,服务会被频繁调用。合理的做法是:在属性层设置死区,在事件层再做一次条件判断,只有越过阈值才执行重逻辑。下面这张图展示了从真实设备到物模型、再到持久化与事件订阅的完整数据链路,以及各环节的关键参数。

空压机物模型数据链路:属性 → 类型/单位 → 持久化 → 变更事件 真实设备 螺杆式空压机 排气压力 40001 排气温度 40002 运行电流 40003 累计运行 40010 加载/卸载 40020 急停报警 40021 采集 物模型属性定义 dischargePressure : NUMBER 单位 bar · 量程 0~16 · 死区 0.05 dischargeTemp : NUMBER 单位 ℃ · 量程 0~120 · 死区 0.2 runCurrent : NUMBER 单位 A · 量程 0~200 · 死区 0.5 loadState : STRING / estop : BOOLEAN Value Stream 持久化策略 压力/温度:变化写入(死区过滤)· 90 天 电流:定时 5 秒写入 · 30 天 累计运行:定时 60 秒写入 · 长期 状态/急停:变化写入 · 长期保留 DataChange 数据变更事件与订阅 条件判断 压力 > 12 bar → 高压预警 安全事件 estop = true → 停机事件 订阅方 Thing 服务 · 混搭绑定 · REST / WebSocket 外部系统 下游消费 · 历史趋势曲线 · 实时监控混搭 · 预警与工单 · 能效分析 · 外部 MES / 云平台 建模要点 类型最小化 单位统一 死区过滤 按需持久化 事件防风暴

五、建模成果的检查与迭代

物模型建好后,不要急于接入所有业务逻辑。建议先用一个最小验证闭环检查:手动写入一个压力值 12.5 bar,观察是否触发高压预警事件;写入 11.8 bar,确认不触发;再写入 12.6 bar,确认再次触发。同时检查 Value Stream 中是否按预期产生了历史记录,时间戳间隔是否符合死区策略。如果发现事件被重复触发,优先检查死区是否生效、事件条件是否写在了正确的层级。

物模型不是一次成型的。随着设备接入数量增加,建议把共性属性抽到 Thing Shape 中复用,例如“电机类设备”Shape 包含电流、温度、运行状态,具体 Thing 只保留设备特有属性。这样后续新增同类设备时,只需继承 Shape 并绑定数据源即可。
常见错误:把累计运行小时当作普通测量量使用“变化时写入”,导致长时间无数据点;或者对急停报警设置死区,造成安全事件被过滤。安全相关属性应始终持久化且不设死区。

总结来看,基于真实设备定义物模型的核心在于:先梳理物理量性质,再确定类型与单位,然后按数据特征选择持久化策略,最后用事件把变化传递给业务层。这四步的顺序不能颠倒,因为后一步的决策依赖前一步的结论。一个清晰的物模型,能让后续的服务编排、混搭开发和数据分析都建立在稳定的数据契约之上。