pymodbus数据_Python Modbus库

大约在同一时间,我遇到了同样的问题-为python modbus master实现选择哪个库,但对于串行通信(modbus RTU),我的观察仅对modbus RTU有效。

在我的考试中,我并没有过多地关注文档,但是最容易找到modbus-tk的串行RTU主站示例,但是仍然在wiki等上未找到源代码。

长话短说:

最小Modbus:

优点:轻量级模块

对于读取约10个寄存器的应用程序,性能可能是可以接受的

缺点:(对于我的应用程序而言)读取〜64个寄存器的速度太慢(对于我的应用程序而言)

相对较高的CPU负载

pymodbus:

独特功能:依赖串行流(由作者发布),并且必须动态设置串行超时,否则性能会很差(必须调整串行超时以获得尽可能长的响应)

优点:低CPU负载

可接受的表现

缺点:即使超时是动态设置的,其性能也比modbus-tk低2倍; 如果将超时保留为恒定值,性能会更差(但查询时间是恒定的)

对硬件敏感(由于我认为是依赖于串行缓冲区的处理流的结果)或事务可能存在内部问题:如果每秒执行20次或更多次不同的读取或读取/写入操作,则可能会混淆响应 。 较长的超时时间有助于但并非总是使串行线路上的pymodbus RTU实施不够坚固,无法用于生产环境。

添加对动态串行端口超时设置的支持需要其他编程:继承基本同步客户端类并实现套接字超时修改方法

响应验证不如modbus-tk中所述。 例如,在总线衰减的情况下,仅引发异常,而在相同情况下,modbus-tk返回错误的从站地址或CRC错误,这有助于识别问题的根本原因(可能是超时时间太短,错误的总线终止/缺少总线错误或 浮动地面等)

modbus-tk:

独特的功能:探测串行缓冲区中的数据,快速组装并返回响应。

优点最棒的表演; 动态超时比pymodbus快约2倍

缺点:大约 与pymodbus相比,CPU负载高出4倍//使这一点无效; 参见最后的“编辑”部分

大型请求的CPU负载增加//可以大大改善,从而使这一点无效; 参见最后的“编辑”部分

代码不如pymodbus优雅

超过6个月的时间,我一直在使用pymodbus,因为它具有最佳的性能/ CPU负载比,但是在更高的请求率下,不可靠的响应成为一个严重的问题,最终我转向了更快的嵌入式系统,并添加了对我最合适的modbus-tk的支持。

对于那些对细节感兴趣的人

我的目标是达到最短的响应时间。

建立:

波特率:153600与实现Modbus从站的微控制器的16MHz时钟同步)

我的rs-485总线只有50m

FTDI FT232R转换器以及TCP桥接上的串行(在RFC2217模式下使用com4com作为桥接)

如果是USB到串行转换器的最低超时和为串行端口配置的缓冲区大小(以降低延迟)

auto-tx rs-485适配器(总线处于显性状态)

用例场景:

每秒轮询5、8或10次,并支持之间的异步访问

读取/写入10到70个寄存器的请求

典型的长期(星期)表现:

MinimalModbus:在初始测试后删除

pymodbus:〜30ms读取64个寄存器; 有效地高达30个请求/秒但是响应不可靠(如果来自多个线程的同步访问)

github上可能有一个线程安全的fork,但是它在master后面,我还没有尝试过([https://github.com/xvart/pymodbus/network)]

modbus-tk:〜16ms读取64个寄存器; 对于较小的请求,有效地达到每秒70-80个请求

基准

码:

import time

import traceback

import serial

import modbus_tk.defines as tkCst

import modbus_tk.modbus_rtu as tkRtu

import minimalmodbus as mmRtu

from pymodbus.client.sync import ModbusSerialClient as pyRtu

slavesArr = [2]

iterSp = 100

regsSp = 10

portNbr = 21

portName = 'com22'

baudrate = 153600

timeoutSp=0.018 + regsSp*0

print "timeout: %s [s]" % timeoutSp

mmc=mmRtu.Instrument(portName, 2) # port name, slave address

mmc.serial.baudrate=baudrate

mmc.serial.timeout=timeoutSp

tb = None

errCnt = 0

startTs = time.time()

for i in range(iterSp):

for slaveId in slavesArr:

mmc.address = slaveId

try:

mmc.read_registers(0,regsSp)

except:

tb = traceback.format_exc()

errCnt += 1

stopTs = time.time()

timeDiff = stopTs - startTs

mmc.serial.close()

print mmc.serial

print "mimalmodbus:\ttime to read %s x %s (x %s regs): %.3f [s] / %.3f [s/req]" % (len(slavesArr),iterSp, regsSp, timeDiff, timeDiff/iterSp)

if errCnt >0:

print " !mimalmodbus:\terrCnt: %s; last tb: %s" % (errCnt, tb)

pymc = pyRtu(method='rtu', port=portNbr, baudrate=baudrate, timeout=timeoutSp)

errCnt = 0

startTs = time.time()

for i in range(iterSp):

for slaveId in slavesArr:

try:

pymc.read_holding_registers(0,regsSp,unit=slaveId)

except:

errCnt += 1

tb = traceback.format_exc()

stopTs = time.time()

timeDiff = stopTs - startTs

print "pymodbus:\ttime to read %s x %s (x %s regs): %.3f [s] / %.3f [s/req]" % (len(slavesArr),iterSp, regsSp, timeDiff, timeDiff/iterSp)

if errCnt >0:

print " !pymodbus:\terrCnt: %s; last tb: %s" % (errCnt, tb)

pymc.close()

tkmc = tkRtu.RtuMaster(serial.Serial(port=portNbr, baudrate=baudrate))

tkmc.set_timeout(timeoutSp)

errCnt = 0

startTs = time.time()

for i in range(iterSp):

for slaveId in slavesArr:

try:

tkmc.execute(slaveId, tkCst.READ_HOLDING_REGISTERS, 0,regsSp)

except:

errCnt += 1

tb = traceback.format_exc()

stopTs = time.time()

timeDiff = stopTs - startTs

print "modbus-tk:\ttime to read %s x %s (x %s regs): %.3f [s] / %.3f [s/req]" % (len(slavesArr),iterSp, regsSp, timeDiff, timeDiff/iterSp)

if errCnt >0:

print " !modbus-tk:\terrCnt: %s; last tb: %s" % (errCnt, tb)

tkmc.close()

结果:

platform:

P8700 @2.53GHz

WinXP sp3 32bit

Python 2.7.1

FTDI FT232R series 1220-0

FTDI driver 2.08.26 (watch out for possible issues with 2.08.30 version on Windows)

pymodbus version 1.2.0

MinimalModbus version 0.4

modbus-tk version 0.4.2

读取100 x 64寄存器:

没有节电

timeout: 0.05 [s]

Serial(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.05, xonxoff=False, rtscts=False, dsrdtr=False)

mimalmodbus: time to read 1 x 100 (x 64 regs): 9.135 [s] / 0.091 [s/req]

pymodbus: time to read 1 x 100 (x 64 regs): 6.151 [s] / 0.062 [s/req]

modbus-tk: time to read 1 x 100 (x 64 regs): 2.280 [s] / 0.023 [s/req]

timeout: 0.03 [s]

Serial(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.03, xonxoff=False, rtscts=False, dsrdtr=False)

mimalmodbus: time to read 1 x 100 (x 64 regs): 7.292 [s] / 0.073 [s/req]

pymodbus: time to read 1 x 100 (x 64 regs): 3.170 [s] / 0.032 [s/req]

modbus-tk: time to read 1 x 100 (x 64 regs): 2.342 [s] / 0.023 [s/req]

timeout: 0.018 [s]

Serial(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.018, xonxoff=False, rtscts=False, dsrdtr=False)

mimalmodbus: time to read 1 x 100 (x 64 regs): 4.481 - 7.198 [s] / 0.045 - 0.072 [s/req]

pymodbus: time to read 1 x 100 (x 64 regs): 3.045 [s] / 0.030 [s/req]

modbus-tk: time to read 1 x 100 (x 64 regs): 2.342 [s] / 0.023 [s/req]

最大程度的省电

timeout: 0.05 [s]

Serial(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.05, xonxoff=False, rtscts=False, dsrdtr=False)

mimalmodbus: time to read 1 x 100 (x 64 regs): 10.289 [s] / 0.103 [s/req]

pymodbus: time to read 1 x 100 (x 64 regs): 6.074 [s] / 0.061 [s/req]

modbus-tk: time to read 1 x 100 (x 64 regs): 2.358 [s] / 0.024 [s/req]

timeout: 0.03 [s]

Serial(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.03, xonxoff=False, rtscts=False, dsrdtr=False)

mimalmodbus: time to read 1 x 100 (x 64 regs): 8.166 [s] / 0.082 [s/req]

pymodbus: time to read 1 x 100 (x 64 regs): 4.138 [s] / 0.041 [s/req]

modbus-tk: time to read 1 x 100 (x 64 regs): 2.327 [s] / 0.023 [s/req]

timeout: 0.018 [s]

Serial(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.018, xonxoff=False, rtscts=False, dsrdtr=False)

mimalmodbus: time to read 1 x 100 (x 64 regs): 7.776 [s] / 0.078 [s/req]

pymodbus: time to read 1 x 100 (x 64 regs): 3.169 [s] / 0.032 [s/req]

modbus-tk: time to read 1 x 100 (x 64 regs): 2.342 [s] / 0.023 [s/req]

读取100 x 10寄存器:

没有节电

timeout: 0.05 [s]

Serial(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.05, xonxoff=False, rtscts=False, dsrdtr=False)

mimalmodbus: time to read 1 x 100 (x 10 regs): 6.246 [s] / 0.062 [s/req]

pymodbus: time to read 1 x 100 (x 10 regs): 6.199 [s] / 0.062 [s/req]

modbus-tk: time to read 1 x 100 (x 10 regs): 1.577 [s] / 0.016 [s/req]

timeout: 0.03 [s]

Serial(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.03, xonxoff=False, rtscts=False, dsrdtr=False)

mimalmodbus: time to read 1 x 100 (x 10 regs): 3.088 [s] / 0.031 [s/req]

pymodbus: time to read 1 x 100 (x 10 regs): 3.143 [s] / 0.031 [s/req]

modbus-tk: time to read 1 x 100 (x 10 regs): 1.533 [s] / 0.015 [s/req]

timeout: 0.018 [s]

Serial(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.018, xonxoff=False, rtscts=False, dsrdtr=False)

mimalmodbus: time to read 1 x 100 (x 10 regs): 3.066 [s] / 0.031 [s/req]

pymodbus: time to read 1 x 100 (x 10 regs): 3.006 [s] / 0.030 [s/req]

modbus-tk: time to read 1 x 100 (x 10 regs): 1.533 [s] / 0.015 [s/req]

最大程度的省电

timeout: 0.05 [s]

Serial(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.05, xonxoff=False, rtscts=False, dsrdtr=False)

mimalmodbus: time to read 1 x 100 (x 10 regs): 6.386 [s] / 0.064 [s/req]

pymodbus: time to read 1 x 100 (x 10 regs): 5.934 [s] / 0.059 [s/req]

modbus-tk: time to read 1 x 100 (x 10 regs): 1.499 [s] / 0.015 [s/req]

timeout: 0.03 [s]

Serial(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.03, xonxoff=False, rtscts=False, dsrdtr=False)

mimalmodbus: time to read 1 x 100 (x 10 regs): 3.139 [s] / 0.031 [s/req]

pymodbus: time to read 1 x 100 (x 10 regs): 3.170 [s] / 0.032 [s/req]

modbus-tk: time to read 1 x 100 (x 10 regs): 1.562 [s] / 0.016 [s/req]

timeout: 0.018 [s]

Serial(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.018, xonxoff=False, rtscts=False, dsrdtr=False)

mimalmodbus: time to read 1 x 100 (x 10 regs): 3.123 [s] / 0.031 [s/req]

pymodbus: time to read 1 x 100 (x 10 regs): 3.060 [s] / 0.031 [s/req]

modbus-tk: time to read 1 x 100 (x 10 regs): 1.561 [s] / 0.016 [s/req]

实际应用:

modbus-rpc网桥的加载示例(约3%由RPC服务器部分引起)

5 x 64寄存器每秒同步同步读取

串行端口超时设置为0.018 s的异步访问

modbus-tk

10条:{'currentCpuUsage':20.6,'requestsPerSec':73.2} //可以改进; 请参阅下面的“编辑”部分

64 regs:{'currentCpuUsage':31.2,'requestsPerSec':41.91} //可以改进; 请参阅下面的“编辑”部分

pymodbus:

10条:{'currentCpuUsage':5.0,'requestsPerSec':36.88}

64条:{'currentCpuUsage':5.0,'requestsPerSec':34.29}

编辑:modbus-tk库可以轻松改进以减少CPU使用率。在原始版本中,发送请求并经过T3.5睡眠传递后,主控器一次汇编一个字节的响应。 分析证明,大多数时间都花在串行端口访问上。 通过尝试从串行缓冲区读取期望的数据长度,可以改善此问题。 根据pySerial文档,如果设置了超时,它应该是安全的(在缺少响应或时间太短时不会挂断):

read(size=1)

Parameters: size – Number of bytes to read.

Returns: Bytes read from the port.

Read size bytes from the serial port. If a timeout is set it may return less characters as

requested. With no timeout it will block until the requested number of bytes is read.

通过以下方式修改`modbus_rtu.py'之后:

def _recv(self, expected_length=-1):

"""Receive the response from the slave"""

response = ""

read_bytes = "dummy"

iterCnt = 0

while read_bytes:

if iterCnt == 0:

read_bytes = self._serial.read(expected_length) # reduces CPU load for longer frames; serial port timeout is used anyway

else:

read_bytes = self._serial.read(1)

response += read_bytes

if len(response) >= expected_length >= 0:

#if the expected number of byte is received consider that the response is done

#improve performance by avoiding end-of-response detection by timeout

break

iterCnt += 1

修改modbus-tk之后,实际应用程序中的CPU负载显着下降,而性能没有明显下降(仍然比pymodbus好):

更新了modbus-rpc网桥的加载示例(约3%由RPC服务器部分引起)

5 x 64寄存器每秒同步同步读取

串行端口超时设置为0.018 s的异步访问

modbus-tk

10条:{'currentCpuUsage':7.8,'requestsPerSec':66.81}

64条:{'currentCpuUsage':8.1,'requestsPerSec':37.61}

pymodbus:

10条:{'currentCpuUsage':5.0,'requestsPerSec':36.88}

64条:{'currentCpuUsage':5.0,'requestsPerSec':34.29}

你可能感兴趣的:(pymodbus数据)