如何提高PySerial读取速度

2024-06-01 08:12:14 发布

您现在位置:Python中文网/ 问答频道 /正文

我目前正在建造一台使用Arduino Mega2560作为主控制器的机器。Arduino连接到over-serial,获取一个命令,执行它,每1毫秒吐出一堆测量数据。我有一个运行Python的Raspberry Pi,为用户提供一个良好的图形用户界面来发送命令,并以可读的形式显示数据。

我面临的问题是: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,然后读取该文件,但这并不是一个很好的解决方案,不是吗?

非常感谢:)


Tags: 数据代码devtruetimeasciiserialsleep
3条回答

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

所以,我想问题的答案是,PySerial确实没有得到很好的优化。

一个很好的解决方案是here

作者说:

The code below gives me 790 kB/sec while replacing the code with pyserial's readline method gives me just 170kB/sec.

此解决方案还避免了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())

我意识到这是一个老线索,但它已经被视为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

相关问题 更多 >