如何修改plot_own.py以适配带1ms写入延迟的低速率数据流生成脚本gen_own.py
如何修改plot_own.py以适配带1ms写入延迟的低速率数据流生成脚本gen_own.py
我来帮你解决这个问题!从你的输出能明显看出来,核心问题是原来的plot_own.py默认每次都能读到完整的64字节数据行,但gen_own.py写数据的速度变慢后,脚本读取时经常拿到不完整的内容(甚至是空的),导致split()操作失败抛出异常。下面是具体的修改方案和详细解释:
问题根源分析
- 原
get_data函数每次打开文件后直接跳到末尾,固定读取64字节。但gen_own.py每1ms才会写入一行完整数据(约64字节),当plot脚本的动画循环触发时,可能文件里还没有新的完整行,或者刚写入了一部分内容,导致读到的内容无法分割成有效的x和y值。 - 频繁打开/关闭文件不仅增加开销,还无法记住上次读取的位置,容易出现重复读取或漏掉数据的情况。
具体修改方案
我们需要调整读取逻辑,确保只处理完整的数据行,同时跟踪上次读取的位置,避免重复或遗漏。下面是修改后的完整plot_own.py代码,关键修改点我会单独标注:
#!/usr/bin/env python3 import io import time import matplotlib.pyplot as plt import matplotlib as mpl import matplotlib.animation DATA_FILENAME = "data.txt" PLOT_LIMIT = 20 ANIM_FILENAME = "video.gif" fig, ax = plt.subplots(1, 1, figsize=(10,8)) ax.set_title("Plot of random numbers from `gen_own.py`") ax.set_xlabel("time / s") ax.set_ylabel("random number / #") ax.set_ylim([0, 1]) # 关键修改1:全局维护文件句柄和上次读取位置,避免重复打开文件 file_handle = open(DATA_FILENAME, "r") last_read_pos = 0 def get_data(): global last_read_pos # 从上次读取的位置开始读取新增内容,而非直接跳到文件末尾 file_handle.seek(last_read_pos) raw_data = file_handle.read() # 更新读取位置到当前文件末尾 last_read_pos = file_handle.tell() # 关键修改2:按行分割数据,过滤掉不完整的行和空行 lines = raw_data.strip().split("\n") # 如果最后一行是空的(比如刚写入换行但还没写数据),直接丢弃 if lines and not lines[-1]: lines.pop() return lines def animate(i, xs, ys, limit=PLOT_LIMIT, verbose=False): try: # 现在get_data返回的是完整数据行的列表 new_lines = get_data() for line in new_lines: if not line: continue x, y = map(float, line.split()) # 确保时间递增,避免重复处理旧数据 if not xs or x > xs[-1]: xs.append(x) ys.append(y) # 限制列表长度,保持图表只显示最新的PLOT_LIMIT条数据 xs = xs[-limit:] ys = ys[-limit:] else: print(f"W: {time.time()} :: STALE!") if verbose and new_lines: print(new_lines) except ValueError as e: print(f"W: {time.time()} :: EXCEPTION! {str(e)}") else: # 关键修改3:只有有新数据时才更新图表,避免无效重绘 if xs and ys: ax.clear() ax.set_ylim([0, 1]) ax.plot(xs, ys) # 重新设置标题和轴标签,避免clear后丢失 ax.set_title("Plot of random numbers from `gen_own.py`") ax.set_xlabel("time / s") ax.set_ylabel("random number / #") # 关键修改4:初始化xs和ys为空列表,避免初始值干扰判断 anim = mpl.animation.FuncAnimation(fig, animate, fargs=([], []), interval=5) plt.show() # 关闭文件句柄 file_handle.close() plt.close()
修改点详细说明
- 持久化文件句柄:把文件打开操作放在全局,避免每次读取都重复打开/关闭文件,同时用
last_read_pos记录上次读到的位置,确保每次只读取新增的内容。 - 读取完整行:不再固定读取64字节,而是读取所有新增数据后按行分割,过滤掉不完整的行(比如文件末尾还没写完的半行),彻底解决
split()失败的问题。 - 支持多行处理:允许一次读取多行数据(如果把动画间隔设为5ms,可能一次读到5行),遍历每一行处理,保证所有新数据都能被添加到图表中。
- 优化初始值:把xs和ys的初始值改为空列表,避免原来的初始时间和
None值带来的判断与绘图问题。 - 减少无效重绘:只有当有新数据时才更新图表,避免每次循环都清空重绘,提升脚本运行效率。
额外建议
- 可以把动画
interval从1ms调整到5-10ms,1ms的循环过于频繁会占用过多CPU资源,而gen_own.py每1ms才写一行数据,5ms的间隔完全能覆盖所有新数据。 - 测试时建议先启动
gen_own.py,再运行plot_own.py,确保文件已经存在且有数据在写入。
备注:内容来源于stack exchange,提问作者ecjb
相关产品推荐
相关产品推荐

