You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

寻求指导:如何用Python模拟正泰DDSU666电能表(Modbus RTU)

模拟正泰DDSU666电能表(Modbus RTU从站)方案与调试指导

问题背景

我已经能用Python读取正泰DDSU666电能表的数据,该电表通过Modbus RTU和光伏逆变器通信。现在想模拟这个电表,通过自定义寄存器数据来调节逆变器的充放电功率。

读取电表的代码如下:

import minimalmodbus
import struct

# The minimalmodbus library uses RTU mode for Modbus communication.
# You will need to set up the serial port and baud rate to match your energy meter's settings.
instrument1 = minimalmodbus.Instrument('com4',1)
instrument1.serial.baudrate = 9600
instrument1.close_port_after_each_call = True

# Set the slave address of your energy meter
instrument1.address = 0x01


while True:
   try:
    ##Chint Meter DDSU666
        spanning = instrument1.read_float(0x2000, 3, 2)
        freq = instrument1.read_float(0x200e, 3, 2)
        stroom = instrument1.read_float(0x2002, 3, 2)
        vermogen = instrument1.read_float(0x2004, 3, 2)
        baurdrate = instrument1.read_register(0x000c, 0)



        print(spanning)
        print(freq)
        print(stroom)
        print(vermogen)
        print(baurdrate)
   except:
        print('communication lost')

我尝试搭建Modbus RTU从站让逆变器读取自定义寄存器,先用pymodbus3.1没成功,换modbus_tk写了代码但无法正常运行:

import minimalmodbus
import struct
import pymodbus
import asyncio
import modbus_tk
import modbus_tk.defines as cst
from modbus_tk import modbus_rtu
import serial
import time

modbusserver = modbus_rtu.RtuServer(serial.Serial('com4'),baudrate = 9600, bytesize=8,parity = 'N', stopbits=1, xonxoff=0)
print('start')
modbusserver.start()

slave1 = modbusserver.add_slave(1)
slave1.add_block('spanning',cst.HOLDING_REGISTERS,0x2000,250)
slave1.add_block('freq',cst.HOLDING_REGISTERS,0x200e,50)
slave1.add_block('vermogen',cst.HOLDING_REGISTERS,0x2002,3000)

需要指导:实现模拟该电能表的合适方案是什么?如何调试当前代码?


一、合适的实现方案

方案1:基于modbus_tk完善实现

modbus_tk是成熟的Modbus RTU从站实现库,适配你的场景,核心要注意两点:

  • 严格匹配DDSU666的寄存器规格:电压(0x2000)、电流(0x2002)、功率(0x2004)、频率(0x200E)都是32位浮点型,对应Modbus的2个连续保持寄存器(单寄存器为16位)。
  • 确保串口资源独占,且服务器持续运行不退出。

方案2:改用pymodbus同步RTU服务器

如果modbus_tk调试受阻,pymodbus的同步RTU从站是可靠替代选项,它的文档更完善,社区支持更广泛,适合快速搭建稳定的Modbus从站。


二、当前modbus_tk代码的调试与修复

你的代码存在几个关键问题,修复后即可正常运行:

1. 串口资源与参数修正

  • 确认com4未被其他程序(比如之前的电表读取代码)占用,否则会导致串口打开失败。
  • 初始化serial.Serial时添加超时参数,避免无响应阻塞:
    ser = serial.Serial(
        port='com4',
        baudrate=9600,
        bytesize=8,
        parity='N',
        stopbits=1,
        xonxoff=0,
        timeout=1  # 添加超时防止阻塞
    )
    modbusserver = modbus_rtu.RtuServer(ser)
    

2. 寄存器块定义错误

add_block的第四个参数是寄存器数量,不是初始值:

  • 32位浮点寄存器需要占用2个连续的16位寄存器,所以所有浮点型参数的寄存器数量都应设为2,而非你写的250、50等数值。
  • 正确的块定义:
    slave1 = modbusserver.add_slave(1)
    # 电压:起始地址0x2000,2个寄存器(对应32位浮点)
    slave1.add_block('spanning', cst.HOLDING_REGISTERS, 0x2000, 2)
    # 频率:起始地址0x200E,2个寄存器
    slave1.add_block('freq', cst.HOLDING_REGISTERS, 0x200E, 2)
    # 电流:起始地址0x2002,2个寄存器
    slave1.add_block('stroom', cst.HOLDING_REGISTERS, 0x2002, 2)
    # 功率:起始地址0x2004,2个寄存器
    slave1.add_block('vermogen', cst.HOLDING_REGISTERS, 0x2004, 2)
    # 波特率寄存器:0x000C,1个寄存器
    slave1.add_block('baudrate', cst.HOLDING_REGISTERS, 0x000C, 1)
    

3. 设置寄存器初始值

添加块后,需要将浮点值转换为Modbus兼容的16位寄存器值(大端模式,和DDSU666一致),并设置初始值:

import struct

def float_to_registers(value):
    # 大端模式打包32位浮点,拆分为两个16位寄存器
    packed = struct.pack('>f', value)
    reg1 = struct.unpack('>H', packed[:2])[0]
    reg2 = struct.unpack('>H', packed[2:])[0]
    return [reg1, reg2]

# 设置初始模拟值
slave1.set_values('spanning', 0x2000, float_to_registers(220.0))
slave1.set_values('freq', 0x200E, float_to_registers(50.0))
slave1.set_values('stroom', 0x2002, float_to_registers(5.0))
slave1.set_values('vermogen', 0x2004, float_to_registers(1100.0))
slave1.set_values('baudrate', 0x000C, [9600])

4. 保持服务器持续运行

原代码启动服务器后直接退出,需要添加循环维持运行:

try:
    print('Modbus RTU Slave started on com4')
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    print('Stopping server...')
    modbusserver.stop()
    ser.close()

5. 分步调试步骤

  • 串口连通性测试:用串口助手监听com4,手动发送Modbus读取指令(比如读取0x2000寄存器),检查是否有响应。
  • 本地读取测试:关闭逆变器,运行原电表读取代码,验证是否能读取到你设置的初始值,确认从站功能正常。
  • 逆变器连接测试:确保逆变器的Modbus参数(从站地址1、波特率9600、奇偶校验N、停止位1)与从站完全一致,再连接串口测试充放电调节。

三、完整修复后的modbus_tk代码示例

import modbus_tk
import modbus_tk.defines as cst
from modbus_tk import modbus_rtu
import serial
import time
import struct

def float_to_registers(value):
    # 大端模式打包32位浮点,拆分两个16位寄存器
    packed = struct.pack('>f', value)
    reg1 = struct.unpack('>H', packed[:2])[0]
    reg2 = struct.unpack('>H', packed[2:])[0]
    return [reg1, reg2]

# 初始化串口
ser = serial.Serial(
    port='com4',
    baudrate=9600,
    bytesize=8,
    parity='N',
    stopbits=1,
    xonxoff=0,
    timeout=1
)

# 启动Modbus RTU从站
modbusserver = modbus_rtu.RtuServer(ser)
modbusserver.start()

# 添加从站(地址1,和DDSU666一致)
slave1 = modbusserver.add_slave(1)
# 定义寄存器块:地址、寄存器数量
slave1.add_block('spanning', cst.HOLDING_REGISTERS, 0x2000, 2)
slave1.add_block('freq', cst.HOLDING_REGISTERS, 0x200E, 2)
slave1.add_block('stroom', cst.HOLDING_REGISTERS, 0x2002, 2)
slave1.add_block('vermogen', cst.HOLDING_REGISTERS, 0x2004, 2)
slave1.add_block('baudrate', cst.HOLDING_REGISTERS, 0x000C, 1)

# 设置初始值
slave1.set_values('spanning', 0x2000, float_to_registers(220.0))
slave1.set_values('freq', 0x200E, float_to_registers(50.0))
slave1.set_values('stroom', 0x2002, float_to_registers(5.0))
slave1.set_values('vermogen', 0x2004, float_to_registers(1100.0))
slave1.set_values('baudrate', 0x000C, [9600])

print('Modbus RTU Slave running... Press Ctrl+C to stop')

try:
    while True:
        time.sleep(1)
        # 可选:动态修改寄存器值,比如每隔5秒调整功率
        # current_power = struct.unpack('>f', struct.pack('>HH', *slave1.get_values('vermogen', 0x2004, 2)))[0]
        # slave1.set_values('vermogen', 0x2004, float_to_registers(current_power + 100))
except KeyboardInterrupt:
    print('Stopping server...')
    modbusserver.stop()
    ser.close()

内容的提问来源于stack exchange,提问作者Baptist Vermeulen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 19:55:14