我正在使用以下代码与数字万用表 (DMM) 通信。
它工作正常。我可以发送命令并读取结果。
我不使用 readline,因为我读取二进制数据。
问题:
问题是它非常非常慢。
用 Ruby 编写的相同代码要快得多。
当在 python 中需要 30 秒时,在 ruby 中需要 2 或 3 秒(使用相同的速度)。
所以这不是硬件问题。
Ruby 代码和 Python 之间的唯一区别是,在 Python 中,我使用 inWaiting() 来读取所有可用的字符。在 Ruby 中,read() 函数会读取所有这些,而不仅仅是一个。
代码:
代码如下:
在 read_retry 函数中,我检查了我必须读取的字符数。
我读取它们,然后调用 data_is_ok 函数来检查它是否完成。
如您所见,返回的数据中嵌入了“\r”。
当最后一个字符为'\r'(没有更多数据可用)时,读取完成。
所以有一个循环来读取大量的 block 。
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 分析器。大部分时间都在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}
问题:
是否可以使代码更快?
最佳答案
您可以做几件事。
你不应该需要 time.sleep()
。如果你知道你只需要 2 个字节然后执行 ser.read(2)
如果你想限制等待时间 ser.timeout = 0.01
EDIT 除非你在一个单独的线程中。 Python 线程是贪婪的。 I/O 操作释放线程,以便主线程可以运行。但是,在您的情况下,您总是读取缓冲区 ser.read(ser.inWaiting())
中的数据。我发现您需要强制串口等待 I/O ser.read(ser.inWaiting() + 32)
。只需确保您也有超时,这样您就不会永远等待 I/O。此外,您当前的超时时间为 5 秒,等待时间很长。 结束编辑
您可以使用 readline ser.readline()
这可能只读到 '\r\n'。我不确定。
如果您使用的是 Python3,则 ser.read()
返回字节,因此任何比较都是错误的。 data[0] == '0'
需要是 data[0:1] == b'0'
另一件事是通过调试器运行代码,以确保您获得了预期的数据。可能是字符串比较或其他错误导致您不必要地循环了很多次。
关于Python串口读取速度极慢,如何加速?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47235381/