Python Serial接收速率低于发送速率的问题排查求助
UART转USB接收速率不达预期的排查思路
问题背景
将两个UART转USB适配器相连,以100Hz速率发送14字节数据包,逻辑分析仪确认发送速率正常,但接收线程仅能达到约60Hz处理速率。已排除其他进程资源占用,简化测试代码如下:
import serial import time import threading port1 = 'COM3' port2 = 'COM4' baud_rate = 115200 packet_size = 14 # Size of each packet in bytes packet_interval = 0.01 # Interval between packets in seconds (10 ms) number_of_packets = 100 # Generate test data to send in each packet test_packet = b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[:packet_size] def sender(): with serial.Serial(port1, baud_rate, timeout=1) as ser: start_time = 0 for _ in range(number_of_packets): ser.write(test_packet) ser.flush() time_taken = time.perf_counter() - start_time print(f"Sent {len(test_packet)} bytes in {time_taken:.6f} seconds. {1/time_taken:0.6f} Hz") start_time = time.perf_counter() # sleep_time = max(packet_interval-time_taken, 0) time.sleep(packet_interval) def receiver(): with serial.Serial(port2, baud_rate, timeout=1) as ser: ser.reset_input_buffer() received_data = b'' start_time = time.perf_counter() prev_data_in_waiting = 0 while True: data_in_waiting = ser.in_waiting if(not data_in_waiting == prev_data_in_waiting): print(f"data in queue {data_in_waiting}") prev_data_in_waiting = data_in_waiting dt = time.perf_counter()-start_time print(f"freq : {1/dt}") start_time = time.perf_counter() time.sleep(0.001) # Start sender and receiver in separate threads start_time = time.perf_counter() receiver_thread = threading.Thread(target=receiver) sender_thread = threading.Thread(target=sender) receiver_thread.start() sender_thread.start() receiver_thread.join() sender_thread.join() total_time = time.time() - start_time print(f"Total time for data transmission: {total_time:.2f} seconds")
期望接收速率与发送速率一致,但实际仅约60Hz,以下是排查思路:
1. 接收线程轮询逻辑缺陷
当前接收线程用time.sleep(0.001)轮询in_waiting,但操作系统sleep精度有限(Windows平台默认约10ms),可能导致多次轮询才检测到数据变化,漏统计部分数据包的到达事件。另外,当前逻辑仅在in_waiting变化时统计频率,若多个数据包在一次sleep期间到达,统计的是批量到达频率而非单个数据包的实时速率。
- 改进:去掉固定sleep,改用
ser.read(packet_size)阻塞等待固定字节数,或用select监听串口文件描述符提升响应及时性;同时统计每个完整数据包的接收时间,而非仅in_waiting变化的时间。
2. UART转USB适配器的缓冲区合并
部分UART转USB芯片(如CH340、PL2303)默认启用数据打包机制:当串口数据量较小时,芯片会等待约10ms或积累到一定字节数,才将数据通过USB上传到主机,导致多个14字节数据包被合并成一批发送,接收端无法实时感知单个数据包的到达。
- 验证:打印接收端每次读取的字节数,若多次出现28/42字节(即多个14字节包合并),则说明存在打包延迟;查阅适配器芯片 datasheet 确认是否有相关配置项。
- 解决:通过驱动配置关闭打包延迟,或调整触发阈值为最小字节数(如1字节)。
3. Python serial库配置问题
- 检查流控设置:确认是否启用了
xonxoff或rtscts流控,不必要的流控可能导致传输延迟。 - 调整超时参数:当前接收端timeout设为1s,若改用阻塞读取,可调整为更小的超时值(如0.005s),避免等待过久。
- 尝试直接读取固定字节:用
ser.read(packet_size)替代轮询in_waiting,更准确统计单个数据包的接收周期。
4. 线程调度限制
Python的GIL锁可能导致接收线程无法及时获取CPU时间片,尤其是发送线程频繁调用sleep时,操作系统调度可能偏向发送线程。
- 改进:将接收线程设置为更高优先级(Windows下用
threading.Thread的setPriority,Linux下用os.nice);或改用多进程替代多线程,规避GIL限制。
5. 波特率匹配与硬件干扰
即使设置了115200波特率,适配器晶振精度误差可能导致实际波特率不匹配,引发接收端丢包或帧错误,进而影响速率统计。
- 验证:在接收端校验每个数据包内容是否与发送的
test_packet一致,若存在错误包则说明波特率不匹配;用逻辑分析仪抓取接收端UART信号,确认数据是否正确到达适配器。
内容的提问来源于stack exchange,提问作者tybar
相关产品推荐
相关产品推荐

