ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于PZEM-004T与Raspberry Pi的Modbus能源监测系统搭建指南

基于PZEM-004T与Raspberry Pi的Modbus能源监测系统搭建指南 1. 从一颗计量芯片说起PZEM-004T到底能干什么很多人第一次接触电能监测都是从一块几十块钱的PZEM-004T模块开始的。它便宜、好用、接线简单插在插座和负载之间就能读出电压、电流、功率、电量这几个关键参数。但用久了你会发现单块PZEM-004T只是一个传感器它本身不存储、不联网、不告警你只能通过串口临时读一下数据拔了线数据就没了。真正有价值的是把它变成一个持续运行、能远程查看、能记录历史、能触发告警的完整能源监测系统。我最初的需求很朴素家里装了一套光伏板加储能电池想搞清楚每天到底发了多少电、用了多少电、有多少是从电网买的。市面上的成品能源监测器动辄上千而且数据锁在厂商云里导出麻烦。于是决定自己搭一套核心思路就是PZEM-004T负责采集Raspberry Pi负责汇聚和计算Modbus负责本地通信MQTT负责把数据推送到我自己的看板和数据库。这套方案跑了大半年中间踩了不少坑也做了几次架构调整。下面把从单块PZEM-004T到完整能源监测系统的全过程拆开讲包括硬件选型、通信协议、数据管道、告警逻辑和实际部署中的各种细节。如果你也在考虑自己搭一套能源监测或者手头已经有PZEM-004T但不知道怎么把它变成系统这篇内容应该能帮你省下不少试错时间。2. 硬件层PZEM-004T的接线、供电与多路扩展2.1 PZEM-004T的两种版本与选型建议PZEM-004T市面上主要有两个版本V3.0和早期版本。V3.0最大的改进是支持Modbus-RTU协议寄存器地址固定读取稳定而且自带隔离的TTL串口输出。早期版本用的是私有协议寄存器不公开读出来的数据格式也不统一不建议新项目使用。V3.0的TTL接口是4针5V、GND、TX、RX。注意这里的TX/RX是3.3V电平直接接Raspberry Pi的GPIO串口没问题但如果接Arduino Mega这类5V逻辑的板子需要加电平转换。我一开始用Arduino Uno读发现数据偶尔乱码后来换成Raspberry Pi的硬件串口就稳定了。供电方面PZEM-004T需要5V供电可以从Raspberry Pi的5V引脚取电但要注意Pi的5V引脚输出能力有限如果同时挂多块PZEM最好用独立5V电源。我试过用Pi的5V带三块PZEM结果其中一块偶尔掉线换成独立电源后问题消失。2.2 多路监测的接线方案与地址冲突处理单块PZEM只能测一路负载。如果要监测光伏、电池、电网、家庭总负载四个回路就需要四块PZEM。Modbus-RTU支持总线式连接所有PZEM的A/B线并联到同一对RS485总线上每块PZEM设置不同的Modbus从站地址。这里有个关键细节PZEM-004T V3.0的Modbus地址是通过串口命令修改的默认地址是0x01。修改地址需要用厂家提供的上位机软件或者自己发Modbus写寄存器命令。我一开始不知道四块PZEM全是默认地址接上总线后数据全乱。后来用USB转TTL单独连接每一块逐个改成0x01、0x02、0x03、0x04。接线时还要注意RS485总线的终端电阻。短距离几米内可以不接但如果总线超过10米或者通信不稳定需要在总线两端各接一个120欧姆的终端电阻。我的四块PZEM分布在配电箱不同位置总线大概8米没接终端电阻也能稳定跑但如果你遇到CRC校验错误频繁先检查终端电阻。2.3 Raspberry Pi作为Modbus主站的硬件配置Raspberry Pi的硬件串口默认是给系统控制台用的需要先禁用串口控制台启用硬件串口。具体操作是在raspi-config里选择Interfacing Options - Serial关闭Would you like a login shell to be accessible over serial?开启Would you like the serial port hardware to be enabled?。然后修改/boot/config.txt确保有这两行enable_uart1 dtoverlaydisable-btdtoverlaydisable-bt是为了把蓝牙占用的PL011串口释放出来给GPIO使用。如果不加这行GPIO串口会变成mini UART波特率不稳定Modbus通信容易出错。USB转RS485适配器我推荐用CH340芯片的便宜且Linux驱动成熟。FTDI芯片的更稳定但贵一些。我手头两种都有实测在115200波特率下CH340也能稳定跑但如果你要挂超过8个从站建议上FTDI或者隔离型适配器。3. 通信层Modbus-RTU的寄存器映射与轮询策略3.1 PZEM-004T的Modbus寄存器详解PZEM-004T V3.0的Modbus寄存器地址是公开的主要数据都在输入寄存器Input Register功能码0x04里。关键寄存器如下寄存器地址含义数据类型单位换算系数0x0000电压16位无符号0.1V除以100x0001电流低16位16位无符号0.001A除以10000x0002电流高16位16位无符号0.001A除以10000x0003功率低16位16位无符号0.1W除以100x0004功率高16位16位无符号0.1W除以100x0005电量低16位16位无符号1Wh直接读0x0006电量高16位16位无符号1Wh直接读0x0007频率16位无符号0.1Hz除以100x0008功率因数16位无符号0.01除以100注意电流、功率、电量都是32位数据分两个16位寄存器存储低16位在前高16位在后。读取时需要一次性读两个寄存器然后拼接。我一开始只读了低16位结果电流超过65.535A后数据回绕差点以为负载出了问题。3.2 轮询频率与总线负载的平衡Modbus-RTU是主从轮询协议主站依次问每个从站要数据。轮询频率太高会占满总线太低又会导致数据更新不及时。我的四块PZEM每块需要读9个寄存器从0x0000到0x0008一次完整的Modbus事务大概需要10-15ms115200波特率下。如果每块PZEM每秒轮询一次四块就是每秒4次事务总线占用率大概5%完全没问题。但如果挂32个从站比如工业场景每秒轮询一次就会导致总线占用率超过40%这时候需要降低频率或者分组轮询。我的策略是电压、电流、功率这些实时性要求高的参数每2秒轮询一次电量这种累积值每30秒读一次就够了。这样既保证了实时性又降低了总线负载。3.3 用Python实现Modbus主站轮询Raspberry Pi上我用的是pymodbus库配合minimalmodbus做备用。pymodbus功能全但API有点绕minimalmodbus更简单直接。下面是我实际用的轮询代码框架from pymodbus.client import ModbusSerialClient import struct import time client ModbusSerialClient( port/dev/ttyUSB0, baudrate115200, parityN, stopbits1, bytesize8, timeout1 ) def read_pzem(slave_id): try: # 读输入寄存器0x0000到0x0008共9个 result client.read_input_registers( address0x0000, count9, slaveslave_id ) if result.isError(): return None regs result.registers voltage regs[0] / 10.0 current ((regs[2] 16) | regs[1]) / 1000.0 power ((regs[4] 16) | regs[3]) / 10.0 energy ((regs[6] 16) | regs[5]) frequency regs[7] / 10.0 pf regs[8] / 100.0 return { voltage: voltage, current: current, power: power, energy: energy, frequency: frequency, pf: pf } except Exception as e: print(fSlave {slave_id} error: {e}) return None # 轮询四块PZEM slaves [1, 2, 3, 4] while True: for sid in slaves: data read_pzem(sid) if data: print(fSlave {sid}: {data}) time.sleep(2)这段代码跑起来后你会看到每2秒输出一次四块PZEM的数据。但实际部署时不能只打印需要把数据存下来并推送到MQTT。4. 数据层从Modbus到MQTT的管道设计4.1 为什么选MQTT而不是HTTP把数据从Raspberry Pi传到服务器可选方案有HTTP POST、WebSocket、MQTT等。我最终选MQTT原因有三个第一MQTT是发布/订阅模型Raspberry Pi只管发不需要知道谁在消费后续加看板、加数据库、加告警都不需要改采集端第二MQTT有QoS等级网络不稳定时可以保证消息不丢第三MQTT协议开销小适合Raspberry Pi这种资源有限的设备。HTTP POST每次都要建立连接开销大而且如果服务器挂了采集端需要自己实现重试和缓存。MQTT的Broker可以持久化消息采集端断线重连后自动补发。4.2 MQTT主题设计与数据格式主题设计要考虑到后续扩展。我的主题结构是energy/{location}/{device}/data比如energy/home/pv/data energy/home/battery/data energy/home/grid/data energy/home/load/data数据格式用JSON包含时间戳和所有参数{ timestamp: 2025-01-15T10:30:00Z, voltage: 230.5, current: 2.345, power: 540.2, energy: 123456, frequency: 50.0, pf: 0.98 }时间戳用ISO 8601格式方便后续入库和分析。注意PZEM的电量是累积值单位是Wh入库时可以直接存也可以转换成kWh。4.3 Raspberry Pi上的MQTT发布实现用paho-mqtt库发布消息代码很简单import paho.mqtt.client as mqtt import json from datetime import datetime client mqtt.Client() client.connect(your-broker-ip, 1883, 60) def publish_data(device, data): topic fenergy/home/{device}/data payload { timestamp: datetime.utcnow().isoformat() Z, **data } client.publish(topic, json.dumps(payload), qos1)QoS设为1保证消息至少送达一次。如果网络特别不稳定可以设QoS 2但开销会大一些。4.4 数据持久化InfluxDB还是SQLiteMQTT Broker收到数据后需要一个订阅者把数据写入数据库。时序数据库首选InfluxDB它对时间序列数据的压缩和查询优化做得很好。但如果你的数据量不大比如每秒几条SQLite也完全够用而且部署更简单。我用的是InfluxDB 2.x配置一个Telegraf或者自己写一个Python订阅者import paho.mqtt.client as mqtt from influxdb_client import InfluxDBClient, Point from influxdb_client.client.write_api import SYNCHRONOUS influx InfluxDBClient(urlhttp://localhost:8086, tokenyour-token, orgyour-org) write_api influx.write_api(write_optionsSYNCHRONOUS) def on_message(client, userdata, msg): data json.loads(msg.payload) device msg.topic.split(/)[2] point Point(energy) \ .tag(device, device) \ .field(voltage, data[voltage]) \ .field(current, data[current]) \ .field(power, data[power]) \ .field(energy, data[energy]) \ .time(data[timestamp]) write_api.write(bucketenergy, recordpoint) client mqtt.Client() client.on_message on_message client.subscribe(energy/home//data, qos1) client.connect(localhost, 1883, 60) client.loop_forever()这样数据就自动流入InfluxDB后续用Grafana做看板或者用Python做分析都很方便。5. 应用层看板、告警与自动化联动5.1 Grafana看板的关键面板设计Grafana连InfluxDB后可以做出很直观的能源看板。我常用的几个面板实时功率曲线显示光伏、电池、电网、负载四路的功率一眼看出当前是在充电还是放电是从电网买电还是往电网送电。日电量柱状图每天的光伏发电量、家庭用电量、电网买卖电量用柱状图对比。电压频率监控电压和频率的实时曲线异常时能快速发现。功率因数趋势功率因数长期偏低可能意味着有感性负载过多可以考虑加补偿。Grafana的查询语句用Flux比如查最近24小时的光伏功率from(bucket: energy) | range(start: -24h) | filter(fn: (r) r._measurement energy and r.device pv and r._field power) | aggregateWindow(every: 1m, fn: mean)5.2 基于MQTT的告警规则引擎告警逻辑可以放在Raspberry Pi上也可以放在服务器上。我选择放在服务器因为Raspberry Pi资源有限而且服务器上更容易做复杂的规则判断。告警规则我用Node-RED实现它可以直接订阅MQTT主题用可视化流程做判断。比如电压超过250V或低于200V触发告警。功率超过设定阈值比如家里总负载超过5kW触发告警。光伏发电量连续2小时为0可能是逆变器故障触发告警。电池电量低于20%触发告警。告警方式可以用MQTT推送到手机App或者发邮件、发Webhook到即时通讯工具。我用的是MQTT推送到一个自建的通知服务再转发到手机。5.3 自动化联动根据能源数据控制负载能源监测的终极价值是自动化。比如光伏发电功率超过家庭负载时自动开启热水器或给电动车充电。电池电量低于30%且电网电价处于低谷时自动从电网充电。家庭总负载接近上限时自动关闭非关键负载如空调、烘干机。这些逻辑用Node-RED或者Home Assistant都很容易实现。Home Assistant有专门的能源面板可以直接接入MQTT数据做自动化和场景联动。我目前的自动化比较简单光伏发电功率超过2kW且电池电量高于80%时自动开启热水器。这个逻辑用Node-RED实现订阅光伏功率主题判断条件后通过MQTT发送控制命令到智能插座。6. 实际部署中的坑与经验总结6.1 Modbus通信不稳定的排查思路Modbus-RTU通信不稳定是最常见的问题表现是CRC校验错误、超时、数据乱码。排查顺序建议如下检查接线A/B线是否接反屏蔽线是否接地。RS485的A接AB接B不要交叉。检查波特率所有从站和主站必须一致。PZEM默认9600我改成115200后忘了改其中一块结果那块一直读不到。检查从站地址是否有重复地址。用Modbus Poll或者自己写扫描脚本逐个确认。检查终端电阻长距离总线需要120欧姆终端电阻。检查电源PZEM供电不足会导致通信异常用万用表量一下5V是否稳定。我遇到过一次诡异的问题四块PZEM中有一块偶尔读不到换线、换电源都没用。后来发现是那块PZEM的固件版本较老Modbus响应时间比其他三块慢主站超时设得太短100ms改成500ms后问题消失。6.2 Raspberry Pi长期运行的稳定性优化Raspberry Pi作为长期运行的采集网关需要注意几点禁用不必要的服务蓝牙、WiFi如果用以太网、HDMI输出都可以关掉减少资源占用和干扰。使用只读文件系统SD卡频繁写入容易损坏可以把根文件系统设为只读或者用overlayfs。日志写到内存或者外部USB存储。配置看门狗Raspberry Pi有硬件看门狗可以在系统卡死时自动重启。定期重启我设置每周日凌晨3点自动重启一次清理内存碎片和僵尸进程。6.3 数据精度与校准PZEM-004T的精度在同类产品中算不错的但仍有误差。我用手头的Fluke万用表对比过电压误差在0.5%以内电流在小电流时误差较大低于0.1A时可能偏差10%以上。如果对精度要求高可以做软件校准用标准表读一个已知负载的功率然后计算PZEM的偏差系数在代码里做补偿。我的做法是每块PZEM单独校准把校准系数存在配置文件里。电量累积值也要注意PZEM的电量寄存器是32位最大约4294967295Wh也就是429万kWh一般家庭用几十年都不会溢出。但如果频繁断电电量可能会回绕或者清零建议在软件层做累积不要完全依赖PZEM的内部电量。6.4 成本与扩展性权衡整套系统的成本大概如下组件型号单价数量小计电能计量模块PZEM-004T V3.035元4140元主控Raspberry Pi 4B 2GB350元1350元USB转RS485CH34015元115元电源5V 2A20元120元SD卡32GB30元130元外壳与接线-50元150元合计605元如果只是监测一路负载成本可以降到200元以内用Raspberry Pi Zero 2W。如果要监测更多回路每增加一路只需加一块PZEM和相应的接线。扩展性方面Modbus-RTU总线理论上可以挂247个从站但实际上受总线负载和电源限制一般挂32个以内比较稳妥。如果回路特别多可以考虑用Modbus TCP网关把RS485转成以太网这样轮询速度更快也更容易扩展。7. 从监测到优化数据驱动的能源管理7.1 用历史数据发现用电规律系统跑了一个月后InfluxDB里积累了大量数据。用Python做分析可以发现很多有意思的规律家里最大的用电户是空调夏季晚上8点到10点是高峰。光伏发电高峰在中午12点到下午2点但家庭用电高峰在晚上存在时间错配。待机功耗加起来有50W左右一年就是438度电。这些发现直接指导了后续的优化把洗衣机、洗碗机定时到中午光伏发电高峰时运行更换待机功耗高的老旧电器给空调加智能插座做定时控制。7.2 峰谷电价下的策略优化如果当地有峰谷电价能源监测系统可以帮你算清楚哪些负载应该放在谷电时段哪些应该用光伏直供。我的策略是电动车充电设置在谷电时段晚上11点到早上7点。热水器优先用光伏余电光伏不足时在谷电时段补。储能电池谷电时段充电峰电时段放电。这些策略用Node-RED实现根据实时功率、电池电量、时间条件自动切换。7.3 系统长期维护的几点建议最后分享几个长期维护的经验定期备份配置Raspberry Pi的SD卡随时可能坏InfluxDB的数据和Node-RED的流程要定期备份到外部存储或云端。监控系统自身用MQTT发布Raspberry Pi的CPU温度、内存使用率、磁盘空间避免采集端自己挂了还不知道。版本锁定Python库和系统包不要随意升级升级前先在测试环境验证。我有一次升级pymodbus后API变了采集脚本直接跑不起来。文档记录接线图、寄存器地址、主题结构、告警规则都记下来过几个月回头看还能快速上手。这套系统从最初的一块PZEM-004T到现在四路监测加自动化联动前后折腾了大概三个月。中间换过主控、改过通信方案、重写过数据管道但最终跑稳定后每天打开手机就能看到家里的能源流向那种掌控感是成品设备给不了的。如果你也在考虑自己搭一套建议先从一块PZEM加Raspberry Pi开始跑通数据链路后再逐步扩展不要一上来就追求大而全。
返回列表