Python串口读取极慢,如何加速? [英] Python serial read is extremely slow, how to speed up?

查看:830
本文介绍了Python串口读取极慢,如何加速?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用以下代码与数字万用表 (DMM) 通信.
它工作正常.我可以发送命令并读取结果.我不使用 readline,因为我读取的是二进制数据.

I'm using the following code to communicate with a Digital Multi Meter (DMM).
It works ok. I can send commands and read results. I don't use readline because I read binary data.

问题:

问题在于它非常非常慢.用 Ruby 编写的相同代码要快得多.在 python 中需要 30 秒时,在 ruby​​ 中需要 2 或 3 秒(使用相同的速度).所以这不是硬件问题.
Ruby 代码和 Python 之间的唯一区别是,在 Python 中我使用 inWaiting() 来读取所有可用字符.在 Ruby 中, read() 函数读取所有这些,而不仅仅是一个.

The problem is that it is very very slow. Same code written in Ruby is much faster. When it takes 30 sec in python, it takes 2 or 3 sec in ruby (using same speed). So it's not a hardware issue.
The only difference between Ruby code and Python is that in Python I use inWaiting() for reading all characters available. In Ruby, read() function reads all of them and not just one.

代码:

代码如下:在 read_retry 函数中,我检查必须读取多少个字符.我阅读了它们,然后调用 data_is_ok 函数来检查它是否已完成.
如您所见,返回的数据中嵌入了\r".当最后一个字符为 '\r'(没有更多数据可用)时,读取完成.
所以有一个循环来读取大量的块.

Here's the code: In read_retry function I check how many characters I have to read. I read them, and then call data_is_ok function to check if it's finished or not.
As you can see, there are '\r' embedded in the data returned. Reading is finished when the last character is '\r' (no more data available).
So there's a loop to read numerous chunks.

import serial
[...]

def data_is_ok(data):
  # No status code yet
  if len(data) < 2: return False

  # Non-OK status
  if len(data) == 2 and data[0] != "0" and data[1] == "\r": return True

  # Non-OK status with extra data on end
  if len(data) > 2 and data[0] != "0":
    raise ValueError('Error parsing status from meter (Non-OK status with extra data on end)')

  # We should now be in OK state
  if data[0] != "0" or data[1] != "\r":
    raise ValueError('Error parsing status from meter (status:%c size:%d)' % (data[0], len(data)))

  return len(data) >= 4 and data[-1] == "\r"

def read_retry():
  retry_count = 0
  data = ""

  while retry_count < 500 and not data_is_ok(data):
    bytesToRead = ser.inWaiting()
    data += ser.read(bytesToRead)
    if data_is_ok(data): return data
    time.sleep (0.001)
    retry_count += 1
  raise ValueError('Error parsing status from meter:  %c %d %r %r' % (data[0],len(data),data[1] == '\r', data[-1] == '\r'))

[...]

# Serial port settings
try:
  ser = serial.Serial(port='/dev/cu.usbserial-AK05FTGH', baudrate=115200, bytesize=8, parity='N', stopbits=1, timeout=5, rtscts=False, dsrdtr=False)
except serial.serialutil.SerialException, err:
  print "Serial Port /dev/cu.usbserial-AK05FTGH doesn't respond"
  print err
  sys.exit()

[...]

ser.write(cmd+'\r')
data = read_retry()

我使用了 cProfile 分析器.大部分时间都花在时间睡觉

I have used cProfile profiler. Most of the time is spent in time.sleep

这是一个摘录:

         363096 function calls (363085 primitive calls) in 28.821 seconds

   Ordered by: internal time
   List reduced from 127 to 10 due to restriction <10>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    19050   25.245    0.001   25.245    0.001 {time.sleep}
        1    1.502    1.502    1.502    1.502 {posix.open}

问题:
是否可以使代码更快?

The question:
Is it possible to make the code faster ?

推荐答案

您可以做一些事情.

你不应该需要 time.sleep().如果你知道你只需要 2 个字节,那么执行 ser.read(2) 并且如果你想限制等待时间 ser.timeout = 0.01编辑,除非您在单独的线程中.Python 线程是贪婪的.I/O 操作释放线程以便主线程可以运行.但是,在您的情况下,您总是读取缓冲区 ser.read(ser.inWaiting()) 中的数据.我发现您需要强制串行端口等待 I/O ser.read(ser.inWaiting() + 32).只需确保您也有超时,这样您就不会永远等待 I/O.此外,您当前的超时时间为 5 秒,等待时间很长.结束编辑

You shouldn't need time.sleep(). If you know you only want 2 bytes then do ser.read(2) and if you want to limit the wait time ser.timeout = 0.01 EDIT unless you are in a separate thread. Python threads are greedy. I/O operations release the thread so the main thread can run. However, in your case you are always reading the data that is in the buffer ser.read(ser.inWaiting()). I've found that you need to force the serial port to wait on I/O ser.read(ser.inWaiting() + 32). Just make sure you also have a timeout, so you aren't waiting for the I/O forever. Also your current timeout is 5 seconds which is a long time to wait. end edit

您也许可以使用 readline ser.readline() 这可能只能读取到 '\r\n'.我不确定.

You might be able to use readline ser.readline() this may only read to '\r\n'. I'm not sure.

如果您使用的是 Python3,则 ser.read() 返回字节,因此任何比较都将是错误的.data[0] == '0' 需要是 data[0:1] == b'0'

If you are on Python3 then ser.read() returns bytes, so any comparison will be false. data[0] == '0' would need to be data[0:1] == b'0'

另一件要做的事情是通过调试器运行代码,以确保您获得了您期望的数据.字符串比较或某些错误可能会使您不必要地循环很多次.

The other thing to do is to run the code through the debugger to make sure you are getting the data you are expecting. It is possible that a string comparison or something is wrong which would make you loop a bunch of times needlessly.

这篇关于Python串口读取极慢,如何加速?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆