通过PySerial读取Arduino串口数据时遭遇COM端口权限拒绝问题
解决PySerial访问COM端口被拒绝的问题
咱们先一步步拆解你遇到的PermissionError(13, 'Access is denied')问题,从最常见的原因到代码里的致命错误逐一解决:
一、最直接的原因:串口被其他进程占用
- 先彻底关闭Arduino IDE(不仅仅是关闭串口监视器,有时候IDE后台进程还会占用端口)
- 检查有没有其他串口工具(比如PuTTY、串口调试助手)正在使用COM3,全部关闭后再运行脚本
- 如果之前脚本崩溃过,打开任务管理器杀掉残留的Python.exe进程——崩溃的进程可能没释放串口资源
二、Python脚本的核心错误:循环内重复打开串口
看你的代码,在while True循环里每次都执行ser_data = serial.Serial("COM3",9600),这会导致每次循环都尝试重新打开串口,但前一次打开的串口没有关闭,系统会判定端口一直被占用,直接触发权限拒绝。
修正后的完整Python代码(关键改动已标注)
import cv2 import sys import serial from serial import SerialException if __name__ == '__main__' : # Set up tracker. tracker = cv2.TrackerMIL_create() # Read video video = cv2.VideoCapture(0) # Exit if video not opened. if not video.isOpened(): print ("Could not open video") sys.exit() # Read first frame. ok, frame = video.read() if not ok: print ('Cannot read video file') sys.exit() # Define ROI bbox = cv2.selectROI(frame, False) # Initialize tracker with first frame and bounding box ok = tracker.init(frame, bbox) # --- 关键改动1:串口初始化移到循环外,只打开一次 --- ser_data = None try: ser_data = serial.Serial("COM3", 9600, timeout=1) # 添加timeout避免阻塞 ser_data.readline() # 读取第一行可能的初始化垃圾数据 except SerialException as e: print(f"无法打开串口: {e}") sys.exit() while True: # Read a new frame ok, frame = video.read() if not ok: break # Start timer for FPS calculation timer = cv2.getTickCount() # Update tracker ok, bbox = tracker.update(frame) # Calculate Frames per second (FPS) fps = cv2.getTickFrequency() / (cv2.getTickCount() - timer); # Calculate Distance from Raspi focallength = 480.20 knownwidth = 30.480 # Almost the same size as A4 paper # --- 改动2:处理bbox宽度为0的情况,避免除以0报错 --- RaspiDist = 0 if bbox[2] != 0: RaspiDist = (knownwidth*focallength)/bbox[2] # --- 关键改动3:正确读取串口数据,而非直接使用serial对象 --- ir_distance = 0 if ser_data.in_waiting > 0: try: line = ser_data.readline().decode('utf-8').strip() if line.isdigit(): ir_distance = int(line) except ValueError: print(f"读取到无效数据: {line}") # Draw bounding box if ok: # Tracking success p1 = (int(bbox[0]), int(bbox[1])) p2 = (int(bbox[0] + bbox[2]), int(bbox[1] + bbox[3])) cv2.rectangle(frame, p1, p2, (0,255,255), 2, 1) else : # Tracking failure cv2.putText(frame, "Tracking failure detected", (100,80), cv2.FONT_HERSHEY_SIMPLEX, 0.75,(0,0,255),2) # Display tracker type on frame cv2.putText(frame, "MIL Tracker", (0,20), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0,255,255), 2); # Display FPS on frame cv2.putText(frame, "FPS : " + str(int(fps)), (0,50), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0,255,255), 2); # Display Distance on frame cv2.putText(frame, "RaspiDistance : " + str(int(RaspiDist))+" cms", (0,75), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0,255,255), 2); cv2.putText(frame, "IRDistance : " + str(ir_distance) + " cms", (0,100), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0,255,255), 2); # Display result cv2.imshow("Tracking", frame) # Exit if ESC pressed k = cv2.waitKey(1) & 0xff if k == 27: break # 新增ESC退出逻辑,确保程序能正常释放资源 # --- 关键改动4:程序结束时主动关闭串口和摄像头 --- if ser_data is not None and ser_data.is_open: ser_data.close() video.release() # Destroy all windows cv2.destroyAllWindows()
三、其他备选解决方案
- Windows权限问题:右键点击Python IDE或终端,选择「以管理员身份运行」后再执行脚本
- 串口驱动故障:在设备管理器里找到Arduino的串口设备,右键卸载驱动后重新安装官方驱动
- 端口编号冲突:如果COM3一直异常,可以在设备管理器里修改Arduino的串口编号(比如改成COM4),再同步修改Python脚本里的端口号
验证步骤
- 关闭所有可能占用串口的程序(Arduino IDE、串口工具)
- 用管理员身份运行修正后的Python脚本
- 若仍报错,检查设备管理器里Arduino的串口编号是否正确,有没有黄色感叹号(驱动问题)
内容的提问来源于stack exchange,提问作者LeafTeaNeko
相关产品推荐
相关产品推荐

