【问题标题】:Reading serial data in realtime in Python在 Python 中实时读取串行数据
【发布时间】:2013-11-23 08:41:00
【问题描述】:

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

PIC 以 2Mbps 的完美时序工作,FTDI USB 串行端口在 2Mbps 下也能正常工作(均已通过示波器验证)

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

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

问题:

  • 通过 Putty 打开串行端口时,我看到所有消息(消息中的计数器以 1 递增 1)。完美!
  • 通过 pySerial 打开串行端口时,我看到所有消息,但不是每秒接收 100-150 倍,而是每秒大约 5 次接收它们(消息仍以 1 递增 1),但它们可能存储在某个缓冲区中我把PIC关掉了,我可以去厨房回来,我仍然收到消息。

这里是代码(我省略了大部分代码,但循环是一样的):

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)

有谁知道为什么 pySerial 从串口读取到行尾要花这么多时间? 有什么帮助吗?

我想实时拥有它。

谢谢

【问题讨论】:

  • 你能试试用ser.read()代替ser.readline()吗?
  • 我使用 ser.read(),在未收到 \n 时读取,在收到字符串时逐个字符地添加,速度仍然相同。
  • 不要等到收到\n - 只需在每个字符到达时打印出来。您是一次突然收到大量角色,还是每个角色自己出现?
  • 因为我在做 ser.read() 然后 print() 我每行得到一个字符
  • @VascoBaptista 使用公认的解决方案(删除超时并使用inWaiting()),Python 仍然有可能必须等待接收所有字节才能打印出整个消息(多个字节)。这在逻辑上会导致延迟。您的系统是否仍然实时您能确认一下吗?

标签: python python-2.7 serial-port pyserial


【解决方案1】:

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

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

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

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

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

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

【讨论】:

  • 它成功了,现在我将尝试分离所有字符串并为每个字符串运行我的代码。我会尽快提供反馈
  • 是的,我明白,但由于我的消息类似于:213531\n 616516\n 516861\n 我认为它会读得很快......无论如何我的超时时间是 2 秒,并且消息是比这更快地到达我的脚本(在 readline 之后打印在屏幕上)。这意味着它永远不会达到超时。即使添加 \r\n 也没有解决任何问题。设置 timeout=0 并没有改变任何东西,同样的速度,同样的一切(除了当我关闭 PIC 时它被锁定)
  • @Vasco Baptista 还有一件事,print() 受您使用的终端的影响...我会给您一个参考 post 尝试阅读它会有所帮助。
  • 感谢@Kobi K 的提示,我知道这一点,但为了足够签入,因为我没有在最终脚本中打印消息。另外脚本现在正在工作,我添加了一些额外的代码来分隔我收到的消息并将它们放入队列中以在其他线程中处理。感谢大家的帮助
  • 那么对于串行数据,它是否会排队等待从缓冲区中读取?例如,如果您执行了 serial.write(b'100\n') serial.write('b'200\n') serial.write(b'300\n')。然后一个 serial.readline() 你会得到 >100,如果你继续做 readline 它会得到下一个?
【解决方案2】:

来自the manual

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

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

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

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

【讨论】:

  • 我在使用 readlines 时遇到了问题。所以我尝试了 Kobi K 解决方案。它有效,现在我只需要解析接收到的数据并分离字符串
【解决方案3】:

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

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

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

一旦你有数据,它会尽快返回。

【讨论】:

    【解决方案4】:

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

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

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

    下面的代码给了我 790 kB/sec 的速度,同时用 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())
    

    【讨论】:

      猜你喜欢
      • 2022-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多