2. Modbus TCP 电表数据采集
任务目标
通过以太网使用 Modbus TCP 协议读取智能电表的电压、电流、有功功率等关键电气参数。本案例以 Python 的 pymodbus 库实现最小可复现的客户端,连接支持 Modbus TCP 的电表模拟器或真实设备,完成数据读取与解析。
环境与依赖
- Python 3.8+
- 安装依赖:
pip install pymodbus==3.6.9 - 测试目标:可使用
pymodbus自带的模拟服务器,或任何支持 Modbus TCP 的电表设备(默认端口 502)
实现代码
以下代码实现 Modbus TCP 客户端,读取保持寄存器中的电压(地址 0x0000,单位 0.1V)、电流(地址 0x0002,单位 0.001A)、有功功率(地址 0x0004,单位 0.1W),并解析为实际工程值:
from pymodbus.client import ModbusTcpClient
# 电表寄存器映射(保持寄存器)
REG_VOLTAGE = 0x0000 # 电压,单位 0.1V
REG_CURRENT = 0x0002 # 电流,单位 0.001A
REG_POWER = 0x0004 # 有功功率,单位 0.1W
REG_COUNT = 6 # 一次读取6个寄存器(覆盖3个参数)
def read_meter(host: str, port: int = 502, unit_id: int = 1) -> dict:
"""读取电表电压/电流/功率,返回工程值字典"""
client = ModbusTcpClient(host, port=port, timeout=3)
if not client.connect():
raise ConnectionError(f"无法连接 {host}:{port}")
try:
# 读取保持寄存器(功能码 0x03)
resp = client.read_holding_registers(REG_VOLTAGE, REG_COUNT, slave=unit_id)
if resp.isError():
raise RuntimeError(f"Modbus 读取错误: {resp}")
regs = resp.registers
voltage = regs[0] * 0.1 # 0.1V → V
current = regs[2] * 0.001 # 0.001A → A
power = regs[4] * 0.1 # 0.1W → W
return {
"voltage_v": round(voltage, 2),
"current_a": round(current, 3),
"power_w": round(power, 2),
}
finally:
client.close()
if __name__ == "__main__":
# 替换为实际电表 IP;本地测试可用 pymodbus 模拟服务器
data = read_meter("192.168.1.100")
print(f"电压: {data['voltage_v']} V")
print(f"电流: {data['current_a']} A")
print(f"功率: {data['power_w']} W")
运行与验证
- 启动模拟服务器(无真实设备时):
python -m pymodbus.server --host 0.0.0.0 --port 5020
将代码中端口改为5020,IP 改为127.0.0.1。 - 运行客户端:
python modbus_meter.py - 判定通过条件:
程序无异常退出,并打印三行参数。模拟服务器默认寄存器值为 0,因此预期输出为电压: 0.0 V、电流: 0.0 A、功率: 0.0 W。若连接真实电表,数值应与电表显示一致(允许±1%误差)。
常见问题
- 连接超时:确认设备 IP/端口正确,且防火墙放行 TCP 502(或自定义端口)。
- 寄存器地址不匹配:不同厂商电表寄存器映射可能不同,需查阅设备手册调整
REG_*常量与缩放系数。 - 字节序问题:部分电表使用大端字节序,若读数异常可尝试
client.read_holding_registers(..., byteorder=1)或交换寄存器高低字节。