2. Modbus TCP 电表数据采集

任务目标

通过以太网使用 Modbus TCP 协议连接智能电表,读取电压(寄存器地址 0x0000)、电流(0x0001)、有功功率(0x0002)三个关键电量参数,并验证读取数据的正确性。本案例使用 Modbus 从站模拟器作为电表替代设备,确保无硬件条件下可完整复现。

环境依赖

  • Python 3.8+
  • 安装依赖:pip install pymodbus==3.6.9
  • Modbus 从站模拟器(如 Modbus Slave、diagslave 或 pymodbus 自带的模拟器),用于模拟电表寄存器数据

完整实现代码

from pymodbus.client import ModbusTcpClient
import time

# 电表 Modbus 寄存器地址映射(以某型号电表为例)
REG_ADDRS = {
    "voltage": 0x0000,      # 电压 (V),放大10倍存储
    "current": 0x0001,      # 电流 (A),放大100倍存储
    "power":   0x0002       # 有功功率 (W),放大10倍存储
}

# 各参数的数据缩放系数(根据电表说明书设定)
SCALE_FACTORS = {
    "voltage": 10.0,
    "current": 100.0,
    "power":   10.0
}

def read_meter_data(host: str, port: int = 502, unit_id: int = 1, timeout: int = 3):
    """
    从 Modbus TCP 电表读取电压、电流、功率数据。
    返回: dict,包含三个参数的原始寄存器值和物理值。
    """
    client = ModbusTcpClient(host, port=port, timeout=timeout)
    if not client.connect():
        raise ConnectionError(f"无法连接 Modbus TCP 设备 {host}:{port}")

    result = {}
    try:
        for param, addr in REG_ADDRS.items():
            # 读取保持寄存器(功能码 0x03),每个参数占1个寄存器
            rr = client.read_holding_registers(address=addr, count=1, slave=unit_id)
            if rr.isError():
                raise RuntimeError(f"读取 {param} 失败: {rr}")

            raw_value = rr.registers[0]
            physical_value = raw_value / SCALE_FACTORS[param]
            result[param] = {
                "raw": raw_value,
                "value": round(physical_value, 2)
            }
    finally:
        client.close()

    return result

if __name__ == "__main__":
    # 从站模拟器地址(根据实际环境修改)
    METER_HOST = "127.0.0.1"
    METER_PORT = 5020   # 模拟器监听端口

    # 连续读取3次,间隔1秒,验证数据稳定性
    for i in range(3):
        try:
            data = read_meter_data(METER_HOST, METER_PORT)
            print(f"第 {i+1} 次读取:")
            print(f"  电压: {data['voltage']['value']} V (原始值: {data['voltage']['raw']})")
            print(f"  电流: {data['current']['value']} A (原始值: {data['current']['raw']})")
            print(f"  功率: {data['power']['value']} W (原始值: {data['power']['raw']})")
        except Exception as e:
            print(f"读取失败: {e}")
        time.sleep(1)

运行与验证

  1. 启动从站模拟器:在本地启动 Modbus TCP 从站模拟器,监听端口 5020,从站地址(Unit ID)设为 1。在模拟器中预设以下寄存器值:
    地址 0x0000 = 2200(表示 220.0V)
    地址 0x0001 = 500(表示 5.00A)
    地址 0x0002 = 11000(表示 1100.0W)
  2. 运行脚本:python modbus_meter.py
  3. 判定通过条件:程序连续 3 次成功读取数据,且输出值与模拟器预设值一致——电压显示 220.0V、电流显示 5.0A、功率显示 1100.0W。若读取失败或数值偏差超过 0.1,则检查网络连接、寄存器地址或缩放系数配置。

常见问题

  • 连接超时:确认模拟器已启动且端口号、IP 地址正确;检查防火墙是否放行 TCP 端口。
  • 读取错误(Illegal Data Address):模拟器中未在对应地址设置寄存器值,需在从站配置中添加地址 0x0000~0x0002 的保持寄存器。
  • 数值偏差:不同型号电表的寄存器缩放系数可能不同,请以实际电表说明书为准修改 SCALE_FACTORS。