为什么需要 OPC UA

工厂里设备五花八门:西门子 S7 用 S7 协议、三菱 MC、Modbus TCP、Profinet、EtherNet/IP。每接一种设备你都要写一套驱动,做一个客户现场要写半年。OPC UA 干的事就是把这些差异封在底下,给上面一个统一的对象模型:你只要会读"温度"这个 NodeId,不管底下是哪台 PLC。

OPC UA 不是一个简单的协议,它是"工业语义模型"。除了传数据,它还告诉你这数据是什么意思、单位是什么、属于哪台设备。
OPC UA 配图:中央数据采集服务器,周围连接工厂设备与云图标
OPC UA 把多厂商的 PLC 抽象成统一的地址空间

这篇不讲理论,讲怎么把一条 S7-1200 的产线数据真正采上来、建模型、订阅、再转发到云平台。

典型采集架构

一条产线的标准采集链路:PLC / 仪表 → 现场总线 → 边缘网关(跑 OPC UA Server)→ 上位机或云(OPC UA Client)。网关那一层把各种私有协议翻译成 OPC UA,对外只暴露一个标准端口。

常见 OPC UA 实现
  • open62541开源 C 栈,适合嵌入式网关。
  • node-opcuaNode.js 写网关很顺手。
  • Python asyncua原型脚本、快速调试用。
  • Kepware / Matrikon商业 Server,驱动全。

如果你只是做 Client(去连别人的 Server),用 Python asyncua 几十行就能跑。

OPC UA 地址空间建模

OPC Server 对外暴露一棵 Node 树:Root → Objects → 设备 → 产线 → 工位 → 具体变量。每个 Node 有 NodeId(比如 ns=2;s=Line1.Press.Temperature)、DisplayName、Value、DataType、EngineeringUnits。

# open62541 风格伪代码,建一个温度变量
node = {
    NodeId: "ns=2;s=Line1.Press.Temperature",
    BrowseName: "Temperature",
    DisplayName: "压力机1# 油温",
    DataType: UA_Float,
    Value: 45.2,
    EngUnits: "°C"
}
server.add_node(node)

建模的关键是按"设备 → 工位 → 测点"分层,不要平铺几百个 NodeId 写在顶层。这样上位机一打开就能顺着树看现场。

从 PLC 读到数据

网关内部:一边用 S7 驱动读 PLC 寄存器,一边把值写到 OPC UA Node。下面是 Python asyncua Client 读一个 S7 标签的简化流程:

from asyncua import Client
import asyncio

async def main():
    async with Client("opc.tcp://192.168.1.10:4840") as c:
        node = c.get_node("ns=2;s=Line1.Press.Temperature")
        while True:
            val = await node.read_value()
            print(f"温度 = {val:.1f} °C")
            await asyncio.sleep(1)

asyncio.run(main())

实际项目里网关同时是 S7 Client(读 PLC)+ OPC UA Server(对外提供数据)。协议转换都在网关内部完成。