4. Modbus协议详解与电表接入实战

Modbus 是 SCADA 能源管理系统中使用最广泛的现场总线协议之一,它简单、开放、易于实现,几乎所有电表、PLC、RTU 都支持。本节从帧结构入手,逐步展开到功能码、寄存器映射、轮询策略,最后用工具实际读取一台电表的数据,形成完整的接入闭环。

一、Modbus RTU 与 TCP 的帧结构差异

Modbus 有两种常见承载方式:串行链路上的 RTU 模式和以太网上的 TCP 模式。两者应用层协议数据单元(PDU)完全一致,区别在于外层封装。

RTU 帧由地址、功能码、数据、CRC 校验组成,帧与帧之间靠至少 3.5 个字符时间的静默间隔分隔。例如读取从站地址 1 的保持寄存器 0x0000 起 2 个寄存器,请求帧为:

01 03 00 00 00 02 C4 0B
│  │  │     │     └── CRC 低字节
│  │  │     └──────── 寄存器数量 = 2
│  │  └────────────── 起始地址 = 0x0000
│  └───────────────── 功能码 03(读保持寄存器)
└──────────────────── 从站地址 01

TCP 帧在 PDU 前增加 7 字节 MBAP 报文头:事务标识、协议标识、长度、单元标识。同样请求在 TCP 下为:

00 01 00 00 00 06 01 03 00 00 00 02
│     │     │     │  │  │     │
│     │     │     │  │  │     └── 数量 2
│     │     │     │  │  └──────── 起始地址
│     │     │     │  └─────────── 功能码 03
│     │     │     └────────────── 单元标识(从站地址)
│     │     └──────────────────── 后续字节数 = 6
│     └────────────────────────── 协议标识 0
└──────────────────────────────── 事务标识

RTU 依赖 CRC 保证完整性,TCP 则由以太网和 TCP 层保证,因此 MBAP 中不再有校验字段。理解这一点,就能在抓包时快速区分两种报文。

Modbus RTU 与 TCP 帧结构对比 RTU 帧(串行链路) 地址 功能码 数据(地址+数量/值) CRC ← 3.5 字符静默 → TCP 帧(以太网) 事务ID 协议ID 长度 单元ID 功能码 数据 无 CRC 共同点:应用层 PDU 相同(功能码 + 数据),从站地址/单元标识含义一致。 差异点:RTU 靠 CRC 校验 + 静默间隔分帧;TCP 靠 MBAP 头 + TCP 流保证完整性。 抓包判断:看到 7 字节 MBAP 头即为 TCP;看到末尾 2 字节 CRC 即为 RTU。

二、常用功能码与寄存器映射

电表接入最常用的功能码有四个:01 读线圈、02 读离散输入、03 读保持寄存器、04 读输入寄存器。电表测量值(电压、电流、功率、电能)通常放在保持寄存器或输入寄存器中,因此 03 和 04 使用频率最高。

寄存器映射是接入的核心。以一台典型三相多功能电表为例,其 Modbus 寄存器表可能如下(示例参数,实际以厂家手册为准):

寄存器地址参数数据类型单位换算系数
0x0000A 相电压UINT16V×0.1
0x0001B 相电压UINT16V×0.1
0x0002C 相电压UINT16V×0.1
0x000A总有功功率INT32kW×0.001
0x0010正向有功电能UINT32kWh×0.01

注意两点:一是寄存器地址与协议地址的偏移,有些手册标 40001 对应 0x0000,有些标 1 对应 0x0000,需以手册说明为准;二是32 位数据占用两个连续寄存器,存在高低字顺序问题,常见为高字在前(Big-Endian),也有低字在前的情况,读回后需按手册拼接。

三、轮询策略

SCADA 采集多台电表时,轮询策略直接影响实时性和总线负载。核心原则是:合并连续寄存器、控制单次读取长度、合理分配轮询周期。

  • 合并读取:若电压、电流、功率寄存器连续,用一次 03 请求读取整段,而不是逐点读取。例如从 0x0000 读 16 个寄存器,一次拿回全部电气量。
  • 限制长度:单次读取一般不超过 125 个寄存器(Modbus 规范上限),实际建议不超过 60 个,避免响应超时。
  • 分级周期:电压、电流等实时量用 1 s 周期;电能累计量变化慢,用 10~30 s 周期即可,减少总线压力。
  • 超时与重试:RTU 下响应超时通常设 300~1000 ms,重试 2~3 次;连续失败应标记该从站离线,避免阻塞其他设备。

四、用工具读取电表数据

实际接入前,先用工具验证通信,再写采集程序。常用工具有 Modbus Poll(Windows)、mbpoll(Linux 命令行)、QModMaster 等。以 mbpoll 读取 RTU 电表为例:

mbpoll -m rtu -b 9600 -p none -d 8 -s 1 \
       -a 1 -t 4 -r 1 -c 3 /dev/ttyUSB0

# -m rtu   RTU 模式
# -b 9600  波特率 9600
# -p none  无校验
# -d 8     8 位数据位
# -s 1     1 位停止位
# -a 1     从站地址 1
# -t 4     读输入寄存器(03 为保持寄存器)
# -r 1     起始地址 1
# -c 3     读 3 个寄存器

若返回类似 [1]: 2201 2198 2205,按 ×0.1 换算即 A/B/C 相电压约 220.1 V、219.8 V、220.5 V,说明通信与映射均正确。若返回超时或异常码,先检查接线 A/B 是否反接、波特率与校验位是否一致、从站地址是否正确。

排查顺序建议:物理层(接线、终端电阻)→ 串口参数(波特率、校验、数据位、停止位)→ 从站地址 → 寄存器地址与功能码 → 数据换算。逐层排除,比盲目改参数高效得多。

完成工具验证后,即可将相同的参数迁移到 SCADA 驱动的设备配置中:填写 IP/串口、从站地址、功能码、起始地址、寄存器数量、数据类型与换算系数,再设置轮询周期和超时重试,电表数据就能稳定进入能源管理平台。