Мне нужно управлять устройством Modbus с последовательным интерфейсом. У меня нет опыта работы с Modbus. Но мое небольшое исследование выявило несколько библиотек Modbus.
Какие преимущества / недостатки, есть ли еще лучшие альтернативы?
Мне нужно управлять устройством Modbus с последовательным интерфейсом. У меня нет опыта работы с Modbus. Но мое небольшое исследование выявило несколько библиотек Modbus.
Какие преимущества / недостатки, есть ли еще лучшие альтернативы?
Примерно в то же время я столкнулся с той же проблемой - какую библиотеку выбрать для реализации ведущего устройства Modbus в Python, но в моем случае для последовательной связи (Modbus RTU), поэтому мои наблюдения действительны только для Modbus RTU.
В моем экзамене я не уделял слишком много внимания документации, но примеры для последовательного ведущего устройства RTU было проще всего найти для modbus-tk, но все еще в исходниках, а не на вики и т. Д.
отличительная особенность: полагается на последовательный поток (сообщение автора), а тайм-аут последовательного интерфейса должен быть установлен динамически, иначе производительность будет низкой (тайм-аут последовательного интерфейса должен быть отрегулирован для максимально длительного ответа)
Отличительная особенность: проверяет последовательный буфер данных, собирает и быстро возвращает ответ.
Более 6 месяцев я использовал pymodbus из-за лучшего соотношения производительности и загрузки процессора, но ненадежные ответы стали серьезной проблемой при более высоких частотах запросов, и в конечном итоге я перешел на более быструю встроенную систему и добавил поддержку modbus-tk, которая лучше всего подходит для меня.
Моей целью было добиться минимального времени отклика.
код:
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 х 64 регистров:
нет энергосбережения
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]
максимальная экономия энергии
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]
чтение 100 х 10 регистров:
нет энергосбережения
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]
максимальная экономия энергии
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]
Пример загрузки для моста modbus-rpc (~ 3% вызвано серверной частью RPC)
5 x 64 регистра синхронных чтений в секунду и одновременных
асинхронный доступ с тайм-аутом последовательного порта, установленным на 0,018 с
Modbus-tk
pymodbus:
РЕДАКТИРОВАТЬ: библиотеку modbus-tk можно легко улучшить, чтобы снизить нагрузку на ЦП. В исходной версии после отправки запроса и перехода в спящий режим 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 загрузка процессора в реальном приложении значительно упала без значительного снижения производительности (все еще лучше, чем pymodbus):
Обновлен пример нагрузки для моста modbus-rpc (~ 3% вызвано серверной частью RPC)
5 x 64 регистра синхронных чтений в секунду и одновременных
асинхронный доступ с тайм-аутом последовательного порта, установленным на 0,018 с
Modbus-tk
pymodbus:
tkmc.execute(slaveId, tkCst.READ_HOLDING_REGISTERS, 0,regsSp)
, как читать конкретный регистр временного хранения или пару регистров? А как распечатать ответ?
- person mrid; 21.05.2018
Я только что открыл для себя uModbus, и для развертывания в чем-то вроде Raspberry PI (или другого небольшого SBC) это мечтать. Это простой единый пакет, который не содержит 10+ зависимостей, как это делает pymodbus.
Это действительно зависит от того, какое приложение вы используете и чего пытаетесь достичь.
pymodbus - очень надежная библиотека. Он работает и дает вам множество инструментов для работы. Но это может показаться немного устрашающим, когда вы пытаетесь его использовать. Мне было трудно работать лично. Он предлагает вам возможность использовать как RTU, так и TCP / IP, и это здорово!
MinimalModbus - очень простая библиотека. В итоге я использовал это для своего приложения, потому что оно делало именно то, что мне нужно. Он осуществляет связь только через RTU, и, насколько мне известно, делает это хорошо. У меня никогда не было с этим проблем.
Я никогда не заглядывал в Modbus-tk, поэтому не знаю, где он находится.
Однако в конечном итоге это зависит от вашего приложения. В конце концов я обнаружил, что Python - не лучший выбор для меня.