在 Python 中实时读取串行数据

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19908167/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 14:56:43  来源:igfitidea点击:

Reading serial data in realtime in Python

pythonpython-2.7serial-portpyserial

提问by Vasco Baptista

I am using a script in Python to collect data from a PIC microcontroller via serial port at 2Mbps.

我正在使用 Python 中的脚本通过串行端口以 2Mbps 的速度从 PIC 微控制器收集数据。

The PIC works with perfect timing at 2Mbps, also the FTDI usb-serial port works great at 2Mbps (both verified with oscilloscope)

PIC 以 2Mbps 的完美时序工作,FTDI usb 串行端口也以 2Mbps 的速度工作(均用示波器验证)

Im sending messages (size of about 15 chars) about 100-150x times a second and the number there increments (to check if i have messages being lost and so on)

我发送消息(大约 15 个字符的大小)每秒大约 100-150 次,并且那里的数字会增加(检查我是否有消息丢失等等)

On my laptop I have Xubuntu running as virtual machine, I can read the serial port via Putty and via my script (python 2.7 and pySerial)

在我的笔记本电脑上,我将 Xubuntu 作为虚拟机运行,我可以通过 Putty 和我的脚本(python 2.7 和 pySerial)读取串口

The problem:

问题:

  • When opening the serial port via Putty I see all messages (the counter in the message increments 1 by 1). Perfect!
  • When opening the serial port via pySerial I see all messages but instead of receiving 100-150x per second i receive them at about 5 per second (still the message increments 1 by 1) but they are probably stored in some buffer as when I power off the PIC, i can go to the kitchen and come back and im still receiving messages.
  • 通过 Putty 打开串行端口时,我会看到所有消息(消息中的计数器以 1 递增)。完美的!
  • 当通过 pySerial 打开串行端口时,我看到所有消息,但不是每秒接收 100-150x,而是以每秒 5 个左右的速度接收它们(消息仍以 1 倍递增),但它们可能在我关闭电源时存储在某个缓冲区中PIC,我可以去厨房然后回来,我仍然收到消息。

Here is the code (I omitted most part of the code, but the loop is the same):

这是代码(我省略了大部分代码,但循环是相同的):

ser = serial.Serial('/dev/ttyUSB0', 2000000, timeout=2, xonxoff=False, rtscts=False, dsrdtr=False) #Tried with and without the last 3 parameters, and also at 1Mbps, same happens.
ser.flushInput()
ser.flushOutput()
While True:
  data_raw = ser.readline()
  print(data_raw)

Anyone knows why pySerial takes so much time to read from the serial port till the end of the line? Any help?

任何人都知道为什么 pySerial 需要这么多时间从串行端口读取到行尾?有什么帮助吗?

I want to have this in real time.

我想实时拥有这个。

Thank you

谢谢

采纳答案by Kobi K

You can use inWaiting()to get the amount of bytes available at the input queue.

您可以使用inWaiting()来获取输入队列中可用的字节数。

Then you can use read()to read the bytes, something like that:

然后你可以使用read()读取字节,像这样:

While True:
    bytesToRead = ser.inWaiting()
    ser.read(bytesToRead)

Why not to use readline()at this casefrom Docs:

为什么不在readline()这种情况下使用 Docs:

Read a line which is terminated with end-of-line (eol) character (\n by default) or until timeout.

You are waiting for the timeout at each reading since it waits for eol. the serial input Q remains the same it just a lot of time to get to the "end" of the buffer, To understand it better: you are writing to the input Q like a race car, and reading like an old car :)

您在每次读取时都在等待超时,因为它等待 eol。串行输入 Q 保持不变,只是需要很多时间才能到达缓冲区的“末尾”,为了更好地理解它:您像赛车一样写入输入 Q,像旧车一样读取:)

回答by msw

From the manual:

手册

Possible values for the parameter timeout: … xset timeout to xseconds

参数超时的可能值:... x将超时设置为x

and

readlines(sizehint=None, eol='\n')Read a list of lines, until timeout. sizehint is ignored and only present for API compatibility with built-in File objects.

Note that this function only returns on a timeout.

readlines(sizehint=None, eol='\n')读取行列表,直到超时。sizehint 被忽略,仅存在于 API 与内置 File 对象的兼容性。

请注意,此函数仅在超时时返回。

So your readlineswill return at most every 2 seconds. Use read()as Tim suggested.

所以你readlines最多每 2 秒返回一次。read()按照蒂姆的建议使用。

回答by Fabian Meier

You need to set the timeout to "None" when you open the serial port:

打开串口时需要将超时设置为“无”:

ser = serial.Serial(**bco_port**, timeout=None, baudrate=115000, xonxoff=False, rtscts=False, dsrdtr=False) 

This is a blocking command, so you are waiting until you receive data that has newline (\n or \r\n) at the end: line = ser.readline()

这是一个阻塞命令,因此您要等到收到末尾有换行符(\n 或 \r\n)的数据: line = ser.readline()

Once you have the data, it will return ASAP.

获得数据后,它将尽快返回。

回答by Joe

A very good solution to this can be found here:

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

Here's a class that serves as a wrapper to a pyserial object. It allows you to read lines without 100% CPU. It does not contain any timeout logic. If a timeout occurs, self.s.read(i)returns an empty string and you might want to throw an exception to indicate the timeout.

这是一个用作 pyserial 对象的包装器的类。它允许您在没有 100% CPU 的情况下读取行。它不包含任何超时逻辑。如果发生超时,则self.s.read(i)返回一个空字符串,您可能希望抛出异常以指示超时。

It is also supposed to be fast according to the author:

根据作者的说法,它也应该很快:

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

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

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())