USB断开重连后Python无法识别RFID设备的诊断与修复问询
问题描述
树莓派OS环境下,部分模拟键盘的RFID阅读器会出现USB短暂断开(仅约300ms)后立即重连的情况,导致基于Python 3.9+EVDEV开发的应用崩溃并丢失状态:
- 已能捕获
[Errno 19] No such device异常,但即使设备重新出现在/dev目录中,Python仍无法识别它,等待10秒重试也无效 - 不想通过保存状态重启应用(操作繁琐),需要找到无需重启即可让Python重新识别设备的方法
异常信息:
2025-10-22 09:21:06 User: {'authorized': True, 'id': '0009444691'} 2025-10-22 09:21:33 Exception: [Errno 19] No such device Traceback (most recent call last): File "drivers/RFID/KeyboardRFID.py", line 28, in run for event in dev.read_loop(): File "/usr/local/lib/python3.9/dist-packages/evdev/eventio.py", line 46, in read_loop for event in self.read(): File "/usr/local/lib/python3.9/dist-packages/evdev/eventio.py", line 71, in read events = _input.device_read_many(self.fd)
核心问题代码块:
for event in dev.read_loop(): # If key event and key up (0) if event.type == ecodes.EV_KEY and event.value == 0: key = scancodes.get(event.code) if key == u'\n': # if enter self.notifyScanObservers(rfid_code) rfid_code = "" else: rfid_code = rfid_code + key except Exception as e: logging.error("Exception: %s" % str(e), exc_info=1) os._exit(42) # Make sure entire application exits
解决方案
1. 移除硬退出逻辑,改为设备重连循环
当前代码在捕获异常后直接强制退出应用,这是必须重启的根本原因。修改异常处理逻辑,改为循环检测设备并重新初始化:
import time import logging from evdev import ecodes def run(self): while True: try: # 循环内重新查找并初始化设备 dev = self.find_rfid_device() if not dev: logging.warning("RFID设备未找到,1秒后重试...") time.sleep(1) continue logging.info("已连接到RFID设备") rfid_code = "" for event in dev.read_loop(): if event.type == ecodes.EV_KEY and event.value == 0: key = scancodes.get(event.code) if key == u'\n': self.notifyScanObservers(rfid_code) rfid_code = "" else: rfid_code = rfid_code + key except OSError as e: if e.errno == 19: # 捕获"设备不存在"错误 logging.error("RFID设备断开,开始重连...") # 关闭旧设备句柄释放资源 if 'dev' in locals(): dev.close() time.sleep(0.5) # 等待设备完成重新枚举 except Exception as e: logging.error("未知异常: %s" % str(e), exc_info=1) time.sleep(1)
2. 用硬件ID动态查找设备,避免依赖固定路径
不要硬编码/dev/input/eventX路径,通过设备的vendor ID和product ID精准匹配目标阅读器(可通过lsusb命令查看对应ID):
import evdev def find_rfid_device(self): # 替换为你的RFID阅读器的vendor_id和product_id target_vid = 0x1234 target_pid = 0x5678 for path in evdev.list_devices(): dev = evdev.InputDevice(path) if dev.info.vendor == target_vid and dev.info.product == target_pid: return dev return None
3. 及时失效文件句柄
设备断开后,原dev对象的文件句柄已失效,必须在捕获异常后主动调用dev.close(),否则无法复用旧对象连接新设备。
4. 用udev监听设备插拔(进阶)
如果需要更及时的重连响应,可结合pyudev库监听USB设备事件,替代轮询:
import pyudev def setup_device_monitor(self): context = pyudev.Context() monitor = pyudev.Monitor.from_netlink(context) monitor.filter_by(subsystem='input') # 启动线程监听事件 def monitor_thread(): for device in iter(monitor.poll, None): if device.action == 'add': # 匹配目标设备的ID if device.get('ID_VENDOR_ID') == '1234' and device.get('ID_MODEL_ID') == '5678': logging.info("RFID设备已重新连接") # 触发设备初始化逻辑 import threading threading.Thread(target=monitor_thread, daemon=True).start()
关键注意事项
- 禁止在异常处理中直接退出应用,保持主循环持续运行
- 每次重连必须重新创建
InputDevice实例,不能复用旧对象 - 用vendor/product ID定位设备,避免设备节点编号变化导致匹配失败
内容的提问来源于stack exchange,提问作者ozindfw
相关产品推荐
相关产品推荐

