如何在Python中设置serial.readline()采样率 实现500次/秒稳定串口读取
串口固定500Hz采样的可控实现方案
原代码波动原因
- 依赖
time()计时精度不足,常规time()在多数系统下精度仅为10~16ms,无法满足2ms间隔的控制需求 ser.readline()为阻塞调用,串口无可用数据时会一直等待到超时,单次执行耗时不稳定- 动态
appendnumpy数组会触发频繁的内存重新分配,额外开销波动大,进一步干扰采样间隔 - 未做显式的间隔控制,循环执行速度完全受串口返回速度、系统调度影响,采样率自然不稳定
可行实现方案
优先使用高精度计时器+忙等待控制间隔+预分配存储空间的方案,兼顾精度和易用性,代码示例如下:
import numpy as np from time import perf_counter import serial # 串口配置 ser = serial.Serial('COM3', 115200, timeout=0.001) # 超时设为1ms,远小于2ms间隔,避免长时间阻塞 ser.flushInput() # 采样参数 sample_rate = 500 # Hz interval = 1 / sample_rate # 2ms duration = 60 # 秒 total_samples = int(sample_rate * duration) # 预分配存储空间,避免动态扩容开销 digital_data = [None] * total_samples sample_idx = 0 start_time = perf_counter() next_sample_time = start_time + interval while sample_idx < total_samples: current_time = perf_counter() # 到达采样时间点 if current_time >= next_sample_time: # 读取串口数据 if ser.in_waiting: # 先判断有数据再读,进一步降低阻塞概率 line = ser.readline().decode('utf-8').strip() digital_data[sample_idx] = line else: digital_data[sample_idx] = None # 无数据时填占位符,避免索引错位 sample_idx += 1 next_sample_time = start_time + sample_idx * interval # 对齐基准时间,避免误差累积 # 不需要极致精度时可取消下面两行注释,降低CPU占用 # else: # time.sleep(0.0005) # 过滤空值转numpy数组 digital_data = np.array([x for x in digital_data if x is not None]) print(f"实际有效采样数: {len(digital_data)}") ser.close()
额外优化建议
- 若对CPU占用敏感,Windows系统可调用
winmm.dll的多媒体定时器,Linux系统可使用nanosleep实现更高精度的低占用休眠 - 确认硬件端串口发送速率匹配500Hz,115200波特率下单次数据包最大长度不能超过23字节(含换行符),否则会出现硬件发送瓶颈导致丢包
- 如果要求完全不丢采样点,可拆分读写逻辑:单独开一个子线程持续读取串口数据存入队列,主线程按固定间隔从队列中取数,完全隔离串口读取阻塞对定时的影响
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

