
道路养护里最磨人的活儿就是裂缝检测。过去靠人工目测一天走几公里路蹲下去看缝、拿笔记录效率低不说不同人判的还不一样。后来我在一个巡检Demo里试过用传统图像处理做裂缝分割阴影、油渍、水渍一堆干扰阈值怎么调都调不干净。直到换成YOLOv8把这套“基于YOLOv8的3类道路裂缝检测龟裂/纵向裂缝/横向裂缝系统”跑通才算是把现场采集回来的照片变成了能直接用的病害台账。这套系统值得参考的不只是模型本身而是完整的工程链路数据集怎么做、三个类怎么标、训练参数怎么设、推理脚本长什么样、中英文输出怎么切源码和效果演示都放在文里。如果你在搞土木检测、做视觉算法或者刚开始接触YOLO系目标检测这篇应该能帮你少走不少弯路。1. 项目概览与核心设计思路1.1 这个项目解决什么问题道路裂缝检测不是“看出有没有缝”那么简单。养护单位需要把裂缝分成不同类型因为不同类型代表不同的病害机理龟裂通常和路面结构疲劳有关纵向裂缝多与施工接缝或路基不均匀沉降有关横向裂缝一般是温度收缩或半刚性基层反射引起的。分类清楚了维修方案才能定。人工巡检的问题是费人力、标准不统一同一个裂缝两个人记录两个类。自动检测系统要做的就是把“看”和“分类”两项工作交给模型现场拍图模型直接输出类别和位置。这套系统的目标很明确输入一张道路表面照片输出三类裂缝的检测框同时给出中英文标签和置信度。适合的人群也很清晰——手里有道路检测需求、想用YOLOv8快速落地的开发者或者做土木工程信息化的人。我把这个项目定位成“工程参考实现”而不是论文里的实验玩具。因为它的数据组织方式、标注规范、推理脚本、双语切换逻辑都是按真实巡检场景设计的。1.2 为什么选YOLOv8而不是传统图像处理传统图像处理做裂缝思路一般是灰度差加阈值分割先转灰度用大津法找暗像素再做形态学连通域。我最早做巡检Demo时就是这套晴天还行一旦碰到阴影、水渍、轮胎印、路面补丁灰度值分布乱到阈值根本没法设。更麻烦的是分割完只知道“这里有裂缝纹理”区分不了龟裂、纵裂、横裂。你硬要分就得给每个连通域手工设计特征什么长宽比、方向角、网状度规则写一长串换个路面材料又得重调。语义分割网络能输出裂缝像素但标注成本很高而且养护台账最关心的其实是一个病害区块不是一个像素。目标检测用框表示病害区域标注快、推理快、天然贴合业务记录格式。YOLOv8属于当前目标检测里工程友好度最高的那一档训练、导出、部署一条龙都给你弄好了。相比v5和v7YOLOv8把检测头换成了anchor-free对裂缝这种长宽比变化很大的目标少了一层anchor的调参痛苦实测下来对小目标的召回也更稳。当然它也有缺点水平框对倾斜长裂缝不太友好这个后面会详细说怎么缓解。1.3 三类裂缝的形态差异决定了标注策略标注前最好先把三类裂缝的形态定义打印出来贴在工位上。龟裂是成片的网状或鳄鱼皮状裂纹往往是很多条细纹交叉纵向裂缝是沿行车方向的长条横向裂缝是垂直于行车方向的长条。这里的关键点是龟裂应该“框整片”不要把每一条网纹单独框一个框。因为业务上它是一处病害区域而且单独框网纹会让模型学得很碎。纵向和横向裂缝是细长目标一张1280乘720的路面图里一条横向裂缝可能占了整张图的宽度如果硬件资源允许建议先切图再训练或者直接用640或960输入尺寸并且把一张图里的长裂缝切分成2到3段来标注。切分不是为了偷懒是为了让水平框的宽高比不至于夸张到几十比一否则模型的回归头很难学。我在实际标注时还有一条规矩纵向和横向裂缝框的边界尽量贴住裂缝的轮廓边缘不要留太多背景但如果裂缝太长宁肯框稍微长一点也不要把一条连续裂缝断成好几截来标因为那会引入“一条裂缝多个目标”的歧义训练时非极大值抑制会把相邻框压掉导致漏检。这条经验很实在。顺带提一句裂缝图像里经常有路面上其他黑条——比如轮胎印、接缝、标线磨损这些都应该作为背景保留在训练图里不要裁掉否则模型学不到拒绝干扰的能力。2. 数据集准备与标注规范2.1 数据来源与数量建议我复现这套系统时数据来源以公开的裂缝图像为基础再从现场补拍了一些不同光照条件下的路面照片。如果你没有现成数据可以分两步走先找公开道路裂缝数据集里面一般都有裂缝切割片段然后现场用手机或者行车记录仪拍路面拍的时候注意包含不同路面材料沥青、水泥、不同光照晴天直射、树荫遮盖、不同干湿状态干燥、雨后潮湿。潮湿路面裂缝会变暗变模糊模型如果没见过现场很容易漏检。数量上目标检测数据集的核心单位不是“多少张图”而是“多少个框”。三类裂缝合计至少800到1000个实例比较稳。如果只有几百张图宁可少一点也要保证每类都有足够的实例比例。我建议每一类的实例数量不要相差超过3倍否则少数类AP会被拉得很低。图像数量的话500张以上、每类实例均衡用yolov8s训练基本能到可用的程度如果能攒到1000张以上效果会明显上一个台阶。2.2 标注工具与标注规范标注工具我用的开源LabelImg支持YOLO格式直接导出。YOLO格式的标签文件是txt每行五个数类别序号、归一化中心点x、归一化中心点y、归一化框宽w、归一化框高h。注意所有坐标都是相对于原图宽高归一化的写0到1之间。文件夹结构我建议固定成dataset/ images/ train/ val/ labels/ train/ val/训练集和验证集按8比2或7比3划分划分时注意同一场景的连续帧要放进同一边不能一帧在训练集一帧在验证集否则模型相当于作弊指标虚高。标注规范要在一开始就定死避免返工。我的规范是龟裂纹理密集的区域用一个多边形框把整块网状区域框住类别标0纵向裂缝是沿行车方向的连续裂缝用最小外接水平矩形标出一个尽量贴合的长条框类别标1横向裂缝同理类别标2。这里最难忍的是“贴边”问题——长裂缝的框如果稍微偏斜把很多路面背景包进来模型会学到背景特征导致推理时把颜色深的路面补丁也框出来。解决办法是标注时放大到单像素级别看边缘宁肯切掉裂缝两端一点点也不要留大片背景。2.3 数据增强与样本均衡裂缝图像很容易被过度增强毁掉。最大的坑是旋转增强纵向裂缝旋转90度就变成了横向裂缝如果你用了90度倍数的旋转增强标签却没跟着改模型就会学疯。我一般限制rotate在正负15度以内Mosaic和MixUp可以开但MixUp的比例调低一点。亮度对比度增强建议范围大一些因为现场光照变化确实大。龟裂样本不够时优先做两类操作一是对龟裂做随机裁剪重缩放制造新的角度二是把龟裂区域粘贴到另一张干净的柏油路面上同时复制对应的标注框。这就是所谓的Copy-Paste增强对少数类很有效。下面给一个我用增强脚本的简化版本基于albumentations库import albumentations as A from albumentations.pytorch import ToTensorV2 train_transform A.Compose([ A.HorizontalFlip(p0.5), A.ShiftScaleRotate(shift_limit0.05, scale_limit0.15, rotate_limit15, border_mode0, p0.6), A.RandomBrightnessContrast(brightness_limit0.25, contrast_limit0.25, p0.6), A.RandomGamma(p0.3), ], bbox_paramsA.BboxParams(formatyolo, label_fields[class_labels]))注意如果你直接用ultralytics训练Mosaic增强框架内置了不需要在albumentations里自己拼。更省事的方式是直接在训练配置里开官方自带增强它默认的HSV扰动、翻转、Mosaic已经调过一轮你只需要把degrees改成15左右mosaic保持1.0就行。自己额外写增强脚本的好处是可以对少样本类单独增加到重复样本适合数据严重不均衡的场景。3. 环境搭建与模型训练3.1 环境依赖与安装这套系统依赖的东西不多Python环境、PyTorch、ultralytics包。有NVIDIA显卡最好没有也能跑就是训练慢好几倍。安装命令我用conda新建环境conda create -n crack python3.10 conda activate crack pip install torch torchvision pip install ultralytics如果机器上装的是带CUDA的GPU环境建议根据你本机的CUDA版本从PyTorch官网选择对应的安装命令核心还是装torch和torchvision。没有GPU的机器装CPU版本就行。装完可以跑一句验证python -c from ultralytics import YOLO; print(ok)看到ok就表示环境没问题。用CPU训练的话建议把模型换成yolov8nepochs可以适当减少先验证链路能不能跑通再谈精度。3.2 数据集配置文件YAMLultralytics训练时需要指定一个YAML文件描述数据路径和类别。我的crack.yaml长这样train: dataset/images/train val: dataset/images/val nc: 3 names: 0: alligator_crack 1: longitudinal_crack 2: transverse_crack注意这里的train和val指向的是存放图片的文件夹ultralytics会自动去同级labels目录找对应的txt标注文件。也就是说images/train对应的标注要放在labels/train。如果目录结构不一样自己用软链接或者脚本整理一下。类别名我这里用英文小写加下划线因为训练时名称只影响日志和导出标签中英文映射放到推理阶段做这样模型文件本身不耦合界面语言。3.3 训练参数怎么定训练命令我给出一个能直接跑的版本yolo detect train datacrack.yaml modelyolov8s.pt epochs150 imgsz640 batch16 device0 patience30这里几个核心参数值得说。modelyolov8s.pt会从预训练权重继续训练不要从零开始迁移学习对裂缝这种数据和自然图像差异很大的任务依然有效收敛快很多。epochs150对中等规模数据集足够patience30表示30个epoch指标不涨就早停省时间。imgsz640是默认值如果你发现小裂缝漏检优先试imgsz960代价是显存占用和训练时间增加。batch16是在12G显存下yolov8s的安全值显存更大可以往上加。还有一个容易被忽略的参数是device多卡环境可以写device0,1。混合精度amp默认开着不要关。如果你只想快速验证数据有没有问题把epochs设成5跑一轮看loss能不能降下来能降就说明数据链路正常再跑完整训练。3.4 训练结果指标解读训练结束会在runs/detect/train目录下生成weights/best.pt和weights/last.pt还有一堆曲线图。我一般只看四个东西PR曲线、混淆矩阵、训练结果曲线里的mAP50曲线。对裂缝检测mAP50比mAP50-95更有参考价值因为裂缝这种细长目标对框的IoU非常苛刻mAP50-95数值低不代表模型不能用。比如我自己训练完mAP50在0.8左右mAP50-95可能只有0.5不到这个差异是正常的。P和R要看业务场景。道路巡检更看重recall因为漏掉一条裂缝比多标一个疑似位置严重得多。如果验证集R值低于0.7先把conf阈值调低到0.1试试再检查标注漏标。如果P很低说明误检多看看是不是真值框太小导致大量IoU不达标或者背景负样本不够。另外按类看mAP报告很关键。三类裂缝的mAP会有明显分层通常龟裂mAP最高因为它是区域目标框好打横向或纵向裂缝mAP低一些因为细长框对标注抖动敏感。如果你看到某一类的AP比其他类低10个点以上不要急着改网络先回去查这类目标的标注宽高比分布看看是不是框得太宽或者太小。4. 推理与效果演示4.1 推理脚本源码讲解训练完成后我习惯把best.pt单独拷出来写一个干净的推理脚本不依赖runs目录。最简版本是直接调YOLO接口from ultralytics import YOLO import glob model YOLO(best.pt) images glob.glob(test_images/*.jpg) for img in images: results model.predict( sourceimg, conf0.25, iou0.5, saveTrue, projectoutput, namedemo, imgsz640 ) boxes results[0].boxes for box in boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) xyxy box.xyxy[0].tolist() print(img, cls_id, conf, xyxy)saveTrue会自动把标注框画在图上并保存到output/demo目录这就是“效果演示”最直接的一种输入图片文件夹输出带框图片。如果你想自己控制画框效果、中文标签、颜色可以关掉save拿xyxy坐标自己用OpenCV画。4.2 效果演示样例与阈值选择跑完一批测试图你大概率会看到这样的现象干燥沥青路上的横向裂缝被清楚框出来置信度在0.6到0.9之间龟裂区域是一个较大的框覆盖了整片网状区域个别阴影边缘被误检成纵向裂缝但置信度通常不高。这就是阈值选择的依据。conf0.25是我常用的默认值偏向召回。如果是自动生成台账、不希望太多误检混进去可以调到0.4误检明显减少但也可能漏掉一些很浅的裂缝。我建议做两个配置巡检初筛用conf0.25人工复核用conf0.45。不用改代码只要改参数。效果演示除了单张图还可以做视频流的逐帧检测把source换成视频文件路径即可ultralytics会自动对视频抽帧检测并输出带框视频。这个模式下要注意推理速度yolov8s在GPU上大概几十毫秒一帧CPU上会慢很多视频检测建议用yolov8n。4.3 模型导出与轻量化部署演示跑通之后很多人想把它接到巡检车或无人机上这时候直接把.pt文件丢上去不一定是最优解。YOLOv8官方支持导出多种格式我常用的是ONNXyolo export modelbest.pt formatonnx imgsz640 opset12导出后可以用OpenCV DNN或者ONNXRuntime加载推理速度比原生PyTorch快而且不依赖ultralytics环境。更轻量的场景可以导出TensorRT格式需要在目标机器上跑速度最快。如果你只是给单位做一个桌面演示工具用PyTorch直接推理就够了别在部署格式上花太多时间。需要提醒的是导出ONNX时imgsz最好和训练时一致否则动态分辨率下有些算子会被转成低效实现推理速度可能不升反降。5. 中英文双版系统实现5.1 双语输出与配置管理标题里的“中英文双版”我理解成两层模型训练和推理脚本本身不写死语言通过一个全局配置切换中文或英文输出界面和日志也跟随切换。实现上用一个字典映射类名。因为训练时names用的是英文字段推理拿到的是类别序号映射表把序号转成目标语言。LANG zh # 可选 zh / en LABELS { zh: { 0: 龟裂, 1: 纵向裂缝, 2: 横向裂缝 }, en: { 0: Alligator Cracking, 1: Longitudinal Crack, 2: Transverse Crack } } def label_name(cls_id: int) - str: return LABELS[LANG][cls_id]这样切中文只需把LANG改成zh其它代码不用动。打印的时候再把置信度格式化到小数点后两位for box in results[0].boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) x1, y1, x2, y2 [round(v, 1) for v in box.xyxy[0].tolist()] print(f{label_name(cls_id)} {conf:.2f} [{x1}, {y1}, {x2}, {y2}])中文输出在Windows控制台偶尔会乱码建议在脚本开头加一句import sys, io sys.stdout io.TextIOWrapper(sys.stdout.buffer, encodingutf-8)或者在运行终端里先执行chcp 65001切到UTF-8代码页这个细节别忽视。5.2 简单Web演示界面如果你要让不会写代码的同事也能用最简单是套一个Flask界面上传图片后台调用模型返回带框图片和检测结果表格。不需要做得很花哨。核心代码大概长这样from flask import Flask, request, render_template_string from ultralytics import YOLO import cv2 import base64 app Flask(__name__) model YOLO(best.pt) HTML form methodpost enctypemultipart/form-data input typefile nameimage button typesubmit检测/button /form {% if result_image %} img srcdata:image/jpeg;base64,{{ result_image }} pre{{ result_text }}/pre {% endif %} app.route(/, methods[GET, POST]) def index(): result_image None result_text if request.method POST: f request.files[image] path temp_upload.jpg f.save(path) results model.predict(sourcepath, conf0.25) img results[0].plot() _, buf cv2.imencode(.jpg, img) result_image base64.b64encode(buf.tobytes()).decode() rows [] for box in results[0].boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) rows.append(f{label_name(cls_id)}: {conf:.2f}) result_text \n.join(rows) return render_template_string(HTML, result_imageresult_image, result_textresult_text)界面语言也由LANG变量控制把文中的“检测”改成“Detect”就是英文版。这个方案只适合单机演示扛不住并发但给检测人员试用完全够了。5.3 日志与结果汇总系统真正有用的部分是检测结果汇总。我用CSV记录每张图的输出方便后面导入养护台账。表头根据LANG切换import csv def write_result(fname, boxes, out_csv): header_zh [图片, 类别, 置信度, x1, y1, x2, y2] header_en [image, class, confidence, x1, y1, x2, y2] header header_zh if LANG zh else header_en with open(out_csv, w, newline, encodingutf-8-sig) as fp: writer csv.writer(fp) writer.writerow(header) for cid, conf, (x1, y1, x2, y2) in boxes: writer.writerow([fname, label_name(cid), round(conf, 3), round(x1, 1), round(y1, 1), round(x2, 1), round(y2, 1)])这里用utf-8-sig编码是为了让Excel直接打开中文表头不乱码属于实操细节。如果你还要带GPS信息只需要在写入时把拍摄点的经纬度加一列逻辑完全一样。6. 常见问题与排查心得6.1 漏检误检排查表我把自己踩过的坑整理成一张表按“现象—原因—解决”来看能省很多排查时间。现象可能原因解决建议龟裂全部漏检龟裂样本太少或者把整片网纹拆碎标注增加龟裂实例标注时框整片区域用Copy-Paste增强细长纵向裂缝漏检输入尺寸太小裂缝像素只占几十个像素imgsz提到960或先切图再训练必要时换更大的模型阴影/水渍被误检成裂缝训练图缺少这些难例负样本采集含阴影、水渍的路面图作为背景降低conf阈值纵向和横向裂缝互相混淆数据增强里允许了大角度旋转把augment参数里的degrees改成0或15不要做90度旋转mAP50不错但mAP50-95很低细长框对IoU太敏感这是正常现象看mAP50和R为主别拿mAP50-95卡自己的模型验证集指标高现场效果差训练集和验证集来自同源视频的连续帧按场景划分数据集连续帧放同一侧6.2 训练不收敛/指标异常的排查思路训练时如果loss曲线不降先别调网络按顺序查数据目录对不对、标注txt坐标是否超出0到1范围、类别序号是否小于nc。常见的一个低级错误是图片是灰度图而标注用的是三通道图尺寸对不上。还有val集里如果混入了没有标注的正常路面图会拉低precision但recall不受影响这个现象能帮你反向定位问题。如果你发现纵向、横向裂缝的召回总是上不去我强烈建议做一次“预测结果可视化原图标注叠加”的对照检查把模型预测的框和GT框用不同颜色画出来一帧一帧看。大多数时候你会看到两类问题一是GT框太紧裂缝边缘稍微出框就IoU不够二是裂缝很淡人在标注时都没看清。这时候修标注比调参有效得多。我试过只修了一遍标注mAP50直接涨了6个点比换模型省事。6.3 项目复现与扩展建议这套系统要复现核心路径是整理数据、定标注规范、训练、导出、接双语界面。想扩展的话有三个方向比较现实。一是把模型输出接到道路养护工单系统检测结果直接生成带GPS位置的裂缝台账二是把裂缝按宽度或严重程度再做分级这需要引入分割模型比如用训练好的YOLO框作为ROI在ROI内跑细分割三是从静态图片扩展到行车视频重点优化推理速度、做帧间去重避免同一条裂缝在连续帧里被重复计数。这三个方向我后续都会整理成单独的文章。我个人在实际操作中的体会是裂缝检测这类任务模型只是最后一步前面数据整理和标注规范花的时间至少占七成。很多找我讨论的人一上来就问yolov8m还是yolov8l其实瓶颈根本不在模型规模。先把三类裂缝的边界定义清楚把标注规则迭代两轮再回来看训练结果你会发现YOLOv8的默认配置已经能解决八成问题。真要说改进空间我会优先做高质量难例数据而不是换网络结构。