简介面向计算机相关专业学生与开发者这是一套基于YOLOv8的工地深基坑变形监测完整项目。内含可直接运行的Python源码、可视化界面、标注数据集与部署教程覆盖模型训练、视频检测与界面展示等环节可输出混淆矩阵、F1曲线、PR曲线、验证集预测结果及标签分布图适合用于毕设、课程设计或初期项目演示。资源包共8个文件压缩包15.91MB其中3个py脚本分别对应可视化界面、模型训练与视频推理3个pt权重文件包含yolov8n与yolo11n等模型2个txt文件为说明文档与部署备忘整体结构清晰便于快速上手。已有66人学习下载代码经测试运行成功下载后按README说明即可复现适合希望直接借鉴完整流程的计算机视觉学习者。1. 基坑监测为什么要用YOLOv8给“面监测”补上一块拼图我见过不少基坑项目测斜数据曲线平稳但腰梁上的裂缝已经明显张开了一条口子——因为测点不在裂缝边上。深基坑变形监测如果只看测斜仪、沉降标和水位计你监测到的只是整面基坑护壁上的几个点面上的变化全凭人工巡检。基于YOLOv8的工地深基坑变形监测思路就是把摄像头对准坑壁、支撑梁和坑边堆载区用目标检测持续识别裂缝、渗漏水、混凝土剥落、违规堆载这四类表观异常。它不是去测毫米级位移那个交给传感器它管的是传感器测不到、巡检又容易漏的视觉异常。对毕设或课程设计来说这个方向链路完整数据集可以自己拍训练和可视化界面有成熟套路部署教程也透明答辩时能讲清楚“解决什么问题、怎么解决、效果如何验证”。2. 深基坑变形监测的数据集四类目标、标注规范与样本平衡2.1 视觉能测什么裂缝、渗漏水、剥落与堆载先定边界YOLOv8是目标检测模型不是变形测量仪。它能做的是“表观异常检测”不是给你一个毫米级的沉降曲线。根据基坑现场能稳定拍到、又能用矩形框表达的目标我一般固定成四类类别名直接用英文后面训练时才不会遇到中文标签乱码。类别名现场现象典型位置标注要点crack混凝土表面线状裂缝支护桩、腰梁、截水沟框整段裂缝细裂缝也要框框内可以有空隙leakage渗漏水迹、水渍桩间土、连续墙接缝框水痕整体不要只框滴水点spalling混凝土剥落掉块支撑梁、冠梁边角剥落区域外扩一点不要只框深坑stack基坑边缘堆土堆料坑边安全距离以内框堆体整体不要拆成小框这四类不是拍脑袋定的。测斜、水位计管“里”视觉管“表”stack则是人为风险摄像头最容易抓到证据。从检测难度看stack最好做leakage最难做因为阴影和水渍的颜色分布太接近YOLOv8很容易把阴影误报成渗漏水。对毕设而言这个难度梯度反而是好事写结论时能拿出“stack类mAP高、leakage类需要更多数据”这样具体的分析比笼统说“模型准确率95%”有价值得多。2.2 用Labelme做YOLO格式数据集转换脚本与标注规范现场照片要用Labelme画框画完保存的是JSON文件。YOLOv8训练要的是TXT标注所以中间需要一步格式转换。我一般直接写脚本批量处理不用Labelme自带的导出功能因为批处理时能看到每张图的坐标范围是否合理。# labelme转yolojson - txt import json import os from glob import glob class_names [crack, leakage, spalling, stack] # 顺序决定类别id def convert(json_path, output_dir): with open(json_path, r, encodingutf-8) as f: data json.load(f) width data[imageWidth] height data[imageHeight] base os.path.splitext(os.path.basename(json_path))[0] with open(os.path.join(output_dir, base .txt), w) as out: for shape in data[shapes]: label shape[label] if label not in class_names: continue points shape[points] # 矩形两个点或自由多边形多个点 x_min min(p[0] for p in points) y_min min(p[1] for p in points) x_max max(p[0] for p in points) y_max max(p[1] for p in points) # yolo格式类别id 中心x 中心y 宽 高全部归一化 x_center (x_min x_max) / 2.0 / width y_center (y_min y_max) / 2.0 / height w (x_max - x_min) / width h (y_max - y_min) / height out.write(f{class_names.index(label)} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n) if __name__ __main__: os.makedirs(labels/train, exist_okTrue) for json_path in glob(labelme_json/train/*.json): convert(json_path, labels/train)这个脚本的核心是归一化。YOLOv8的坐标都是0到1之间的比例值不是像素值直接拿像素坐标训练会出大问题。另一个细节是shape里的points如果标注时画的是矩形只有两个点如果用了多边形可能有七八个点所以代码里取所有点的min/max这样两种标注方式都能兼容。配套的data.yaml长这样注意train和val的路径指向图片目录而TXT标注要放在与图片同名的目录下。ultralytics会自动去找“图片同路径下同名的txt文件”所以目录结构要提前摆好。# data.yaml 示例 train: images/train val: images/val nc: 4 names: [crack, leakage, spalling, stack]标注规范里最容易翻车的一点同类目标重叠时一定分开框不要一个大框套住两个裂缝。检测模型学的是“每个框里有一个完整目标”你把两个裂缝框成一个框模型预测时就会拼命找“一整根长裂缝”反而漏掉单根短裂缝。另外裂缝即使很细也要框住整段不要为了省事只框最粗的一段——训练数据里框的形态方差越大模型泛化越稳。2.3 数据增强与样本失衡Mosaic不是保险箱做完标注要分训练集和验证集常见做法是8:1:1但这里有个容易忽略的坑数据泄漏。同一天同一机位连拍的照片一张进train、一张进val模型在val上会“见过”几乎一样的画面验证指标虚高。正确做法是按采集批次划分比如周一拍的都进train周二拍的都进val这样val里是模型没见过的现场条件。做监测项目时这个数据泄漏比任何参数都更容易让指标失真。数据增强方面YOLOv8默认开Mosaic。但裂缝是细条目标Mosaic拼接时很容易把裂缝切成两段标签落在拼缝上等于给模型喂错误样本。我的习惯是第一阶段开Mosaic跑第二阶段载入上一轮的best.pt把Mosaic降到0.2再微调几个epoch专门纠正拼接造成的定位抖动。如果嫌麻烦全程0.4到0.5保平安不要一直开1.0。样本失衡是监测场景的家常便饭stack好拍leakage难拍一个数据集里可能stack有300个框leakage只有40个框。常见做法是先写个统计脚本数各类别框数然后对少样本类别做重采样——把漏水图片复制几份每份做不同的亮度、对比度扰动。这里注意单纯复制粘贴会导致模型过拟合那几张图复制时必须配合光度扰动。另一种做法是提高分类损失权重在train参数里设置cls1.5甚至2.0让分类分支更重视少数类但效果不如数据重采样来得直接。3. YOLOv8训练环境配置、参数选择与损失曲线3.1 Ubuntu 20.04搭建YOLOv8环境CPU版本很多课程设计和毕设的机器没有独立显卡CPU环境也能把YOLOv8n训练跑起来只是慢所以要先做冒烟测试再全量训练。Ubuntu 20.04上最常见的安装路子是用pip装ultralytics它会把依赖的PyTorch CPU版一起拉下来。# Ubuntu 20.04 CPU环境安装 sudo apt update sudo apt install -y python3-pip pip install ultralytics onnxruntime # 验证环境是否正常 yolo predict modelyolov8n.pt sourcehttps://ultralytics.com/images/bus.jpg这里要说明yolo命令是ultralytics包提供的命令行入口predict时会自动下载yolov8n.pt官方预训练权重第一次运行需要联网。CPU版本安装后默认走CPUExecutionProvider不需要装CUDA。如果你机器上之前装过GPU版PyTorch又装了一个CPU版版本冲突会让人头疼我一般建议用虚拟环境隔离。CPU上训练yolov8n、640分辨率、几十张图单epoch可能要几分钟到十几分钟这取决于CPU核心数和内存带宽。所以训练前先跑冒烟测试# 冒烟测试只训练1个epoch验证数据流水线没有问题 yolo train datadata.yaml modelyolov8n.pt epochs1 imgsz640 devicecpu batch2冒烟测试能暴露大部分路径问题data.yaml路径写错、标注txt与图片对不上、类别数不匹配等。这些问题在1个epoch里就会以报错或训练中断的形式出现不要等全量训练跑了一小时才翻车。3.2 影响精度的五个参数imgsz、batch、epochs、mosaic、cls训练自己的数据集,YOLOv8并不是装上就能出好结果。下面五个参数我每次都会重新调按影响程度排序。参数我的常用值作用与原因imgsz640起步有条件上1280裂缝是小目标分辨率不够直接糊掉CPU跑不动1280就减batchbatchCPU用2到4GPU看显存基坑图片分辨率高显存不够时降低batch不要降低imgszepochs patience150 patience20监测数据量小150轮足够收敛patience防过拟合mosaic第一段1.0第二段0.2裂缝细条目标怕拼接切断后期关掉让模型稳定收边cls1.2到1.5分类损失权重缓解leakage等少数类漏检代码里我倾向于直接写在Python脚本里而不是用yolo命令行因为参数多、方便统一管理。# train_pit.py from ultralytics import YOLO # 第一段训练开mosaic用预训练权重冷启动 model YOLO(yolov8n.pt) model.train( datadata.yaml, epochs80, imgsz640, batch8, mosaic1.0, cls1.2, devicecpu, # 无GPU就写cpu有GPU可写0 patience20, namepit_stage1 ) # 第二段训练关mosaic用stage1最佳权重微调收边 model YOLO(runs/detect/pit_stage1/weights/best.pt) model.train( datadata.yaml, epochs40, imgsz640, batch8, mosaic0.2, cls1.2, devicecpu, patience15, namepit_stage2 )这段代码的意图是分阶段训练。第一段让模型快速学到四类目标的粗轮廓Mosaic的多样性能帮助泛化第二段把Mosaic关小让预测框能稳定贴合裂缝这种细长目标。batch参数在CPU上不要贪大CPU内存带宽会拖慢速度2和8之间差别不大但内存不够时会直接OOM。模型选型上yolov8n是默认选择CPU推理快精度在交叉场景够用如果验证集mAP差5个点以上换yolov8s试一下但CPU推理帧率会明显下降。yolov8m及以上在CPU上做实时监测就比较吃力了除非你只是做离线分析。GTX 1660 Ti这类6G显存的卡跑yolov8n很轻松batch可以开到16甚至32。3.3 损失曲线怎么读从results.csv里看训练是否翻车训练结束后ultralytics会在runs/detect/xxx/目录下生成results.csv、results.png、confusion_matrix.png等。不要只截一张results.png放进论文要学会自己看曲线判断模型状态。YOLOv8有三个损失box_loss、cls_loss、dfl_loss分别管定位、分类和边框回归。我习惯把train和val的cls_loss单独拉出来画一张图判断标准很简单train_loss一直降、val_loss降不下去或者反弹就是过拟合两条线都锯齿状震荡说明batch太小或Mosaic在后期搞乱标签。# 画损失曲线 import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/pit_stage2/results.csv) plt.figure(figsize(10, 4)) plt.plot(df[epoch], df[train/cls_loss], labeltrain_cls) plt.plot(df[epoch], df[val/cls_loss], labelval_cls) plt.xlabel(epoch) plt.ylabel(cls_loss) plt.title(Train/Val Classification Loss) plt.legend() plt.grid(True) plt.savefig(loss_curve.png, dpi200)读曲线时注意val_loss不是越低越好要看它和train_loss之间的距离。如果val_cls_loss在第60个epoch开始往上翘而train_cls_loss还在往下走说明模型开始死记训练图。这时候不要继续加epoch而是去看patience有没有触发没触发就手动提前结束。更关键的是看confusion_matrix.png。基坑场景里leakage和spalling容易互相混淆混淆矩阵能告诉你错误具体发生在哪两个类之间。还有一个容易被忽略的指标PR曲线在低置信度时的表现。如果precision在0.8就断崖下跌说明模型误报多如果recall到0.7就上不去说明漏检多。工程上我只看IoU0.5的mAP不用去追mAP95——基坑目标的框本身就不需要精确到像素级框住裂缝位置才是核心。4. 可视化界面PyQt5 OnnxRuntime搭建监测面板4.1 界面布局与模块拆分标题里说“可视化界面”拿到源码包后先别急着跑先看它的模块划分。我一般会把界面项目拆成三个部分数据读取图片、视频、RTSP流、模型推理ONNX或PyTorch、业务逻辑报警记录、结果显示。PyQt5做桌面界面最稳推理部分用OnnxRuntime而不是直接跑PyTorch原因有二一是OnnxRuntime部署时不用装整个PyTorch现场机器干净二是CPU上OnnxRuntime比PyTorch的推理快一截。界面布局建议如下表这个布局能兼顾展示和操作区域组件功能左侧主画布QLabel 绘图显示检测结果帧右侧结果列表QTableWidget当前帧所有目标类别、置信度、坐标顶部配置栏QComboBox / QDoubleSpinBox选择输入源、调置信度阈值底部状态栏QLabel帧率、累计报警数、当前时间一个原则推理绝不能放在UI主线程。PyQt的界面刷新是事件循环驱动的你在主线程里跑一个while循环读视频窗口会直接假死点什么都无响应。正确做法是开一个QThread专门做推理通过信号把结果帧传回UI线程刷新。4.2 模型导出为ONNX让界面不依赖PyTorch训练完的best.pt是PyTorch权重界面里直接用要依赖torch库部署机器上还得装对应版本麻烦。常见的做法是导出ONNX格式然后界面里只依赖onnxruntime。# 导出ONNXopset不要乱改默认即可 yolo export modelruns/detect/pit_stage2/weights/best.pt formatonnx opset12导出后在同目录会生成best.onnx。下面是一个OnnxRuntime推理类的最小实现包含letterbox预处理和后处理的关键步骤。预处理必须和训练时一致否则检测框会整体偏移。# detector.py import cv2 import numpy as np import onnxruntime as ort class Detector: def __init__(self, onnx_path, conf_thres0.35): self.session ort.InferenceSession(onnx_path, providers[CPUExecutionProvider]) self.input_name self.session.get_inputs()[0].name self.conf_thres conf_thres def letterbox(self, img, size640): # 保持纵横比填充灰色边(114)和训练时一致 h, w img.shape[:2] r min(size / h, size / w) new_w, new_h int(w * r), int(h * r) resized cv2.resize(img, (new_w, new_h)) canvas np.full((size, size, 3), 114, dtypenp.uint8) dw, dh (size - new_w) // 2, (size - new_h) // 2 canvas[dh:dh new_h, dw:dw new_w] resized return canvas, r, dw, dh def detect(self, frame): canvas, r, dw, dh self.letterbox(frame) blob canvas[:, :, ::-1].transpose(2, 0, 1).astype(np.float32) / 255.0 blob np.expand_dims(blob, 0) outputs self.session.run(None, {self.input_name: blob})[0] # [1, 84, 8400] preds outputs[0].T # [8400, 84] boxes [] for p in preds: scores p[4:] cls_id int(scores.argmax()) conf float(scores[cls_id]) if conf self.conf_thres: continue # 坐标是letterbox后的坐标要映射回原图 x1 (p[0] - dw) / r y1 (p[1] - dh) / r x2 (p[2] - dw) / r y2 (p[3] - dh) / r boxes.append((x1, y1, x2, y2, conf, cls_id)) return boxesletterbox是YOLOv8系列最容易踩坑的地方。训练时输入是640x640的方形图推理时如果你的图片是1920x1080直接resize会压扁目标不resize又进不了模型。letterbox的做法是等比缩放短边不足的地方用灰色114填充。推理得到的框坐标是在缩放后坐标系里的所以要逆运算回原图坐标否则画框位置会偏。后处理里有个细节YOLOv8的输出形状是[1, 84, 8400]844个坐标80个COCO类别。如果你训练的是4类这个维度是[1, 8, 数量]所以不要写死。我上面的代码用scores.argmax()拿到类别id这么做对自定义类别数也通用。4.3 视频流接入与报警记录界面部署在工地输入源多半是网络摄像头RTSP流是标配。cv2.VideoCapture可以直接支持rtsp地址但要注意响应延迟和断线重连。我的习惯是把采集和推理放到同一个线程检测完再发信号给UI。# cap_thread.py from PyQt5.QtCore import QThread, pyqtSignal import cv2 import numpy as np class CaptureThread(QThread): frame_ready pyqtSignal(np.ndarray) alarm_triggered pyqtSignal(dict) def __init__(self, detector, source, parentNone): super().__init__(parent) self.detector detector self.source source self.running True def run(self): cap cv2.VideoCapture(self.source) # RTSP地址形如 rtsp://user:passip:554/stream1 # 设置缓冲小一点降低延迟 cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) while self.running: ok, frame cap.read() if not ok: continue boxes self.detector.detect(frame) for b in boxes: x1, y1, x2, y2, conf, cls_id b cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 2) self.frame_ready.emit(frame) cap.release()报警逻辑要有消抖。单帧检测到leakage不报警连续N帧都检测到同一个位置才触发否则树叶影子晃动一下就会产生几十条误报记录。我一般用IoU来判断是不是同一个目标当前帧某个框与上一帧某个框的IoU大于0.7就认为还是同一个目标累计计数达到3帧再写报警记录。报警记录写什么常见做法是存一张截图加一段JSON。截图用于事后人工复核JSON里记录类别、置信度、时间戳、原始坐标。这一套下来界面才不是“能画框”而是真的能当监测工具用。5. 部署和避坑排查从CPU主机到RK3588边缘盒子5.1 部署环境对比与选型部署教程是标题里的重头戏但先别跳进代码先想清楚部署到哪。三种常见环境各有边界部署环境优势劣势适合场景CPU主机环境好搭、调试方便、成本低帧率个位数到十几FPS课程设计、离线分析、原型验证NVIDIA GPU帧率高、可跑多路视频功耗高、现场取电麻烦监控室集中处理RK3588等NPU板卡低功耗、体积小、可挂现场转换流程复杂、算子兼容性要试边缘现场实时监测CPU主机做毕设演示完全够用PPT上放帧率数据就够了。RK3588部署的意义是“能挂到工地现场”但你要有心理准备从pt到rknn的转换链路会消耗大量时间特别是写自定义后处理时YOLOv8的Dfl结构在转RKNN时偶尔会掉精度。转换流程一般是先导出ONNX再用rknn-toolkit2在PC上将ONNX转成RKNN格式最后把RKNN模型拷贝到板端推理。# pt转onnx前面已做过 yolo export modelbest.pt formatonnx opset12 # onnx转rknn需要单独的工具链不是pip直接装 # 转换时注意量化数据集要用训练集里的真实样本图片不要用网上随便拉的照片CPU环境下onnxruntime的部署相对丝滑。唯一要注意的是ONNX版本和opset的对应关系opset12是兼容性最好的选择。rk3588上如果遇到某个算子不支持常见的兜底方案是把模型导出成opset11或者把Dfl结构单独拆出来放到板端用C实现。5.2 部署必看的五个坑现象、原因、解决先亮结论坑都是踩出来的我已经对着这几个问题翻过几次车。坑一中文标签全部变成乱码框现象用Labelme标注时用了“裂缝”“漏水”等中文名训练时显示正常推理时结果类名在界面里显示成乱码或者直接训练中断报错“class names contain non-ASCII characters”。原因ultralytics底层对类别名的编码处理不一致中文在多进程dataloader下容易踩到编码坑。解决别和编码过不去labelme标注和data.yaml里的names全部用英文或拼音。界面显示时再单独维护一份中英文映射表比如crack显示为“裂缝”。这是最省事的做法不要指望框架修好编码。坑二检测框整体偏移目标在框外现象画出来的框比真实目标偏左上或者偏右下置信度还不低。原因推理预处理用了普通resize没做letterbox或者做了letterbox但坐标逆运算时忘了减掉填充边dw/dh。YOLOv8训练时强制letterbox推理不一致框必然飘。解决复现训练时的预处理检测线程里的letterbox函数参数必须和训练时一致。逆运算公式就是x(pred_x - dw)/ratio、y(pred_y - dh)/ratio。坑三CPU推理只有2FPS界面一卡一卡现象读RTSP流帧率只有2帧左右界面明显掉帧。原因模型用的是yolov8m或yolov8x或者输入分辨率设成1280且batch8或者使用了GPU版Torch但没GPU导致CPU推理。解决推理部署统一用yolov8nonnxruntime分辨率压到640CPU上能跑到个位数到十几FPS。如果还想快把输入分辨率降到480试一下裂缝这种目标480仍然可检测但对非常细的裂缝会漏。坑四渗漏水识别被阴影干扰误报刷屏现象晴天下午支护桩的阴影一移动就会触发leakage报警一天报了两百多次。原因阴影和水渍在颜色空间上太接近模型学到的是“暗色斑块”不是“水迹纹理”。单一摄像头视角下两者本来就难分。解决从两个方向下手。训练层面数据增强里加强亮度扰动让阴影的形态更多样推理层面引入帧间差分——水迹是静态的阴影是移动的同一位置连续多帧框置信度均高才报警阴影晃过一两帧就消失。这个消抖逻辑比换模型更有效。坑五Mosaic增强导致裂缝一半有标签一半没标签现象训练loss曲线正常但验证集mAP持续偏低可视化预测时发现模型漏检拼接缝附近的裂缝。原因Mosaic拼接时把裂缝裁到两张图交界的灰边上标签没跟着切过去模型学到“半个裂缝”的负样本。解决第二段微调时mosaic降到0.2以下或者自定义Mosaic时把目标完全落到某一象限再做变换但工程上直接调参更现实。5.3 排查问题的顺序模型部署出问题很多人上来就怀疑代码其实先排查数据更高效。我的排查顺序先用单张图片跑通整个推理链路确认预处理后处理没毛病再用一段固定视频反复测试对比PyTorch权重和ONNX权重的输出差异最后才怀疑现场环境。快速定位手段是让ultralytics把每张预测图保存下来带上类别和置信度信息# 在测试集上预测并保存结果 yolo predict modelbest.pt sourcetest_images/ save_txtTrue save_confTruesave_txtTrue会输出每个框的类别和坐标和界面推理的结果对比就能知道是不是界面代码的问题。如果两者一致但界面画框位置错回坑二查坐标逆运算如果界面结果稳定优于现场视频回坑四查报警消抖。6. 用连续监测数据验证别让模型只活在训练集里6.1 统计误报率与漏报率很多毕设演示用的是挑好的几张照片测出来准确率95%。但这没有说服力——真正检验监测模型的是连续视频流里的误报率和漏报率。我建议你把验证集按时间顺序重排模拟真实监测场景统计三个指标误报次数不是目标被框出来、漏报次数真目标没框出来、平均响应帧数目标出现第几帧开始被连续检测到。这三个指标直接对应工程价值误报太多没人信系统漏报太多系统没意义。我做基坑监测项目时有一周的工作就是坐在屏幕前把误报一条条标出来看是阴影、雨滴还是灯光反光。这个过程很枯燥但比调参有用。6.2 和传统传感器数据对齐最后给课程设计或毕设加分的一步把视觉报警的时间戳和测斜仪、水位计的数据对齐。基坑变形是缓慢过程视觉异常往往比位移曲线拐点早几天出现。哪怕只是把你标注的裂缝发现时间点和测斜累计位移画在一张图上也能讲出“视觉监测具备早期预警潜力”这种有分量的结论。我现在做这个方向有个习惯拿到新现场先固定机位拍一周视频把视频里的目标分布、光线变化、天气影响摸清楚再回头调模型。数据质量差的时候调任何参数都是玄学数据质量扎实模型参数反而不需要反复折腾。可视化界面是给答辩和业主看的真正检验模型的是连续七天不重样的误报记录。希望帮到你。本文还有配套的精品资源点击获取
