Python modbus 库
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17081442/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Python modbus library
提问by P3trus
I have to control a modbus device with a serial interface. I've got not experience with modbus. But my short research revealed several modbus libraries
我必须控制带有串行接口的 modbus 设备。我没有使用 modbus 的经验。但我的简短研究揭示了几个 modbus 库
What are the advantages/disadvantages, are there even better alternatives?
有什么优点/缺点,还有更好的选择吗?
采纳答案by Mr. Girgitt
About the same time I faced the same problem - which library to choose for python modbus master implementation but in my case for serial communication (modbus RTU) so my observations are only valid for modbus RTU.
大约在同一时间,我遇到了同样的问题 - 为 python modbus master 实现选择哪个库,但在我的情况下是串行通信(modbus RTU),所以我的观察只对 modbus RTU 有效。
In my examination I didn't pay too much attention to documentation but examples for serial RTU master were easiest to find for modbus-tk however still in source not on a wiki etc.
在我的考试中,我并没有过多关注文档,但是对于 modbus-tk 最容易找到串行 RTU 主站的示例,但是仍然在源代码中,而不是在 wiki 等上。
keeping long story short:
长话短说:
MinimalModbus:
最小Modbus:
- pros:
- lightweight module
- performance may be acceptable for applications reading ~10 registers
- cons:
- unacceptably (for my application) slow when reading ~64 registers
- relatively high CPU load
- 优点:
- 轻量级模块
- 对于读取约 10 个寄存器的应用程序,性能可能是可以接受的
- 缺点:
- 读取 ~64 个寄存器时(对于我的应用程序)速度慢得令人无法接受
- CPU负载相对较高
pymodbus:
pymodbus:
distinctive feature: relies on serial stream (post by the author) and serial timeout must be dynamically set otherwise performance will be low (serial timeout must be adjusted for the longest possible response)
显着特点:依赖串行流(作者发布),串行超时必须动态设置,否则性能会很低(必须调整串行超时以获得尽可能长的响应)
- pros:
- low CPU load
- acceptable performance
- cons:
- even when timeout is dynamically set performance is 2 x lower compared to modbus-tk; if timeout is left at a constant value performance is much worse (but query time is constant)
- sensitive to hardware (as a result of dependency on processing stream from serial buffer I think) or there may be internal problem with transactions: you can get responses mixed-up if different reads or reads/writes are performed ~20 times per second or more. Longer timeouts help but not always making pymodbus RTU implementation over a serial line not enough robust for use in production.
- adding support for dynamic serial port timeout setting requires additional programming: inheriting base sync client class and implementing socket timeout modification methods
- responses validation not as detailed as in modbus-tk. For example in case of a bus decay only exception is thrown whereas modbus-tk returns in the same situation wrong slave address or CRC error which helps identifying root cause of the problem (which may be too short timeout, wrong bus termination / lack thereof or floating ground etc.)
- 优点:
- 低 CPU 负载
- 可接受的表现
- 缺点:
- 即使超时是动态设置的,性能也比 modbus-tk 低 2 倍;如果超时保持恒定值,性能会差很多(但查询时间是恒定的)
- 对硬件敏感(我认为是由于对来自串行缓冲区的处理流的依赖)或者事务可能存在内部问题:如果不同的读取或读取/写入执行约每秒 20 次或更多,您可能会得到混合的响应. 更长的超时有帮助,但并不总是使串行线上的 pymodbus RTU 实现不够健壮,无法用于生产。
- 添加对动态串口超时设置的支持需要额外的编程:继承基本同步客户端类并实现套接字超时修改方法
- 响应验证不像 modbus-tk 中那样详细。例如,在总线衰减的情况下,仅抛出异常,而 modbus-tk 在相同情况下返回错误的从地址或 CRC 错误,这有助于确定问题的根本原因(可能是超时太短、错误的总线终止/缺少或浮地等)
modbus-tk:
modbus-tk:
distinctive feature: probes serial buffer for data, assembles and returns response quickly.
显着特点:探测串行缓冲区数据,快速组装和返回响应。
- pros
- best performance; ~2 x times faster than pymodbus with dynamic timeout
- cons:
- approx. 4 x higher CPU load compared to pymodbus // can be greately improved making this point invalid; see EDIT section at the end
- CPU load increases for larger requests // can be greately improved making this point invalid; see EDIT section at the end
- code not as elegant as pymodbus
- 优点
- 最棒的表演; 比具有动态超时的 pymodbus 快 2 倍
- 缺点:
- 大约 与 pymodbus 相比,CPU 负载高 4 倍 //可以大大改善这一点,使这一点无效;见最后的编辑部分
- 较大请求的 CPU 负载增加 //可以大大改善使这一点无效;见最后的编辑部分
- 代码不如 pymodbus 优雅
For over 6 months I was using pymodbus due to best performance / CPU load ratio but unreliable responses became a serious issue at higher request rates and eventually I moved to faster embedded system and added support for modbus-tk which works best for me.
6 个多月以来,由于最佳性能/CPU 负载率,我一直在使用 pymodbus,但在更高的请求率下,不可靠的响应成为一个严重问题,最终我转向了更快的嵌入式系统,并添加了对 modbus-tk 的支持,这对我来说效果最好。
For those interested in details
对于那些对细节感兴趣的人
My goal was to achieve minimum response time.
我的目标是实现最短的响应时间。
setup:
设置:
- baudrate: 153600
- in sync with 16MHz clock of the microcontroller implementing modbus slave)
- my rs-485 bus has only 50m
- FTDI FT232R converter and also serial over TCP bridge (using com4com as a bridge in RFC2217 mode)
- in case of USB to serial converter lowest timeouts and buffer sizes configured for serial port (to lower latency)
- auto-tx rs-485 adapter (bus has a dominant state)
- 波特率:153600
- 与实现 modbus slave 的微控制器的 16MHz 时钟同步)
- 我的 rs-485 总线只有 50m
- FTDI FT232R 转换器和串行 TCP 桥接器(在 RFC2217 模式下使用 com4com 作为桥接器)
- 在 USB 到串行转换器的情况下,为串行端口配置的最低超时和缓冲区大小(以降低延迟)
- auto-tx rs-485 适配器(总线具有显性状态)
Use case scenario:
用例场景:
- Polling 5, 8 or 10 times a second with support for asynchronous access in between
- Requests for reading/writing 10 to 70 registers
- 每秒轮询 5、8 或 10 次,并支持其间的异步访问
- 请求读/写 10 到 70 个寄存器
Typical long-term (weeks) performance:
典型的长期(数周)表现:
- MinimalModbus: dropped after initial tests
- pymodbus: ~30ms to read 64 registers; effectively up to 30 requests / sec
- but responses unreliable (in case of synchronized access from multiple threads)
- there is possibly a threadsafe fork on github but it's behind the master and I haven't tried it (https://github.com/xvart/pymodbus/network)
- modbus-tk: ~16ms to read 64 registers; effectively up to 70 - 80 requests / sec for smaller requests
- MinimalModbus:初始测试后下降
- pymodbus:~30ms 读取 64 个寄存器;有效高达 30 个请求/秒
- 但响应不可靠(在多线程同步访问的情况下)
- github 上可能有一个线程安全叉,但它在 master 后面,我还没有尝试过(https://github.com/xvart/pymodbus/network)
- modbus-tk:~16ms 读取 64 个寄存器;对于较小的请求,有效高达 70 - 80 个请求/秒
benchmark
基准
code:
代码:
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()
results:
结果:
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
reading 100 x 64 registers:
读取 100 x 64 寄存器:
no power saving
不省电
timeout: 0.05 [s]
Serial<id=0xd57330, open=False>(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<id=0xd57330, open=False>(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<id=0xd57330, open=False>(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]
maximum power saving
最大节能
timeout: 0.05 [s]
Serial<id=0xd57330, open=False>(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<id=0xd57330, open=False>(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<id=0xd57330, open=False>(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]
reading 100 x 10 registers:
读取 100 x 10 寄存器:
no power saving
不省电
timeout: 0.05 [s]
Serial<id=0xd56350, open=False>(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<id=0xd56350, open=False>(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<id=0xd56350, open=False>(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]
maximum power saving
最大节能
timeout: 0.05 [s]
Serial<id=0xd56350, open=False>(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<id=0xd56350, open=False>(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<id=0xd56350, open=False>(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]
real-life application:
实际应用:
Load example for modbus-rpc bridge (~3% is caused by RPC server part)
modbus-rpc 桥的负载示例(~3% 是由 RPC 服务器部分引起的)
5 x 64 registers synchronous reads per second and simultaneous
asynchronous access with serial port timeout set to 0.018 s
modbus-tk
- 10 regs: {'currentCpuUsage': 20.6, 'requestsPerSec': 73.2} // can be improved; see EDIT section below
- 64 regs: {'currentCpuUsage': 31.2, 'requestsPerSec': 41.91} // can be improved; see EDIT section below
pymodbus:
- 10 regs: {'currentCpuUsage': 5.0, 'requestsPerSec': 36.88}
- 64 regs: {'currentCpuUsage': 5.0, 'requestsPerSec': 34.29}
5 x 64 寄存器同步读取每秒和同时
串行端口超时设置为 0.018 秒的异步访问
modbus-tk
- 10 regs: {'currentCpuUsage': 20.6, 'requestsPerSec': 73.2} //可以改进;请参阅下面的编辑部分
- 64 regs: {'currentCpuUsage': 31.2, 'requestsPerSec': 41.91} //可以改进;请参阅下面的编辑部分
pymodbus:
- 10 regs: {'currentCpuUsage': 5.0, 'requestsPerSec': 36.88}
- 64 regs: {'currentCpuUsage': 5.0, 'requestsPerSec': 34.29}
EDIT:the modbus-tk library can be easily improved to reduce the CPU usage. In the original version after request is sent and T3.5 sleep passed master assembles response one byte at a time. Profiling proved most od the time is spent on serial port access. This can be improved by trying to read the expected length of data from the serial buffer. According to pySerial documentationit should be safe (no hang up when response is missing or too short) if timeout is set:
编辑:可以轻松改进 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.
after modifying the `modbus_rtu.py' in the following way:
按以下方式修改“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
After modbus-tk modification the CPU load in the real-life application dropped considerably without significant performance penalty (still better than pymodbus):
在 modbus-tk 修改后,实际应用程序中的 CPU 负载大幅下降,而没有显着的性能损失(仍然比 pymodbus 好):
Updated load example for modbus-rpc bridge (~3% is caused by RPC server part)
更新 modbus-rpc 桥的负载示例(约 3% 是由 RPC 服务器部分引起的)
5 x 64 registers synchronous reads per second and simultaneous
asynchronous access with serial port timeout set to 0.018 s
modbus-tk
- 10 regs: {'currentCpuUsage': 7.8, 'requestsPerSec': 66.81}
- 64 regs: {'currentCpuUsage': 8.1, 'requestsPerSec': 37.61}
pymodbus:
- 10 regs: {'currentCpuUsage': 5.0, 'requestsPerSec': 36.88}
- 64 regs: {'currentCpuUsage': 5.0, 'requestsPerSec': 34.29}
5 x 64 寄存器同步读取每秒和同时
串行端口超时设置为 0.018 秒的异步访问
modbus-tk
- 10 条:{'currentCpuUsage':7.8,'requestsPerSec':66.81}
- 64 regs: {'currentCpuUsage': 8.1, 'requestsPerSec': 37.61}
pymodbus:
- 10 regs: {'currentCpuUsage': 5.0, 'requestsPerSec': 36.88}
- 64 regs: {'currentCpuUsage': 5.0, 'requestsPerSec': 34.29}
回答by Windsplunts
It really depends on what application you're using, and what you're trying to achieve.
这实际上取决于您使用的应用程序以及您要实现的目标。
pymodbus is a very robust library. It works, and it gives you a lot of tools to work with. But it can prove to be a little intimidating when you try to use it. I found it hard to work with personally. It offers you the ability to use both RTU and TCP/IP, which is great!
pymodbus 是一个非常强大的库。它有效,并且为您提供了许多工具。但是当您尝试使用它时,它可能会被证明有点令人生畏。我发现很难与个人合作。它为您提供了同时使用 RTU 和 TCP/IP 的能力,这很棒!
MinimalModbus is a very simple library. I ended up using this for my application because it did exactly what I needed it to do. It only does RTU communications, and it does it well as far as I know. I've never had any trouble with it.
MinimalModbus 是一个非常简单的库。我最终在我的应用程序中使用了它,因为它完全满足了我的需求。它只进行 RTU 通信,据我所知它做得很好。我从来没有遇到过任何麻烦。
I've never looked into Modbus-tk, so I don't know where it stands.
我从来没有研究过 Modbus-tk,所以我不知道它在哪里。
Ultimately though, it does depend on what your application is. In the end I found that python wasn't the best choice for me.
但最终,它确实取决于您的应用程序是什么。最后我发现python不是我的最佳选择。
回答by Travis Griggs
I just discovered uModbus, and for deployment in something like a Raspberry PI (or other small SBC), it's a dream. It's a simple single capable package that doesn't bring in 10+ dependencies like pymodbus does.
我刚刚发现了uModbus,并且对于在 Raspberry PI(或其他小型 SBC)之类的设备中进行部署,这是一个梦想。这是一个简单的单一功能包,不会像 pymodbus 那样引入 10 多个依赖项。

