
简介一套基于YOLO11的边坡护坡滑坡检测系统面向计算机、人工智能、自动化等专业的学生、教师及开发者可满足课程设计、毕业设计或实际项目演示需求。系统已封装PyQt5图形界面开箱即用配置好环境即可快速上手。内含6600多张已标注滑坡样本、训练好的模型、评估指标曲线、安装使用教程及演示图片视频从数据准备到推理展示链路完整另附Python源码与配置文件便于二次改造。压缩包共2000个文件含txt标注/说明、yaml配置、py脚本及少量xml文件整体约929.18MB。已有682人学习下载适合初学进阶或直接用于课题落地。1. 滑坡检测不等同于普通目标检测为什么选择YOLO11PyQt5这套组合第一次跑通这套基于YOLO11的边坡滑坡检测系统时我最大的感触是“终于不用拿肉眼盯着一帧帧山体图像看了”。传统边坡监测主要靠位移传感器和人工巡检传感器布点有限人工巡检又滞后而用深度学习做图像检测可以把无人机巡检、固定摄像头、甚至手机拍的山坡照片统一丢进同一个识别流程。标题里这套系统把YOLO11检测模型、PyQt5图形界面、6600多张已标注的边坡数据集和训练好的权重打包在一起目标就是开箱即用——你不需要从零训练模型装好Python环境就能在界面上传图片或视频实时看到边坡、护坡、滑坡区域的位置和置信度。适合做地质灾害监测、土木工程巡检、智慧矿山边坡管理以及想拿目标检测练手的工程师和研究生。2. YOLO11检测原理与数据集构成先在边界框里理解边坡滑坡2.1 为什么是YOLO11而不是更早的版本网络结构上的实际差异YOLO11是Ultralytics推出的新一代目标检测模型它的整体思路仍然是“一次前向传播同时预测类别和边界框”但在网络结构上和YOLOv8有几个关键区别。最明显的是C3k2模块替代了原来的C2f模块C3k2保留了CSP的梯度分流思想同时减少了部分冗余计算检测头继续采用anchor-free设计也就是直接回归中心点和宽高不再依赖预设锚框。对边坡场景来说这个变化最直接的影响是训练更稳定尤其是当数据集里存在大量尺度差异大的目标时anchor-free头对中心点的回归比anchor匹配更省心。另一个值得关注的点是YOLO11在颈部网络的特征融合上做了优化。P2层的使用比之前版本更灵活这让小目标检测能力有了一些提升。滑坡检测里有一个很现实的痛点远处的山体滑坡可能只占几十个像素近处的护坡破损却可能占满整个画面。如果网络不能同时覆盖这种尺度跨度要么漏掉远处的小型滑坡要么把近处的大块背景误报成滑坡。YOLO11的SPPF模块和PAN-FPN结构在这一点上并没有完全解决尺度问题但它的C3k2和轻量检测头让模型在相同算力下可以用更大的输入分辨率这反而比单模块改进更有效。选型上还有一个实际理由生态兼容。YOLO11和YOLOv8一样由Ultralytics维护训练命令、权重格式、predict和train接口几乎一致。这意味着你之前写过的数据处理脚本、评估指标代码甚至PyQt5界面里的推理函数只需要改模型路径就能跑起来。对于“开箱即用”的交付场景这种兼容性比网络结构带来的精度提升更值钱。当然如果未来要接入SAM3做像素级分割YOLO11输出的边界框也可以直接作为分割的提示框检测和分割可以串成一条流水线但那是后话。2.2 滑坡检测的类别定义与标注粒度一个滑坡检测系统能识别什么完全取决于标注数据里的人为定义。常见的做法是将目标分成三类裸露滑坡体、已做过工程防护的护坡、以及疑似风险的边坡区域。也有的数据集会把“裂缝”单独作为一类因为细微裂缝是滑坡的前兆特征。但类别过多对检测模型不友好尤其是6600多张图如果每类样本很不均衡模型容易偏向样本多的类别。我一般建议在拿到数据集后先用脚本统计每个类别的实例数量而不是直接开训。标注粒度是另一个容易被忽略的点。边界框应该覆盖整个滑动区域包括上方的错台和后缘裂缝而不是只框住中间最明显的泥土部分。如果标注得太碎模型会学到“滑坡是一块一块的”在推理时就会把一个连续滑坡切出好几个框。标注得太松也不行把大量非滑动区域包进框里会让模型的背景区分能力下降。一个相对可靠的标准是框沿滑坡体边缘收缩约1%到2%的边距让框尽量贴合可见的滑动边界。YOLO格式的标注是每个图片对应一个txt文件每行内容是类别编号 x_center y_center width height其中坐标都是相对于图片宽高归一化到0-1之间的浮点数。比如一个滑坡框在图片正中央大约占图片一半宽度和三分之一高度对应的行可能是0 0.5 0.5 0.5 0.333。读取这个格式不需要额外库纯文本解析就能做数据检查。2.3 6600多张数据集的目录划分与标签校验标题里的6600多张标注数据在实际项目中已经属于中等偏上的规模。对于滑坡检测这种背景复杂、目标形态不规则的任务数据复杂度比单纯数量更重要。拿到数据集后我第一件事不是看图片而是看目录结构是否符合YOLO约定datasets/images/train、datasets/images/val、datasets/labels/train、datasets/labels/val并且图片和标签文件名一一对应。如果源数据用的是VOC格式或COCO格式通常还需要做一次格式转换。下面这个脚本可以用来统计标签类别和框尺寸分布我在接手不少标注数据集时都靠它先摸清底细import os from collections import Counter label_dir datasets/labels/train stats Counter() small_boxes 0 total_boxes 0 for name in os.listdir(label_dir): if not name.endswith(.txt): continue with open(os.path.join(label_dir, name), r) as fp: for line in fp: parts line.split() if len(parts) 5: continue cls, w, h int(parts[0]), float(parts[3]), float(parts[4]) stats[cls] 1 total_boxes 1 if w * h 0.001: # 归一化后面积小于0.1%的图片 small_boxes 1 print(各类别数量:, dict(stats)) print(小目标占比: {:.2f}%.format(100.0 * small_boxes / total_boxes))这段代码逐行解析每个txt标签parts[0]是类别编号parts[3]和parts[4]分别是归一化后的框宽和高。框面积等于宽乘高如果大量框的面积小于0.001说明小目标占比高训练时就需要考虑提高输入分辨率或者开启马赛克增强。标签文件名不匹配的问题脚本里没检查但你可以把images和labels目录下的文件名做一次集合比对补上缺少标签的图片。数据划分上6600多张图建议按9:1切训练集和验证集不需要单独再留测试集。验证集要保持和训练集来源一致否则会出现“域漂移”比如训练集全是无人机俯拍验证集却是手机平视拍摄mAP再高也不能说明模型真实能力。3. 环境配置与开箱推理从Python安装到GUI界面弹出第一张检测图3.1 为了避免PyQt5装不上先把Python环境隔离“开箱即用”最常翻车的环节就是环境。很多用户拿到源码包后直接pip install requirements.txt装到一半PyQt5编译报错或者装完后import直接段错误。这多半不是源码问题而是系统Python环境里已经有其他Qt相关的包在打架。我的一般做法是强制使用虚拟环境并固定Python版本为3.10。conda create -n slope python3.10 -y conda activate slope pip install ultralytics pyqt5 opencv-python pillow用3.10而不是3.12的原因很现实PyQt5和onnxruntime这些包在3.10下的预编译wheel最全Python3.12也能装PyQt5但部分依赖lapack、sip的包容易出现兼容问题。如果你没有conda用python -m venv slope创建虚拟环境是同样的作用。注意Windows系统下安装完成后如果启动GUI报could not find or load the Qt platform plugin windows通常是PyQt5的platforms目录没有正确拷贝或者环境变量里存在多个Qt路径。最简单的处理是把虚拟环境里的site-packages/PyQt5/Qt5/plugins路径手动加入到QT_QPA_PLATFORM_PLUGIN_PATH环境变量里。这里我建议装完先跑一个最简导入测试python -c from PyQt5.QtWidgets import QApplication; print(PyQt5 ok) python -c from ultralytics import YOLO; print(YOLO11 ok)两条命令都通过再往下走。这一条检查能提前挡掉一半的“界面闪退”问题。3.2 先用训练好的模型跑一张图确认模型文件是好的在双击GUI界面之前先做一次命令行推理。这一步的目的很纯粹确认best.pt权重在当前环境里能正常加载和预测。如果这里都失败说明环境或权重有问题没必要去界面里排查。yolo detect predict modelweights/best.pt sourcesample_images/landslide.jpg conf0.25 device0命令里的model指定权重文件source指定图片路径conf是置信度阈值只保留置信度大于0.25的检测框device0表示使用第一张GPU卡如果没有GPU就改成devicecpu。输出结果会保存在runs/detect/predict目录下生成一张画有边界框的图片。如果想在Python里做同样的事方便后面接进GUI用这样一段代码from ultralytics import YOLO model YOLO(weights/best.pt) results model.predict(sample_images/landslide.jpg, conf0.25) for r in results: r.save() # 保存结果图片 print(r.boxes.cls) # 检测到的类别编号 print(r.boxes.conf) # 检测到的置信度r.save()把带框图片保存到默认输出目录r.boxes.cls是预测出的类别张量r.boxes.conf是对应置信度。如果这里打印出空列表说明模型没有检测到目标可能是置信度阈值太高也可能是输入图片本身没有滑坡。我习惯把conf调低到0.1再跑一次如果低阈值下出现框说明模型本身没问题只是原始阈值设太严。3.3 启动PyQt5界面从源码到窗口的完整流程模型能跑了再来看GUI。标题里“带Pyqt5界面”指的是用Python和PyQt5写成的图形操作界面。源码包里通常会有一个主入口文件比如main.py或run.py运行方式就是python main.py界面本身并不神奇本质上就是把前面那段推理代码挂到按钮事件里。给你一个最小的PyQt5窗口骨架能帮助理解源码包里的界面结构import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton, QVBoxLayout, QWidget class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(边坡滑坡检测系统) self.label QLabel(选择图片后显示检测结果) self.button QPushButton(打开图片) layout QVBoxLayout() layout.addWidget(self.label) layout.addWidget(self.button) container QWidget() container.setLayout(layout) self.setCentralWidget(container) if __name__ __main__: app QApplication(sys.argv) win MainWindow() win.show() sys.exit(app.exec_())这段代码只创建了一个标签和一个按钮实际系统里按钮的回调函数会调用模型推理并把画好框的图片转成QImage显示在标签上。在这里QApplication(sys.argv)是每个PyQt5程序的入口sys.exit(app.exec_())进入事件循环窗口才能保持响应。在PyCharm里调试这种界面时别用“运行”按钮直接跑最好在Python Console里执行否则有时会碰到Qt窗口无法嵌入IDE控制台的怪问题。如果你拿到源码后发现界面文件是.ui文件说明界面是用Qt Designer设计的常见做法是用pyuic5命令把.ui转成.py文件再调用。.ui是xml格式不能直接运行pyuic5 -o ui_main.py main.ui转换后才是一个Python模块。4. 训练你自己的滑坡检测模型数据、参数与评估曲线4.1 标注数据格式检查YOLO txt、类别编号和损坏图片开箱用现成模型是一回事想训自己的模型就是另一回事。训练前必须确认数据集的完整性和一致性。下面这个脚本负责检查三件事图片是否损坏、每张图片是否有对应标签、标签中的类别编号是否在合法范围内。import os from PIL import Image img_dir datasets/images/train label_dir datasets/labels/train num_classes 3 # 根据你的类别数量修改 problems [] for name in os.listdir(img_dir): stem os.path.splitext(name)[0] img_path os.path.join(img_dir, name) label_path os.path.join(label_dir, stem .txt) if not os.path.exists(label_path): problems.append(f缺少标签: {name}) continue try: Image.open(img_path).verify() except Exception: problems.append(f损坏图片: {name}) with open(label_path, r) as fp: for line in fp: parts line.split() if not parts: continue cls int(parts[0]) if cls 0 or cls num_classes: problems.append(f{name} 类别越界: {cls}) if problems: print(发现{}个问题:.format(len(problems))) for p in problems[:20]: print(p) else: print(数据检查通过)这段代码用PIL.Image.open().verify()快速验证图片文件是否可解码避免训练中途因为坏图中断。类别越界检查很关键YOLO标签要求类别编号从0开始如果数据集中有类编号是3而你的data.yaml只定义了3类编号0、1、2训练会直接报错或跳过该标签。检查时把num_classes改成你的实际类别数。4.2 写data.yaml与训练命令关键的几个参数取舍训练前需要准备一个YOLO格式的数据描述文件通常叫slope.yaml内容大致如下path: datasets train: images/train val: images/val names: 0: landslide 1: slope 2: protectionpath是数据集根目录train和val是图片子目录的相对路径names是类别名列表。注意类别编号必须和标注txt里的编号一致顺序不对会导致整个训练无效。训练命令推荐这样写yolo detect train dataslope.yaml modelyolo11s.pt epochs120 imgsz640 batch16 device0 patience20几个参数的实际作用我解释一下。epochs120指训练120轮6600多张图配合常规增强120轮足够让模型收敛到稳定水平如果你发现模型还在缓慢提升可以加到200轮。imgsz640是输入分辨率这是速度和精度的平衡点。如果显存足够且小目标较多设为768或896效果一般会更好但训练时间也成倍增加。batch16是每轮喂给GPU的图片数量显存不够就降到8或4千万别硬撑。patience20是早停参数连续20轮mAP不再提升就自动停止能省不少时间。使用预训练权重yolo11s.pt是推荐做法它会从COCO数据集的先验知识开始微调比从随机权重开始收敛更快。如果你想从零训练可以把modelyolo11s.pt改成modelyolo11s.yaml但那样需要更长训练时间数据量不够时精度反而不如微调。4.3 评估指标曲线mAP、PR曲线、混淆矩阵怎么读训练完成后runs/detect/train目录下会生成一组评估文件。results.png里包含训练损失的下降曲线、验证集的mAP50和mAP50-95曲线以及精确率和召回率曲线。confusion_matrix.png则是所有预测结果的混淆矩阵横轴是真实类别纵轴是预测类别最后一列和最后一行分别是背景误检和漏检。对滑坡检测来说我会重点看两个指标mAP50和背景误检率。mAP50指的是IoU阈值为0.5时的平均精度滑坡检测中它最好大于0.8mAP50-95是更严格的指标IoU从0.5到0.95逐步计算再取平均一般来说大于0.6就算实用。背景误检则要看混淆矩阵里第一行非对角线上的值如果很多“滑坡”被判成“护坡”说明这两个类别外观太接近需要检查标注是否有偏差。如果发现mAP高但实际推理时误检多尤其是把道路或河滩识别成滑坡常见原因是负样本缺少。可以额外收集一批不包含滑坡的山坡、林地、建筑图片把它们作为纯背景加到训练集里但每张图不需要标签。这样能强迫模型学到“这是背景”而不是“框里有东西就报出来”。5. 避坑手册YOLO11PyQt5落地跑路前要检查的6个问题5.1 PyQt5安装失败或启动闪退软件包冲突是最常见元凶现象pip install pyqt5装完后运行程序窗口闪一下就没了或者报Aborted (core dumped)。原因系统环境里同时存在PySide2、PyQt5或其他Qt绑定两个库都注册了Qt插件导致加载平台插件时冲突。另一个常见原因是Python版本太高PyQt5没有对应wheel。解决新建干净虚拟环境Python固定为3.10一次性安装pyqt5和ultralytics不要单独装PySide。如果之前装过labelme这类自带Qt的平台用conda activate slope进入新环境再运行。5.2 OpenCV读取中文路径图片直接返回None现象界面点击打开图片明明路径是对的但图片区一片空白后台打印数组是None。原因OpenCV的imread在Windows上对中文路径支持不完整路径里含“边坡”、“检测”等中文时直接读取失败。解决不用cv2.imread改用numpy.fromfile配合cv2.imdecodeimport cv2 import numpy as np def imread_chinese(path): data np.fromfile(path, dtypenp.uint8) return cv2.imdecode(data, cv2.IMREAD_COLOR)np.fromfile把文件读成二进制字节流这一步不涉及路径编码解析imdecode再从字节流解码图片。在GUI代码里把所有的图片读取都换到这个函数中文路径问题就消失了。5.3 模型推理时检测框空白权重文件加载错误或阈值太高现象模型能加载图片能打开但结果区域一个框也没有命令行推理却能有框。原因GUI代码里加载的权重路径错误可能指向了训练中断的last.pt而不是best.pt或者界面上的置信度阈值默认设成了0.8而模型输出置信度只有0.5。解决在GUI入口处打印模型类别和一次前向结果来验证。代码里加上model YOLO(weights/best.pt) print(模型类别:, model.names)如果model.names显示{0: license plate}之类的内容说明权重根本不是滑坡模型换权重文件。如果names正确把界面置信度阈值改成0.2左右再试。5.4 训练时CUDA out of memorybatch和imgsz要一起降现象训练到第1轮就报CUDA out of memory或者跑几分钟内存溢出被杀。原因batch16和imgsz640在较小显存的显卡上直接超出显存尤其训练时数据增强会临时生成多个尺度的中间特征图。解决先降batch到4保持imgsz640测试如果还是不够把imgsz降为480。注意显存占用量是batch * imgsz^2成正比imgsz从640降到480显存占用大约下降44%。另外在训练时关掉界面和浏览器释放视频内存。5.5 GUI点击开始检测后界面卡死、鼠标变转圈现象界面失去响应Windows提示“未响应”。原因YOLO推理是同步阻塞的放在主线程里Qt事件循环被卡住了界面自然无法刷新。解决把推理函数丢到QThread里不要直接在按钮槽函数里调用。最简单的做法是新建一个工作线程类from PyQt5.QtCore import QThread, pyqtSignal from ultralytics import YOLO class DetectThread(QThread): finished pyqtSignal(object) def __init__(self, model_path, image_path): super().__init__() self.model_path model_path self.image_path image_path def run(self): model YOLO(self.model_path) results model.predict(self.image_path, conf0.25) self.finished.emit(results)按钮回调里self.thread.start()启动线程结果通过finished信号传回到主界面这样界面保持流畅。5.6 训练后模型在GUI里不生效忘记重新加载权重现象训练新模型后界面仍然沿用旧的检测结果新模型完全没有起作用。原因GUI程序在启动时只加载一次权重如果你在界面运行期间重新训练了模型或者把新的best.pt复制到weights/目录但GUI没有重新加载文件它依然使用内存里的旧模型。解决在界面加一个“重新加载模型”按钮回调里重新创建YOLO实例。依赖路径时用绝对路径不要用相对路径避免当前工作目录变化导致加载到不同文件。6. 进阶用QThread给GUI加视频流检测并验证模型泛化能力如果你已经把静态图片的检测跑通了下一步自然是想让界面支持摄像头或视频文件。这个需求在边坡监测里很常见因为现场更多是固定摄像头连续采集。核心思路还是用QThread但要注意视频每一帧都要经过模型推理计算量比图片大帧率不要强求太高。一个可用的视频检测线程结构大概是这样的import cv2 from PyQt5.QtCore import QThread, pyqtSignal class VideoThread(QThread): frame_out pyqtSignal(object) def __init__(self, model, video_path): super().__init__() self.model model self.video_path video_path self.running True def run(self): cap cv2.VideoCapture(self.video_path) while self.running: success, frame cap.read() if not success: break results self.model.predict(frame, conf0.25, devicecpu) annotated results[0].plot() # 画好框的BGR图像 self.frame_out.emit(annotated) cap.release()这里为了让演示稳定我把device设成了cpu避免在GUI线程外再去抢占GPU资源。results[0].plot()是Ultralytics提供的快捷方法返回已经画好框的图像。信号frame_out每传出一帧主界面就把这帧图像转换成QImage显示视频窗体才能实时刷新。加一个置信度阈值滑杆是性价比很高的体验改进。把滑杆的值传入model.predict的conf参数用户就可以在界面里调节检测灵敏度。实际使用中0.2适合普查阶段0.45适合报警阶段。低于0.2时误检框会明显变多高于0.5则会漏掉一些小滑坡。最后一定要做一次泛化验证把训练集里的图片全部放到一边换一批完全没见过的边坡图片覆盖不同光照、阴雨、晨雾、以及相机角度。这类检测系统的真正瓶颈往往不在mAP而在新场景下能不能保持稳定输出。我处理过不少场景训练集里全是晴天补光照片一阴天或起雾检测率直接掉一半。这时候最有效的补救措施是把现场的阴天、雨雾图片补充进来做二次微调哪怕一次只加一两百张效果也比继续加大训练轮次更明显。希望这套YOLO11PyQt5的组合能帮你把边坡监测从“人工看图”里解放出来。我自己的经验是先跑通现成权重再修改界面最后才考虑重新训练这个顺序踩坑最少。希望帮到你。本文还有配套的精品资源点击获取