读取USB设备时触发Index Out of Range错误的排查求助
USB设备读取索引越界问题修复
问题分析
触发IndexError的核心原因是:在非阻塞模式下,simpad.read(11)并非每次都能返回完整的11字节数据——当设备还没准备好数据时,会返回空列表或长度不足的字节数组,这时直接访问readout[2]就会超出索引范围。你的开发机上数据传输稳定,所以没出现问题,但其他PC可能因为USB驱动差异、设备初始化延迟等原因出现读取不完整的情况。
另外,你在每次run()循环里重复打开USB设备,这种反复初始化的操作会加剧设备通信的不稳定性,也是潜在问题。
解决方案
- 提前初始化USB设备:只在程序启动时打开一次设备,避免反复打开/关闭
- 增加读取有效性判断:每次读取后先检查返回数据的长度,只有长度符合要求时才处理数据
- 容错处理:读取失败或数据异常时跳过本次循环,继续等待下一次读取
修改后的代码
from tkinter import * from PIL import ImageTk, Image import hid import sys import os def resource_path(relative_path): """ 获取资源的绝对路径,兼容开发环境和PyInstaller打包 """ try: base_path = sys._MEIPASS except Exception: base_path = os.path.abspath(".") return os.path.join(base_path, relative_path) win = Tk("Test") win.geometry("460x410") win.config(bg="grey") Btn = Button(win, text="ok", activebackground='gray', bg='grey', bd=0) path = resource_path("Animation/") List = ['000.png', '001.png', '002.png', '003.png', '004.png', '005.png', '006.png', '007.png', '008.png', '009.png', '010.png', '011.png', '012.png', '013.png', '014.png', '015.png', '016.png', '017.png', '018.png', '019.png', '020.png', '021.png', '022.png', '023.png', '024.png', '025.png', '026.png', '027.png', '028.png', '029.png', '030.png', '031.png', '032.png', '033.png', '034.png', '035.png', '036.png', '037.png', '038.png', '039.png', '040.png', '041.png', '042.png', '043.png', '044.png', '045.png', '046.png', '047.png', '048.png', '049.png', '050.png', '051.png', '052.png', '053.png', '054.png', '055.png', '056.png', '057.png', '058.png', '059.png', '060.png', '061.png', '062.png', '063.png', '064.png', '065.png', '066.png', '067.png', '068.png', '069.png', '070.png', '071.png', '072.png', '073.png', '074.png', '075.png', '076.png', '077.png', '078.png', '079.png', '080.png', '081.png', '082.png', '083.png', '084.png', '085.png', '086.png', '087.png', '088.png', '089.png', '090.png', '091.png', '092.png', '093.png', '094.png', '095.png', '096.png', '097.png', '098.png', '099.png', '100.png', '101.png', '102.png', '103.png', '104.png', '105.png', '106.png', '107.png', '108.png', '109.png', '110.png', '111.png', '112.png', '113.png', '114.png', '115.png', '116.png', '117.png', '118.png', '119.png', '120.png', '121.png', '122.png', '123.png', '124.png', '125.png', '126.png', '127.png', '128.png', '129.png', '130.png', '131.png', '132.png', '133.png', '134.png', '135.png', '136.png', '137.png', '138.png', '139.png', '140.png', '141.png', '142.png', '143.png', '144.png', '145.png', '146.png', '147.png', '148.png', '149.png', '150.png', '151.png', '152.png', '153.png', '154.png', '155.png', '156.png', '157.png', '158.png', '159.png', '160.png', '161.png', '162.png', '163.png', '164.png', '165.png', '166.png', '167.png', '168.png', '169.png', '170.png', '171.png', '172.png', '173.png', '174.png', '175.png', '176.png', '177.png', '178.png', '179.png', '180.png', '181.png', '182.png', '183.png', '184.png', '185.png', '186.png', '187.png', '188.png', '189.png', '190.png', '191.png', '192.png', '193.png', '194.png', '195.png', '196.png', '197.png', '198.png', '199.png', '200.png', '201.png', '202.png', '203.png', '204.png', '205.png', '206.png', '207.png', '208.png', '209.png', '210.png', '211.png', '212.png', '213.png', '214.png', '215.png', '216.png', '217.png', '218.png', '219.png', '220.png', '221.png', '222.png', '223.png', '224.png', '225.png', '226.png', '227.png', '228.png', '229.png', '230.png', '231.png', '232.png', '233.png', '234.png', '235.png', '236.png', '237.png', '238.png', '239.png', '240.png', '241.png', '242.png', '243.png', '244.png', '245.png', '246.png', '247.png', '248.png', '249.png', '250.png', '251.png', '252.png', '253.png', '254.png', '255.png', '256.png', '257.png', '258.png', '259.png', '260.png', ] def to_pil2(img, button, x, y, w, h): image = Image.open(img) image = image.resize((w, h)) pic = ImageTk.PhotoImage(image) button['image'] = pic button.image = pic button.place(x=x, y=y) label = Label(win, font=('Stencil', 30, 'bold'), bg='grey', fg='black') label.place(x=5, y=5) # 全局初始化USB设备,只执行一次 simpad = hid.device() simpad.open(0x2341, 0x8037) simpad.set_nonblocking(True) def run(): global simpad readout = simpad.read(11) # 先判断读取的数据长度是否足够,避免索引越界 if len(readout) >= 3: read_x2 = readout[2] # 确保读取到的数值在List的索引范围内(0-260) if 0 <= read_x2 < len(List): to_pil2(path + List[read_x2], Btn, 5, 5, 450, 400) label['text'] = read_x2 else: # 数值超出范围时的容错处理 label['text'] = "Out of Range" else: # 读取数据不足时的提示 label['text'] = "No Data" # 继续下一次循环 win.after(10, run) print(readout) run() Btn["command"] = lambda: print('Freeze-test') # 程序关闭时关闭USB设备,避免资源占用 def on_closing(): simpad.close() win.destroy() win.protocol("WM_DELETE_WINDOW", on_closing) win.mainloop()
额外说明
- 新增了程序关闭时的设备释放逻辑,避免USB设备被占用
- 增加了
read_x2的范围判断,防止因设备返回异常值导致图片路径错误 - 移除了重复打开设备的操作,提升通信稳定性
内容的提问来源于stack exchange,提问作者Emanresu
相关产品推荐
相关产品推荐

