我正在使用以下代码与Digital Multi Meter(DMM)进行通信 它工作正常。我可以发送命令和读取结果。 我不使用readline,因为我读了二进制数据。
问题:
问题是非常慢。 用Ruby编写的相同代码要快得多。 当在python中花费30秒时,红宝石需要2或3秒(使用相同的速度)。 所以这不是硬件问题 Ruby代码和Python之间的唯一区别是在Python中我使用inWaiting()来读取所有可用的字符。在Ruby中,read()函数读取所有这些而不仅仅是一个。
代码:
这是代码: 在read_retry函数中,我检查我必须读取多少个字符。 我读了它们,然后调用data_is_ok函数来检查它是否已经完成 如您所见,返回的数据中嵌入了'\ r'。 当最后一个字符是'\ r'(没有更多可用数据)时,读取完成 所以有一个循环来读取大量的块。
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 profiler。大多数时间花在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}
问题:
是否可以使代码更快?
答案 0 :(得分:3)
你可以做几件事。
您不应该需要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秒,这是一个很长的等待时间。 结束编辑
您可以使用readline ser.readline()
这可能只读取'\ r \ n'。我不确定。
如果您使用的是Python3,那么ser.read()
将返回字节,因此任何比较都将为false。 data[0] == '0'
需要data[0:1] == b'0'
另一件事是通过调试器运行代码以确保获得您期望的数据。字符串比较可能是错误的,这会让你不必要地循环多次。
答案 1 :(得分:2)
我遇到了同样的问题,即在使用serial.readline方法时pySerial库的读取速度非常慢。
我正在运行Windows 7,并尝试了pySerial的不同版本,从2.7到3.4。
让我展示我的关注。发送器以0.02 = 1的波特率发送每0.02s的新数据。
如果我执行以下操作
import serial
if __name__ == '__main__':
serial_port = serial.Serial('COM26', 115200)
while(True):
print(serial_port.is_open)
serial_port.readline()
print(serial_port.in_waiting)
我看到缓冲区中的字节数正在增加。写入缓冲区要快于读取。
以下代码段显示了此问题的解决方案,该代码段必须使用serial.read方法并一次读取所有缓冲的字节。如果我执行这个
import serial
if __name__ == '__main__':
serial_port = serial.Serial('COM26', 115200)
while(True):
print(serial_port.is_open)
serial_port.read(serial_port.in_waiting)
print(serial_port.in_waiting)
然后我看到serial_port.read(serial_port.in_waiting)可以非常快速地读取,并且避免了缓冲区中字节累积的问题。
我现在对解决方案还可以,但是也许有人可以解释为什么这样做。
谢谢。
答案 2 :(得分:0)
尝试使用超时而不是重试计数
import time
def read_retry():
timeout = 500 * 0.001
data = ""
tic = time.time()
while toc - tic < timeout and not data_is_ok(data):
bytesToRead = ser.inWaiting()
data += ser.read(bytesToRead)
if data_is_ok(data): return data
toc = time.time()
raise ValueError('Error parsing status from meter: %c %d %r %r' % (data[0],len(data),data[1] == '\r', data[-1] == '\r'))