PySerial读取Arduino红外编码器数据异常 调用pygame.display.update后冻结
可行修复方案
- 将串口读取逻辑移至独立子线程
主线程中pygame.display.update()的渲染操作会抢占主线程IO资源,导致串口读取被阻塞。单独启动守护线程处理串口数据,通过队列将最新状态传递给主线程游戏逻辑即可解决冲突,代码示例如下:
import threading import queue # 初始化全局队列,最多存储1条最新数据 sensor_data_queue = queue.Queue(maxsize=1) def serial_read_worker(): while True: if serialInst.inWaiting(): packet = serialInst.readline() try: current_data = int(packet.decode('utf').rstrip('\n')) # 清空队列旧数据,保证永远取到最新的传感器状态 if not sensor_data_queue.empty(): sensor_data_queue.get_nowait() sensor_data_queue.put_nowait(current_data) except ValueError: # 忽略解析异常,避免线程崩溃 continue # 串口初始化完成后启动线程,daemon=True保证程序退出时线程自动关闭 threading.Thread(target=serial_read_worker, daemon=True).start()
修改后的readData函数直接从队列取值即可:
def readData(self): if not sensor_data_queue.empty(): current_state = sensor_data_queue.get_nowait() print(current_state) self.last_sensor_state = current_state # 无新数据时返回上一次有效状态,避免逻辑中断 return self.last_sensor_state
- 简化串口通信协议,降低解析开销
修改Arduino端输出逻辑,不要发送带换行符的数字字符串,直接发送单字节ASCII字符'0'/'1',Python端改用单字节读取,避免readline等待换行符的阻塞问题。
Arduino端代码修改示例:
// 替换原有Serial.println(1) / Serial.println(0)逻辑 if (传感器被遮挡) { Serial.write('1'); } else { Serial.write('0'); }
对应Python端读取逻辑修改:
def readData(self): # 清空缓冲区所有旧数据,只保留最后一个字节的最新状态 while serialInst.inWaiting() > 0: latest_byte = serialInst.read(1) try: data = latest_byte.decode('utf') if data == '1': print('1') self.last_sensor_state = 1 elif data == '0': print('0') self.last_sensor_state = 0 except: pass return self.last_sensor_state
- 限制Pygame渲染帧率,减少CPU占用
在主循环中添加帧率控制,避免pygame.display.update()高频调用占用过多系统资源,给串口读取留足时间片:
# 主循环初始化时创建Clock对象 game_clock = pygame.time.Clock() while 游戏运行: readData() # 其他游戏逻辑 pygame.display.update() # 固定30帧,可根据实际需求调整 game_clock.tick(30)
- 升级Pygame版本修复M1适配问题
如果以上方案无效,可更新Pygame到最新版本,M1芯片的渲染阻塞问题在2.1.0及以上版本有大量适配修复,升级命令:pip install pygame --upgrade
内容的提问来源于stack exchange,提问作者Josh
相关产品推荐
相关产品推荐

