You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 09:50:08