Pymodbus库的通信延迟

编程语言 2026-07-12

我有下面的示例代码。该代码会更新当前正在使用的电机的频率设置,并为其发出一个向前移动的指令。在执行这些操作时,我还额外添加了一个以秒为单位的固定等待时间。

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次也可能出现停止命令延迟的情况。)

我对这里的两个问题感到好奇:

  1. 这个在一段时间后命令不生效的问题,是不是由于这段脚本代码的持续重新运行引起的?如果是,我该怎么办?

  2. 在一个大型项目中,建立连接只需要一次后,电机会被重复提示“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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章