繁体   English   中英

如何提高 PySerial 读取速度

[英]How can I improve PySerial read speed

我目前正在构建一台使用 Arduino Mega2560 作为其主控制器的机器。 Arduino 通过串行连接,获取命令,执行它并每 1 毫秒吐出一堆测量数据。 我有一个运行 Python 的 Raspberry Pi,它为用户提供了一个很好的 GUI 来发送命令,并以可读的形式呈现数据。

我面临的问题:Arduino 能够每毫秒吐出 15 个字节的数据(所以只有 15kbyte/s),但是我正在运行的代码每 10 毫秒只能处理大约 15 个字节,所以 1.5kB/s .

当我运行cat /dev/ttyACM0 > somefile ,我很好地看到了所有数据点。

我有以下精简的 Python 代码

# Reset Arduino by starting serial
microprocBusy = True
serialPort = serial.Serial("/dev/ttyACM0", baudrate=460800, timeout=0)
time.sleep(0.22);
serialPort.setDTR(False);
time.sleep(0.22);
serialPort.setDTR(True);
time.sleep(0.10);

logfile = open(logfilenamePrefix + "_" + datetime.datetime.now().isoformat() + '.txt', 'a')

# Bootloader has some timeout, we need to wait for that
serialPort.flushInput()
while(serialPort.inWaiting() == 0):
    time.sleep(0.05)

# Wait for welcome message
time.sleep(0.1)
logfile.write(serialPort.readline().decode('ascii'))
logfile.flush()

# Send command
serialPort.write((command + '\n').encode('ascii'))

# Now, receive data
while(True):
    incomingData = serialPort.readline().decode('ascii')
    logfile.write(incomingData)
    logfile.flush() 

    if(incomingData[:5] == "FATAL" or incomingData[:6] == "HALTED" or incomingData[:5] == "RESET"):
        break;
    elif(incomingData[:6] == "RESULT"):
            resultData = incomingData;

logfile.flush() 

当我运行它时,第一个 ~350 个数据点进来,然后我看到一些损坏的数据并错过了大约 2000 个数据点,之后我看到另外 350 个左右的数据点。 进程中CPU使用率为100%

出了什么问题? PySerial 是否优化不当,或者我遗漏的代码中是否存在一些错误? 我可以从 Python 运行cat /dev/ttyACM0 > somefile然后读取该文件,但这不是一个很好的解决方案,是吗?

非常感谢 :)

可以在这里找到一个很好的解决方案。

作者指出:

下面的代码给了我 790 kB/秒,而用 pyserial 的 readline 方法替换代码给我只有 170kB/秒。

没有关于为此比较设置的波特率的说明。 以下示例中的 9600 波特值仅用于测试。

此解决方案还避免了 100% 的 CPU 使用率。

class ReadLine:
    def __init__(self, s):
        self.buf = bytearray()
        self.s = s
    
    def readline(self):
        i = self.buf.find(b"\n")
        if i >= 0:
            r = self.buf[:i+1]
            self.buf = self.buf[i+1:]
            return r
        while True:
            i = max(1, min(2048, self.s.in_waiting))
            data = self.s.read(i)
            i = data.find(b"\n")
            if i >= 0:
                r = self.buf + data[:i+1]
                self.buf[0:] = data[i+1:]
                return r
            else:
                self.buf.extend(data)

ser = serial.Serial('COM7', 9600)
rl = ReadLine(ser)

while True:

    print(rl.readline())

我已经从 PySerial 切换到PyTTY ,这解决了我的问题。 只需将其插入此代码(进行一些小的更改,例如将serialPort.inWaiting() == 0替换为serialPort.peek() == b'' )使我的代码能够处理数据流并且不会超过 50% CPU 使用率,这意味着它的速度至少是原来的 10 倍。 不过,我仍在使用 PySerial 来设置 DTR 线。

所以,我想这个问题的答案是确实 PySerial 确实优化得很差。

我意识到这是一个旧线程,但在撰写本文时已被查看 3000 次,我讨厌有人在这次遭遇中关闭 pySerial。

我相信作者问题最有可能的罪魁祸首是读取之间正在进行的隐式解析:

incomingData = serialPort.readline().decode('ascii')

readline()方法告诉 pyserial 解析到下一行。 您还在接收周期的中间执行decode() 所有这一切都发生在您的信息流中间。

更好的方法可能如下所示:

waiting = port.in_waiting  # find num of bytes currently waiting in hardware
buffer += [chr(c) for c in port.read(waiting)] # read them, convert to ascii

# ...keep accumulating the buffer for as long as is reasonable...

processSerialData(buffer)  # whatever processing needs to happen, split your
                           # lines, log, or whatever else *after* you get
                           # your data

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM