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

COM5串口访问被拒:Arduino雷达可视化Python脚本故障求助

串口权限错误:无法打开COM5端口(PermissionError 13)

我正在编写Python脚本,通过COM5端口读取搭载舵机和超声波传感器的Arduino设备串口数据,用来绘制雷达样式图表。已经尝试以下排查步骤:

  • 关闭Arduino IDE串口监视器,确认无程序占用端口
  • 以管理员权限运行脚本
  • 重启电脑
  • 卸载并重装pyserial库

但始终触发如下错误:

serial.serialutil.SerialException: could not open port 'COM5': PermissionError(13, 'Access is denied.', None, 5)

我的Python代码:

from matplotlib import pyplot as plt
import matplotlib
matplotlib.use('TkAgg')
import numpy as np
import time
import serial
import keyboard

ser = serial.Serial('COM5', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=2)

# setup the basic display window
fig = plt.figure(facecolor='k')
fig.canvas.toolbar.pack_forget()
fig.canvas.manager.set_window_title('Radar')
mng = plt.get_current_fig_manager()
mng.window.state('zoomed')

#setup the sub plot (the radar screen)  
ax = fig.add_subplot(1,1,1, polar=True, facecolor='green')
ax.tick_params(axis='both', colors='white' )
ax.xaxis.grid(True, color='grey', linestyle='--')
ax.yaxis.grid(True, color='grey', linestyle='--')
rmax = 100
ax.set_ylim([0.0, rmax])
ax.set_xlim([0.0, np.pi])
ax.set_position([0.1, 0.1, 0.8, 0.8 ])
ax.set_rticks(np.linspace(0.0, rmax, 5))
ax.set_rlabel_position(0)
ax.set_thetagrids(np.linspace(0.0, 360, 17))

angles = np.arange(0, 360, 1)

#convert angles to radians
theta = angles * (np.pi / 180) 

pols = ax.plot([], linestyle='', marker='o', markerfacecolor='r', markeredgecolor='w', 
               markeredgewidth=1.0, markersize=3.0, alpha=0.5)
line1 = ax.plot([], color='w', linewidth=3.0)

fig.canvas.draw()
dists = np.ones((len(angles),))
axbbackground = fig.canvas.copy_from_bbox(ax.bbox)

while True:

    try:
        data = ser.readline()
        decoded = data.decode()
        data = (decoded.replace('\r','')).replace('\n','')
        #split data values (one distance, one angle) where there is a comma
        vals = [float(ii) for ii in data.split(',')]
        #check that there is in fact 2 values and it doesnt try plot an incomplete graph
        if len(vals) < 2:
            continue
        angle, dist = vals
        # can test working by adding 
        #print(angle, dist)

        #Create all angles into a list to plot all at once
        dists[int(angle)] = dist

        pols.set_data(theta, dists)
        #plot background
        fig.canvas.restore_region(axbbackground)
        ax.draw_artist(pols)

        #create sweeping line
        line1.set_data(np.repeat((angle*(np.pi/180)), 2),np.linspace(0.0, rmax, 2))
        ax.draw_artist(line1)    
        fig.canvas.blit(ax.bbox)
        fig.canvas.flush_events()
        
        if keyboard.is_pressed('q'):
            plt.close('all')
            print('User to  quit the application')
            break

    except KeyboardInterrupt:
        plt.close('all')
        ser.close()  # Always close the port after usage
        print("Keyboard interrupt")
        break

exit

我的Arduino代码:

#include <Servo.h>

Servo micro_servo;

int pos = 0;

const int servo_pin = 7;
const int trig_echo_pin = 3;

float duration, distance;

void setup() {
  pinMode (servo_pin, OUTPUT);
  pinMode (trig_echo_pin, OUTPUT);
  Serial.begin(9600);
  micro_servo.attach(servo_pin);
}

void loop() {
  // Sweep the servo from 0 to 180 degrees
  for (pos = 0; pos <= 180; pos++)
  {
    micro_servo.write(pos);
    delay(50);
    read_ultrasonic_distance(pos);
  }
    // Sweep the servo back from 180 to 0 degrees
  for (pos = 180;pos>=180; pos--)
  {
    micro_servo.write(pos);
    delay(50);
    read_ultrasonic_distance(pos);
  }
  delay(200);
}

void read_ultrasonic_distance(int angle)
{
pinMode(trig_echo_pin, OUTPUT);
  digitalWrite(trig_echo_pin, LOW);
  delayMicroseconds(2);
  digitalWrite(trig_echo_pin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trig_echo_pin, LOW);

  // Set the pin to input mode to receive the echo
  pinMode(trig_echo_pin, INPUT);
  duration = pulseIn(trig_echo_pin, HIGH);  // Measure the duration of the echo pulse

  // Calculate the distance based on the duration
  distance = (duration * 0.0343) / 2;

  // Send the angle and distance to the Python program via serial
  Serial.print(angle);
  Serial.print(",");
  Serial.println(distance);
}

解决建议

1. 清理隐蔽的端口占用进程

Arduino IDE关闭后,后台可能残留java.exe进程占用串口。打开任务管理器,结束所有与Arduino、Python相关的进程,再重新运行脚本。

2. 优化串口初始化与关闭逻辑

给串口初始化加重试机制,同时确保所有退出路径都能正确关闭端口:

import serial.tools.list_ports

# 先验证端口存在性
ports = [port.device for port in serial.tools.list_ports.comports()]
if 'COM5' not in ports:
    print("COM5端口不存在,请检查设备连接")
    exit()

# 带重试的串口初始化
ser = None
for _ in range(3):
    try:
        ser = serial.Serial('COM5', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=2)
        break
    except serial.SerialException as e:
        print(f"尝试打开端口失败:{e}")
        time.sleep(1)

if not ser:
    print("多次尝试后仍无法打开端口")
    exit()

# 原循环逻辑...

# 确保程序退出时关闭串口
try:
    while True:
        # 原循环代码
        pass
finally:
    if ser and ser.is_open:
        ser.close()
        print("串口已关闭")

3. 修复Arduino的舵机回扫逻辑

你的Arduino代码中,舵机回扫的循环条件错误,导致只会执行一次:

// 错误代码
for (pos = 180;pos>=180; pos--)
// 修正为
for (pos = 180; pos >= 0; pos--)

错误的循环会导致数据发送异常,间接影响串口连接稳定性。

4. 排查硬件与驱动问题

  • 更换USB接口重新连接Arduino,排除接触不良
  • 卸载并重新安装Arduino官方USB串口驱动
  • 检查设备管理器中COM5端口是否有黄色感叹号,若有则修复驱动

内容的提问来源于stack exchange,提问作者Loft

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 10:54:50