Pymodbus库的通信延迟
我有下面的示例代码。该代码会更新当前正在使用的电机的频率设置,并为其发出一个向前移动的指令。在执行这些操作时,我还额外添加了一个以秒为单位的固定等待时间。
from pymodbus.client import ModbusSerialClient
import time
PORT = "/dev/ttyAMA2"
BAUDRATE = 19200
PARITY = 'N'
STOPBITS = 1
SLAVE_ID = 1
REG_OP_CMD = 8
REG_FREQ_RAM = 13
REG_RESET = 1
def connect():
client = ModbusSerialClient(
port=PORT,
baudrate=BAUDRATE,
parity=PARITY,
stopbits=STOPBITS,
timeout=1
)
if client.connect():
print("Connection successful.")
result = client.read_holding_registers(address=8, count=1, slave=1)
print("Result: ", result)
return client
else:
print("Connection failed!")
return None
def forward(client):
return client.write_register(REG_OP_CMD, 2, slave=SLAVE_ID)
def stop_motor(client):
return client.write_register(REG_OP_CMD, 1, slave=SLAVE_ID)
def set_freq(client, hz):
raw = int(hz * 100)
return client.write_register(REG_FREQ_RAM, raw, slave=SLAVE_ID)
def main():
client = connect()
if not client:
return
try:
print("Frequency set to 20 Hz")
set_freq(client, 20)
time.sleep(2)
print("Moving Forward...")
forward(client)
time.sleep(3)
print("3 seconds passed. Motor stopped.")
stop_motor(client)
time.sleep(2)
print("TEST COMPLETED SUCCESSFULLY...")
finally:
client.close()
print("Port closed.")
if __name__ == "__main__":
main()
当我在系统上持续运行这段代码时,经过一段时间后,发送给电机的停止命令会被延迟。
(这种情况并不是很稳定。举例来说,代码连续正常执行了5次,但在第6次执行时停止命令被延迟。如果它连续正常执行了10次,到了第11次也可能出现停止命令延迟的情况。)
我对这里的两个问题感到好奇:
-
这个在一段时间后命令不生效的问题,是不是由于这段脚本代码的持续重新运行引起的?如果是,我该怎么办?
-
在一个大型项目中,建立连接只需要一次后,电机会被重复提示“go forward and wait 3 seconds”吗?
版本
pymodbus : 3.8.6
解决方案
以下是一个即时替换风格的重构,解决了常见的坑点:
- 显式的RTU解帧器
- 可选的缓冲区重置
- 响应检查+ 时序日志
- 在重复失败时重连
- 调用序列化(即使在单线程中,也更清晰)
import time
import logging
from pymodbus.client import ModbusSerialClient
from pymodbus.framer import FramerType
log = logging.getLogger("vfd")
logging.basicConfig(level=logging.INFO)
PORT = "/dev/ttyAMA2"
BAUDRATE = 19200
PARITY = "N"
STOPBITS = 1
SLAVE_ID = 1
REG_OP_CMD = 8
REG_FREQ_RAM = 13
def make_client():
return ModbusSerialClient(
port=PORT,
framer=FramerType.RTU,
baudrate=BAUDRATE,
parity=PARITY,
stopbits=STOPBITS,
bytesize=8,
timeout=0.3, # keep small if you want fast fail
retries=1, # don’t let a stop block for ages
strict=False, # timing tolerance; see RTU timing docs
)
def safe_reset_buffers(client):
# Best-effort; depends on pymodbus/pyserial internals
ser = getattr(client, "socket", None)
if ser:
try:
ser.reset_input_buffer()
ser.reset_output_buffer()
except Exception:
pass
def write_reg(client, addr, value):
t0 = time.monotonic()
res = client.write_register(addr, value, slave=SLAVE_ID)
dt = (time.monotonic() - t0) * 1000
# Many pymodbus responses support isError()
if hasattr(res, "isError") and res.isError():
raise RuntimeError(f"Modbus error {res} (took {dt:.1f} ms)")
log.info("write %s=%s took %.1f ms", addr, value, dt)
return res
def set_freq(client, hz):
raw = int(hz * 100)
return write_reg(client, REG_FREQ_RAM, raw)
def forward(client):
return write_reg(client, REG_OP_CMD, 2)
def stop_motor(client):
return write_reg(client, REG_OP_CMD, 1)
def run_loop():
client = make_client()
if not client.connect():
raise RuntimeError("Connect failed")
safe_reset_buffers(client)
failures = 0
while True:
try:
set_freq(client, 20)
time.sleep(2)
forward(client)
time.sleep(3)
# Stop is “critical”: do it, and if it fails, retry quickly
stop_motor(client)
failures = 0
time.sleep(2)
except Exception as e:
failures += 1
log.warning("cycle failed (%d): %s", failures, e)
# Try to recover cleanly
try:
client.close()
except Exception:
pass
time.sleep(0.2)
client = make_client()
client.connect()
safe_reset_buffers(client)
# If failures keep happening, you might escalate (alarm / safe-stop)
if __name__ == "__main__":
run_loop()
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。