基于 YOLOv10 的无人机目标检测系统
介绍基于 YOLOv10 算法的无人机检测系统。系统支持图片、视频及摄像头实时检测,配备 PyQt5 图形界面。内容包括数据集划分与制作流程、Anaconda 环境配置、模型训练参数设置及核心代码实现。适用于空域监控与安防场景,具有高精度和实时性特点。

介绍基于 YOLOv10 算法的无人机检测系统。系统支持图片、视频及摄像头实时检测,配备 PyQt5 图形界面。内容包括数据集划分与制作流程、Anaconda 环境配置、模型训练参数设置及核心代码实现。适用于空域监控与安防场景,具有高精度和实时性特点。

YOLOv10 无人机识别检测系统是一个基于 YOLOv10(You Only Look Once version 10)目标检测算法的智能系统,专门用于检测和识别无人机。该系统能够自动识别并定位无人机,适用于空域监控、无人机管理、安防监控等场景。通过该系统,用户可以实时检测无人机的存在和位置,帮助维护空域安全、防止非法无人机入侵,并为无人机管理提供技术支持。
该功能允许用户通过单张图片进行目标检测。输入一张图片后,YOLO 模型会实时分析图像,识别出其中的目标,并在图像中框出检测到的目标,输出带有目标框的图像。

视频检测功能允许用户将视频文件作为输入。YOLO 模型将逐帧分析视频,并在每一帧中标记出检测到的目标。最终结果可以是带有目标框的视频文件或实时展示,适用于视频监控和分析等场景。
该功能支持通过连接摄像头进行实时目标检测。YOLO 模型能够在摄像头拍摄的实时视频流中进行目标检测,实时识别并显示检测结果。此功能非常适用于安防监控、无人驾驶、智能交通等应用,提供即时反馈。
核心特点:
数据集名称: 无人机检测数据集
数据集类别: 1 类
类别名称: ['drone']
数据集划分:
数据集特点:
应用场景:

train: .\datasets\images\train
val: .\datasets\images\val
test: .\datasets\images\test
nc: 1
names: ['drone']
<object-class> <x_center> <y_center> <width> <height>,这些坐标是相对于图像尺寸的比例。

首先新建一个 Anaconda 环境,每个项目用不同的环境,这样项目中所用的依赖包互不干扰。
终端输入:
conda create -n yolov10 python==3.9
激活虚拟环境
conda activate yolov10
安装 cpu 版本 pytorch
pip install torch torchvision torchaudio
在 PyCharm 设置中选择 Project Interpreter,添加已创建的 Conda 环境。
pip install -r requirements.txt
from ultralytics import YOLOv10
model_path = 'yolov10s.pt'
data_path = 'datasets/data.yaml'
if __name__ == '__main__':
model = YOLOv10(model_path)
results = model.train(data=data_path, epochs=500, batch=64, device='0', workers=0, project='runs/detect', name='exp')
根据实际情况更换模型:
yolov10n.yaml (nano):轻量化模型,适合嵌入式设备,速度快但精度略低。
yolov10s.yaml (small):小模型,适合实时任务。
yolov10m.yaml (medium):中等大小模型,兼顾速度和精度。
yolov10b.yaml (base):基本版模型,适合大部分应用场景。
yolov10l.yaml (large):大型模型,适合对精度要求高的任务。
--batch 64:每批次 64 张图像。
--epochs 500:训练 500 轮。
--datasets/data.yaml:数据集配置文件。
--weights yolov10s.pt:初始化模型权重,yolov10s.pt 是预训练的轻量级 YOLO 模型。

# -*- coding: utf-8 -*-
import time
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QMessageBox, QWidget, QHeaderView, QTableWidgetItem, QAbstractItemView
import sys
import os
from PIL import ImageFont
from ultralytics import YOLOv10
sys.path.append('UIProgram')
from UIProgram.UiMain import Ui_MainWindow
import sys
from PyQt5.QtCore import QTimer, Qt, QThread, pyqtSignal, QCoreApplication
import detect_tools as tools
import cv2
import Config
from UIProgram.QssLoader import QSSLoader
from UIProgram.precess_bar import ProgressBar
import numpy as np
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(QMainWindow, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.initMain()
self.signalconnect()
# 加载 css 渲染效果
style_file = 'UIProgram/style.css'
qssStyleSheet = QSSLoader.read_qss_file(style_file)
self.setStyleSheet(qssStyleSheet)
def signalconnect():
.ui.PicBtn.clicked.connect(.open_img)
.ui.comboBox.activated.connect(.combox_change)
.ui.VideoBtn.clicked.connect(.vedio_show)
.ui.CapBtn.clicked.connect(.camera_show)
.ui.SaveBtn.clicked.connect(.save_detect_video)
.ui.ExitBtn.clicked.connect(QCoreApplication.quit)
.ui.FilesBtn.clicked.connect(.detact_batch_imgs)
():
.show_width =
.show_height =
.org_path =
.is_camera_open =
.cap =
.model = YOLOv10(, task=)
.model(np.zeros((, , )))
.fontC = ImageFont.truetype(, , )
.colors = tools.Colors()
.timer_camera = QTimer()
.ui.tableWidget.verticalHeader().setSectionResizeMode(QHeaderView.Fixed)
.ui.tableWidget.verticalHeader().setDefaultSectionSize()
.ui.tableWidget.setColumnWidth(, )
.ui.tableWidget.setColumnWidth(, )
.ui.tableWidget.setColumnWidth(, )
.ui.tableWidget.setColumnWidth(, )
.ui.tableWidget.setColumnWidth(, )
.ui.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows)
.ui.tableWidget.verticalHeader().setVisible()
.ui.tableWidget.setAlternatingRowColors()
():
.cap:
.video_stop()
.is_camera_open =
.ui.CaplineEdit.setText()
.cap =
file_path, _ = QFileDialog.getOpenFileName(, , , )
file_path:
.ui.comboBox.setDisabled()
.org_path = file_path
.org_img = tools.img_cvread(.org_path)
t1 = time.time()
.results = .model(.org_path)[]
t2 = time.time()
take_time_str = .(t2 - t1)
.ui.time_lb.setText(take_time_str)
location_list = .results.boxes.xyxy.tolist()
.location_list = [((, e)) e location_list]
cls_list = .results.boxes.cls.tolist()
.cls_list = [(i) i cls_list]
.conf_list = .results.boxes.conf.tolist()
.conf_list = [ % (each*) each .conf_list]
total_nums = (location_list)
cls_percents = []
i ():
total_nums == :
res =
:
res = .cls_list.count(i) / total_nums
cls_percents.append(res)
.set_percent(cls_percents)
now_img = .results.plot()
.draw_img = now_img
.img_width, .img_height = .get_resize_size(now_img)
resize_cvimg = cv2.resize(now_img, (.img_width, .img_height))
pix_img = tools.cvimg_to_qpiximg(resize_cvimg)
.ui.label_show.setPixmap(pix_img)
.ui.label_show.setAlignment(Qt.AlignCenter)
.ui.PiclineEdit.setText(.org_path)
target_nums = (.cls_list)
.ui.label_nums.setText((target_nums))
choose_list = []
target_names = [Config.names[] + + (index) index, (.cls_list)]
choose_list = choose_list + target_names
.ui.comboBox.clear()
.ui.comboBox.addItems(choose_list)
target_nums >= :
.ui.type_lb.setText(Config.CH_names[.cls_list[]])
.ui.label_conf.setText((.conf_list[]))
.ui.label_xmin.setText((.location_list[][]))
.ui.label_ymin.setText((.location_list[][]))
.ui.label_xmax.setText((.location_list[][]))
.ui.label_ymax.setText((.location_list[][]))
:
.ui.type_lb.setText()
.ui.label_conf.setText()
.ui.label_xmin.setText()
.ui.label_ymin.setText()
.ui.label_xmax.setText()
.ui.label_ymax.setText()
.ui.tableWidget.setRowCount()
.ui.tableWidget.clearContents()
.tabel_info_show(.location_list, .cls_list, .conf_list, path=.org_path)
():
.cap:
.video_stop()
.is_camera_open =
.ui.CaplineEdit.setText()
.cap =
directory = QFileDialog.getExistingDirectory(, , )
directory:
.org_path = directory
img_suffix = [,,,]
file_name os.listdir(directory):
full_path = os.path.join(directory, file_name)
os.path.isfile(full_path) file_name.split()[-].lower() img_suffix:
img_path = full_path
.org_img = tools.img_cvread(img_path)
t1 = time.time()
.results = .model(img_path)[]
t2 = time.time()
take_time_str = .(t2 - t1)
.ui.time_lb.setText(take_time_str)
location_list = .results.boxes.xyxy.tolist()
.location_list = [((, e)) e location_list]
cls_list = .results.boxes.cls.tolist()
.cls_list = [(i) i cls_list]
.conf_list = .results.boxes.conf.tolist()
.conf_list = [ % (each * ) each .conf_list]
total_nums = (location_list)
cls_percents = []
i ():
total_nums == :
res =
:
res = .cls_list.count(i) / total_nums
cls_percents.append(res)
.set_percent(cls_percents)
now_img = .results.plot()
.draw_img = now_img
.img_width, .img_height = .get_resize_size(now_img)
resize_cvimg = cv2.resize(now_img, (.img_width, .img_height))
pix_img = tools.cvimg_to_qpiximg(resize_cvimg)
.ui.label_show.setPixmap(pix_img)
.ui.label_show.setAlignment(Qt.AlignCenter)
.ui.PiclineEdit.setText(img_path)
target_nums = (.cls_list)
.ui.label_nums.setText((target_nums))
choose_list = []
target_names = [Config.names[] + + (index) index, (.cls_list)]
choose_list = choose_list + target_names
.ui.comboBox.clear()
.ui.comboBox.addItems(choose_list)
target_nums >= :
.ui.type_lb.setText(Config.CH_names[.cls_list[]])
.ui.label_conf.setText((.conf_list[]))
.ui.label_xmin.setText((.location_list[][]))
.ui.label_ymin.setText((.location_list[][]))
.ui.label_xmax.setText((.location_list[][]))
.ui.label_ymax.setText((.location_list[][]))
:
.ui.type_lb.setText()
.ui.label_conf.setText()
.ui.label_xmin.setText()
.ui.label_ymin.setText()
.ui.label_xmax.setText()
.ui.label_ymax.setText()
.tabel_info_show(.location_list, .cls_list, .conf_list, path=img_path)
.ui.tableWidget.scrollToBottom()
QApplication.processEvents()
():
now_img = img.copy()
location_list = results.boxes.xyxy.tolist()
.location_list = [((, e)) e location_list]
cls_list = results.boxes.cls.tolist()
.cls_list = [(i) i cls_list]
.conf_list = results.boxes.conf.tolist()
.conf_list = [ % (each * ) each .conf_list]
loacation, type_id, conf (.location_list, .cls_list, .conf_list):
type_id = (type_id)
color = .colors((type_id), )
now_img = tools.drawRectBox(now_img, loacation, Config.CH_names[type_id], .fontC, color)
.img_width, .img_height = .get_resize_size(now_img)
resize_cvimg = cv2.resize(now_img, (.img_width, .img_height))
pix_img = tools.cvimg_to_qpiximg(resize_cvimg)
.ui.label_show.setPixmap(pix_img)
.ui.label_show.setAlignment(Qt.AlignCenter)
.ui.PiclineEdit.setText(.org_path)
target_nums = (.cls_list)
.ui.label_nums.setText((target_nums))
target_nums >= :
.ui.type_lb.setText(Config.CH_names[.cls_list[]])
.ui.label_conf.setText((.conf_list[]))
.ui.label_xmin.setText((.location_list[][]))
.ui.label_ymin.setText((.location_list[][]))
.ui.label_xmax.setText((.location_list[][]))
.ui.label_ymax.setText((.location_list[][]))
:
.ui.type_lb.setText()
.ui.label_conf.setText()
.ui.label_xmin.setText()
.ui.label_ymin.setText()
.ui.label_xmax.setText()
.ui.label_ymax.setText()
.ui.tableWidget.setRowCount()
.ui.tableWidget.clearContents()
.tabel_info_show(.location_list, .cls_list, .conf_list, path=.org_path)
now_img
():
com_text = .ui.comboBox.currentText()
com_text == :
cur_box = .location_list
cur_img = .results.plot()
.ui.type_lb.setText(Config.CH_names[.cls_list[]])
.ui.label_conf.setText((.conf_list[]))
:
index = (com_text.split()[-])
cur_box = [.location_list[index]]
cur_img = .results[index].plot()
.ui.type_lb.setText(Config.CH_names[.cls_list[index]])
.ui.label_conf.setText((.conf_list[index]))
.ui.label_xmin.setText((cur_box[][]))
.ui.label_ymin.setText((cur_box[][]))
.ui.label_xmax.setText((cur_box[][]))
.ui.label_ymax.setText((cur_box[][]))
resize_cvimg = cv2.resize(cur_img, (.img_width, .img_height))
pix_img = tools.cvimg_to_qpiximg(resize_cvimg)
.ui.label_show.clear()
.ui.label_show.setPixmap(pix_img)
.ui.label_show.setAlignment(Qt.AlignCenter)
():
file_path, _ = QFileDialog.getOpenFileName(, , , )
file_path:
.org_path = file_path
.ui.VideolineEdit.setText(file_path)
file_path
():
.ui.tableWidget.setRowCount()
.ui.tableWidget.clearContents()
.ui.comboBox.clear()
.timer_camera.start()
.timer_camera.timeout.connect(.open_frame)
():
path = path
location, cls, conf (locations, clses, confs):
row_count = .ui.tableWidget.rowCount()
.ui.tableWidget.insertRow(row_count)
item_id = QTableWidgetItem((row_count+))
item_id.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
item_path = QTableWidgetItem((path))
item_cls = QTableWidgetItem((Config.CH_names[cls]))
item_cls.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
item_conf = QTableWidgetItem((conf))
item_conf.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
item_location = QTableWidgetItem((location))
.ui.tableWidget.setItem(row_count, , item_id)
.ui.tableWidget.setItem(row_count, , item_path)
.ui.tableWidget.setItem(row_count, , item_cls)
.ui.tableWidget.setItem(row_count, , item_conf)
.ui.tableWidget.setItem(row_count, , item_location)
.ui.tableWidget.scrollToBottom()
():
.cap.release()
.timer_camera.stop()
():
ret, now_img = .cap.read()
ret:
t1 = time.time()
results = .model(now_img)[]
t2 = time.time()
take_time_str = .(t2 - t1)
.ui.time_lb.setText(take_time_str)
location_list = results.boxes.xyxy.tolist()
.location_list = [((, e)) e location_list]
cls_list = results.boxes.cls.tolist()
.cls_list = [(i) i cls_list]
.conf_list = results.boxes.conf.tolist()
.conf_list = [ % (each * ) each .conf_list]
total_nums = (location_list)
cls_percents = []
i ():
total_nums != :
res = .cls_list.count(i) / total_nums
:
res =
cls_percents.append(res)
.set_percent(cls_percents)
now_img = results.plot()
.img_width, .img_height = .get_resize_size(now_img)
resize_cvimg = cv2.resize(now_img, (.img_width, .img_height))
pix_img = tools.cvimg_to_qpiximg(resize_cvimg)
.ui.label_show.setPixmap(pix_img)
.ui.label_show.setAlignment(Qt.AlignCenter)
target_nums = (.cls_list)
.ui.label_nums.setText((target_nums))
choose_list = []
target_names = [Config.names[] + + (index) index, (.cls_list)]
choose_list = choose_list + target_names
.ui.comboBox.clear()
.ui.comboBox.addItems(choose_list)
target_nums >= :
.ui.type_lb.setText(Config.CH_names[.cls_list[]])
.ui.label_conf.setText((.conf_list[]))
.ui.label_xmin.setText((.location_list[][]))
.ui.label_ymin.setText((.location_list[][]))
.ui.label_xmax.setText((.location_list[][]))
.ui.label_ymax.setText((.location_list[][]))
:
.ui.type_lb.setText()
.ui.label_conf.setText()
.ui.label_xmin.setText()
.ui.label_ymin.setText()
.ui.label_xmax.setText()
.ui.label_ymax.setText()
.tabel_info_show(.location_list, .cls_list, .conf_list, path=.org_path)
:
.cap.release()
.timer_camera.stop()
():
.is_camera_open:
.is_camera_open =
.ui.CaplineEdit.setText()
video_path = .get_video_path()
video_path:
.cap = cv2.VideoCapture(video_path)
.video_start()
.ui.comboBox.setDisabled()
():
.is_camera_open = .is_camera_open
.is_camera_open:
.ui.CaplineEdit.setText()
.cap = cv2.VideoCapture()
.video_start()
.ui.comboBox.setDisabled()
:
.ui.CaplineEdit.setText()
.ui.label_show.setText()
.cap:
.cap.release()
cv2.destroyAllWindows()
.ui.label_show.clear()
():
_img = img.copy()
img_height, img_width, depth = _img.shape
ratio = img_width / img_height
ratio >= .show_width / .show_height:
.img_width = .show_width
.img_height = (.img_width / ratio)
:
.img_height = .show_height
.img_width = (.img_height * ratio)
.img_width, .img_height
():
.cap .org_path:
QMessageBox.about(, , )
.is_camera_open:
QMessageBox.about(, , )
.cap:
res = QMessageBox.information(, , , QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
res == QMessageBox.Yes:
.video_stop()
com_text = .ui.comboBox.currentText()
.btn2Thread_object = btn2Thread(.org_path, .model, com_text)
.btn2Thread_object.start()
.btn2Thread_object.update_ui_signal.connect(.update_process_bar)
:
:
os.path.isfile(.org_path):
fileName = os.path.basename(.org_path)
name, end_name = fileName.rsplit(, )
save_name = name + + end_name
save_img_path = os.path.join(Config.save_path, save_name)
cv2.imwrite(save_img_path, .draw_img)
QMessageBox.about(, , .(save_img_path))
:
img_suffix = [, , , ]
file_name os.listdir(.org_path):
full_path = os.path.join(.org_path, file_name)
os.path.isfile(full_path) file_name.split()[-].lower() img_suffix:
name, end_name = file_name.rsplit(, )
save_name = name + + end_name
save_img_path = os.path.join(Config.save_path, save_name)
results = .model(full_path)[]
now_img = results.plot()
cv2.imwrite(save_img_path, now_img)
QMessageBox.about(, , .(Config.save_path))
():
cur_num == :
.progress_bar = ProgressBar()
.progress_bar.show()
cur_num >= total:
.progress_bar.close()
QMessageBox.about(, , .(Config.save_path))
.progress_bar.isVisible() :
.btn2Thread_object.stop()
value = (cur_num / total * )
.progress_bar.setValue(cur_num, total, value)
QApplication.processEvents()
():
():
update_ui_signal = pyqtSignal(, )
():
(btn2Thread, ).__init__()
.org_path = path
.model = model
.com_text = com_text
.colors = tools.Colors()
.is_running =
():
cap = cv2.VideoCapture(.org_path)
fourcc = cv2.VideoWriter_fourcc(*)
fps = cap.get(cv2.CAP_PROP_FPS)
size = ((cap.get(cv2.CAP_PROP_FRAME_WIDTH)), (cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
fileName = os.path.basename(.org_path)
name, end_name = fileName.split()
save_name = name +
save_video_path = os.path.join(Config.save_path, save_name)
out = cv2.VideoWriter(save_video_path, fourcc, fps, size)
prop = cv2.CAP_PROP_FRAME_COUNT
total = (cap.get(prop))
(.(total))
cur_num =
(cap.isOpened() .is_running):
cur_num +=
(.(cur_num, total))
ret, frame = cap.read()
ret == :
results = .model(frame)[]
frame = results.plot()
out.write(frame)
.update_ui_signal.emit(cur_num, total)
:
cap.release()
out.release()
():
.is_running =
__name__ == :
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online