python - 如何减少“readline()”从串行数据中花费的时间
问题描述
我正在尝试创建一个函数来从传感器获取陀螺仪组件 X、Y、Z。
功能如下:
def bimu_get_gyroscope_raw():
#ser = serial.Serial('/dev/ttyUSB0', 115200, timeout=15)
ser = serial.Serial('/dev/tty.usbserial-00002014', 115200, timeout=15)
ser_io = io.TextIOWrapper(io.BufferedRWPair(ser, ser, 1),
newline = '\r',
line_buffering = True)
try:
ser.isOpen()
print('serial is open')
except:
print('error_1')
exit()
#--------------------
i = 0
gyro_dict = dict()
if (ser.isOpen()):
ser.flushInput()
# write the function to get
while (i==0):
try:
print('serial is open_1')
line = ser_io.readline()
print('serial is open_2')
print(line)
except serial.SerialException as err:
print("Error ocurred while reading data: {}".format(err))
if not line.endswith('\r'):
print("Attempt to read from serial port timed out ... Exiting.")
break # terminate the loop and let the program exit
if line.startswith('S,'):
i += 1
line = line.split(',')
print(line)
if len(line)==12:
gyro_dict = {'x':float(line[1]), 'y': float(line[2]), 'z': float(line[3]) }
else:
print('Cannot open serial port')
return gyro_dict
我得到以下输出:
raw = bimu_get_gyroscope_raw()
print(raw)
serial is open
serial is open_1
-43,-122,-2833,83,65
serial is open_2
serial is open_1
S,2,0,0,-20,19,1014,-146,184,-158,168,99
serial is open_2
['S', '2', '0', '0', '-20', '19', '1014', '-146', '184', '-158', '168', '99\r']
{'z': 0.0, 'y': 0.0, 'x': 2.0}
我遇到的问题是,在我第一次拨打电话之间,line = ser_io.readline()
用手动计时器在屏幕上写字大约需要 2.25 秒serial is open_2
。
如果该函数需要再次调用,ser_io.readline()
则没有延迟,并且线条
几乎同时出现serial is open_1
。serial is open_2
我认为第一次调用在readline()
内部对端口或数据缓冲区做了一些已经完成的事情,这使得连续调用readline()
运行得更快。
有什么办法可以解决这个问题并使函数始终快速运行。
编辑
我已经用time
python 模块测试了时间并修改了 readline 部分,如下所示:
while (i<=5):
try:
print('before readline')
start_time = time.time()
line = ser_io.readline()
#print(line)
print("--- %s seconds ---" % (time.time() - start_time))
#print(line)
print('after readline')
except serial.SerialException as err:
print("Error ocurred while reading data: {}".format(err))
if not line.endswith('\r'):
print("Attempt to read from serial port timed out ... Exiting.")
break # terminate the loop and let the program exit
if line.startswith('S,'):
i += 1
line = line.split(',')
print(line)
if len(line)==12:
gyro_dict = {'x':float(line[1]), 'y': float(line[2]), 'z': float(line[3]) }
结果如下:
serial is open
before readline
--- 2.1859400272369385 seconds ---
after readline
before readline
--- 5.9604644775390625e-06 seconds ---
after readline
['S', '0', '0', '0', '380', '0', '-902', '-497', '-228', '200', '63', '103\r']
before readline
--- 2.86102294921875e-06 seconds ---
after readline
before readline
--- 3.814697265625e-06 seconds ---
after readline
['S', '-1', '0', '1', '375', '-8', '-918', '-497', '-223', '194', '64', '108\r']
before readline
--- 3.0994415283203125e-06 seconds ---
after readline
['S', '1', '0', '2', '380', '-10', '-909', '-500', '-223', '200', '65', '113\r']
before readline
--- 2.1457672119140625e-06 seconds ---
after readline
before readline
--- 1.9073486328125e-06 seconds ---
after readline
['S', '0', '0', '0', '379', '-1', '-914', '-500', '-220', '197', '66', '69\r']
before readline
--- 2.1457672119140625e-06 seconds ---
after readline
['S', '0', '0', '-1', '374', '-5', '-902', '-500', '-225', '1\r']
before readline
--- 3.0994415283203125e-06 seconds ---
after readline
['S', '1', '1', '1', '376', '-2', '-915', '-500', '-223', '192', '37', '75\r']
该函数第一次迭代需要两秒多,其余迭代非常快。
解决方案
我有几个建议给你。我编写使用串行端口的 Windows 应用程序并使用不同的方法 - 我假设所有操作系统的原理都是相同的。我首先在程序开始时创建并打开端口,然后将其保持打开状态。在程序存在之前关闭端口是一个很好的做法,但这并不是真正必要的,因为之后操作系统会清理。
但是每次调用该函数时,您的代码都会创建并初始化端口。完成后,您没有明确关闭它;也许您可以摆脱这种情况,因为端口对象会被垃圾收集。在您尝试再次打开它之前,您相信串行库会在操作系统级别正确关闭端口。在任何情况下,如果创建端口对象有开销,为什么不产生一次并完成它呢?
您根本不需要创建 TextIOWrapper,更不用说双向的了。您想知道这是否是您的性能问题的原因,那么为什么不摆脱它呢?串行端口库具有您需要的所有功能:查看read_until
功能。
我认为你应该从这样的框架开始。我无法运行和测试这个程序,所以它只是一个示意图。我已经删除了所有错误处理代码。一个小问题是串行端口对字节进行操作,您必须将其转换为字符串。
ser = serial.Serial('/dev/tty.usbserial-00002014', 115200, timeout=15)
def bimu_get_gyroscope_raw():
while True:
ser.flushInput()
b = ser.read_until('\r')
s = str(b, encoding='latin1') # convert to str
if a.startswith('S,'):
line = s.split(',')
if len(line)==12:
return dict(x = float(line[1]),
y = float(line[2]),
z = float(line[3]))
我已经创建了ser
一个全局变量,但您也可以将它作为参数传递给函数。
请记住串行端口在现代操作系统上的工作方式。您永远不会直接从硬件读取字符 - 操作系统正在为您执行此操作,并将字符放在输入缓冲区中。当您从端口“读取”时,您实际上是在从缓冲区中检索任何字符,或者等待它们的到来。您所观察到的 - 长时间的延迟,然后是快速连续的数据行 - 可以解释为陀螺仪硬件在几秒钟内什么都不做,然后产生超过一条线长的数据突发。我不知道你的陀螺仪是如何工作的,所以我不能说真的是这样。
PySerial 实现实际上是一组操作系统调用的包装器。Python 开销非常小,其中大部分是错误处理代码。我相信您将能够使用 Python 每秒接收数千个字符——我一直都这样做。在现代 PC 上,三秒几乎是永恒的。必须有另一种解释。暂时不要认为 Python 是您的瓶颈。
通过查看屏幕并单击秒表来计时事件是笨拙的。查看 Python 时间包。您可以简单地在每个打印语句中打印 time.time() 的值并收起您的计时器。
您可以独立测试实现的数据收集部分。只需去掉解析数据的逻辑,并永远停留在 while 循环中。打印数据以及每个接收行的时间戳。如果您有另一个与串行端口通信的仪器,您可以将仪器的性能与软件的性能隔离开来。
最后,什么事件导致陀螺仪进行数据传输?它是只是定期广播其数据的仪器之一,还是您必须向它发送一些命令来请求数据?如果前者和广播每三秒一次,谜团就解开了;同样,如果是后者并且响应的延迟为三秒。我可以想象可能会出现这样的情况,因为仪器必须读取一些传感器并将结果转换为字符串。你没有向我们展示整个程序或告诉我们仪器是如何工作的,所以这只是猜测。
推荐阅读
- javascript - 实现可折叠不消耗点击
- maven - Grails 应用程序运行失败,在编译过程中抛出致命错误 org.apache.tools.ant.BuildException: java.lang.NoSuchMethodError
- html - Textarea 溢出它自己的宽度
- c# - 实体框架一对一关系错误
- typescript - 打字稿:在类型上找不到带有“字符串”类型参数的索引签名
- authentication - Blazor Server .Net Core 身份验证链接到其他数据库表
- c - 服务器搞砸了读取功能
- asp.net-web-api - Protobuf-net 和 WebApi,使用哪种格式通过网络返回数据?
- c# - 字符串 PostgreSQL 中的变量
- python - 我的线程 python 代码有什么问题?