2. Modbus TCP 电表数据采集

任务目标

编写一个Python程序,通过网络连接支持Modbus TCP协议的电表,周期性地读取电压(U)、电流(I)、有功功率(P)等关键电气参数,并在控制台以格式化表格输出。本案例聚焦于Modbus TCP通信链路建立与标准功能码读取,不涉及具体电表品牌私有寄存器映射。

环境依赖

需要Python 3.7+环境,并安装开源Modbus库pymodbus(版本2.5.3或更新):

pip install pymodbus

关键完整代码

from pymodbus.client import ModbusTcpClient
import time
import struct

# 电表Modbus寄存器地址(以常见电表为例,可按实际设备修改)
REG_ADDRS = {
    'U': 0x0000,   # 电压寄存器起始地址(float32,2个寄存器)
    'I': 0x0002,   # 电流寄存器起始地址
    'P': 0x0004,   # 有功功率寄存器起始地址
}

def read_float32(client, addr, unit=1):
    """读取2个连续寄存器并解析为32位浮点数(大端序)"""
    result = client.read_holding_registers(address=addr, count=2, slave=unit)
    if result.isError():
        raise RuntimeError(f"读取寄存器0x{addr:04X}失败: {result}")
    # Modbus寄存器为大端序,需按大端字节序组合为float
    raw = struct.pack('>HH', result.registers[0], result.registers[1])
    return struct.unpack('>f', raw)[0]

def main():
    # 电表IP与端口(按实际设备修改)
    HOST = '192.168.1.100'
    PORT = 502
    UNIT_ID = 1       # Modbus从站地址
    INTERVAL = 2.0    # 采集间隔(秒)

    client = ModbusTcpClient(HOST, port=PORT, timeout=3)
    if not client.connect():
        print(f"无法连接到 {HOST}:{PORT}")
        return

    print(f"已连接电表 {HOST}:{PORT},从站地址 {UNIT_ID}")
    print("开始采集数据(Ctrl+C退出)...\n")
    print(f"{'时间':<20} {'电压(V)':<12} {'电流(A)':<12} {'功率(W)':<12}")

    try:
        while True:
            try:
                u = read_float32(client, REG_ADDRS['U'], UNIT_ID)
                i = read_float32(client, REG_ADDRS['I'], UNIT_ID)
                p = read_float32(client, REG_ADDRS['P'], UNIT_ID)
                ts = time.strftime('%Y-%m-%d %H:%M:%S')
                print(f"{ts:<20} {u:<12.2f} {i:<12.3f} {p:<12.2f}")
            except Exception as e:
                print(f"[{time.strftime('%H:%M:%S')}] 读取异常: {e}")
                # 尝试重连
                if not client.connected:
                    print("连接断开,尝试重连...")
                    client.connect()
            time.sleep(INTERVAL)
    except KeyboardInterrupt:
        print("\n采集停止")
    finally:
        client.close()

if __name__ == '__main__':
    main()

运行与验证

运行方式:

python modbus_meter.py

验证方法:

  1. 将代码中HOST、PORT、UNIT_ID修改为实际电表参数;
  2. 确认电表已启用Modbus TCP服务,且本机网络可访问电表IP;
  3. 运行程序后,若连接成功,控制台每2秒输出一行包含电压、电流、功率的数据;
  4. 判定通过条件:能连续稳定输出非零且数值合理的电气参数(如电压在200~240V范围内),且无持续报错。

常见问题

  • 连接失败:检查IP、端口(默认502)是否正确,电表是否允许Modbus TCP访问,以及防火墙是否放行。
  • 读取异常(从站无响应):确认UNIT_ID与电表配置的从站地址一致;部分电表使用从站地址0或255,需按设备手册调整。
  • 数值明显错误:寄存器地址或数据格式不匹配。本代码按大端序float32解析,若电表使用int32或小端序,需修改read_float32中的字节序(将>改为<)或改用read_float32的word_order参数。
Modbus TCP 电表数据采集流程 上位机 (Python) Modbus TCP Client 以太网 / TCP/IP IP: 192.168.1.100:502 Modbus 电表 从站地址: 1 请求: 读保持寄存器 功能码 0x03 响应: 寄存器值 字节序: 大端 Float32 寄存器地址映射表 参数名称 起始地址 寄存器数量 数据类型 电压 (U) 0x0000 2 Float32 (大端) 电流 (I) 0x0002 2 Float32 (大端) 有功功率 (P) 0x0004 2 Float32 (大端) 注: 实际寄存器地址需参考具体电表型号的Modbus协议文档