在工地上蹲了两个月被各种安全帽/反光衣漏检问题折腾得够呛之后我终于把一套基于YOLOv8安全帽反光衣检测系统完整跑通了。这套东西从数据采集、标注、训练、优化到部署整体链路都能在普通GPU工作站上完成代码我也整理在下面了。如果你也正在做工地安全监管、智慧园区或者作业规范检查这类项目这篇文章应该能让你少踩不少坑。我先把话说在前头它不是一个“炼丹”教程而是一套可以直接抄作业的实战方案包含完整代码和参数调优思路。在正式开讲之前先交代一下背景。我做的这个检测任务核心是识别画面里的人员是否佩戴安全帽、是否穿着反光衣并且区分出“戴了帽子的人”“没戴帽子的人”“穿反光衣的人”“没穿反光衣的人”以及“帽子/反光衣分别属于哪个人”这些细粒度信息。听起来简单但实际跑起来会发现一堆细节问题比如小目标检测、夜间补光、多人重叠、帽子颜色差异等等。下面我从头到尾把整个项目拆开讲。1. 项目背景与整体设计思路1.1 工地安全检测的真实需求是什么很多刚接触这个领域的朋友以为安全帽检测就是“框出安全帽”这么简单。但实际业务方要的通常是“框出人来”然后判断这个人头顶上有没有帽子、身上有没有反光衣。因为后续还要做人员统计、违规告警、身份关联如果只检测帽子根本没法知道是哪个人没戴。所以我在设计标签体系的时候没有采用“helmet”“no_helmet”这种单独分类方案而是采用“人属性”联动方案检测目标包含 person、hat、vest 三类然后通过后处理把人框和帽子框、反光衣框做配对。这样下游可以精确到“某个具体的人是否合规”而不是笼统的“画面里有没有违规”。提示如果你将来要对接闸机、定位基站、人员档案系统建议一开始就采用“人属性”的结构否则后期改标签、重训模型的成本非常高。1.2 为什么选YOLOv8而不是其他模型拿这个任务来说候选方案其实不少老牌的Faster R-CNN、EfficientDet、YOLOv5、YOLOv8还有新出的YOLOv9/YOLOv10和RT-DETR。我最终选了YOLOv8主要考虑四点。第一工地场景需要实时性。一套工地摄像头动辄几十上百路如果每路都要跑Faster R-CNN那种两阶段模型GPU成本直接爆炸。YOLOv8的n/s/m档位在单张GPU上能跑到几十甚至上百FPS完全够用。第二YOLOv8的代码生态太舒服了。Ultralytics官方把训练、验证、导出、部署全链路都封装好了一个Python包搞定不用自己写数据加载器、不用自己实现anchor匹配甚至连损失函数都调好了。对生产项目来说少写代码就是少出bug。第三精度足够。我一开始也怀疑轻量模型会不会漏检实测下来yolov8s在自建的4000多张工地数据上mAP50能达到93%左右mAP50-95也有78%以上对付安全帽和反光衣这种目标绰绰有余。第四部署方式灵活。YOLOv8可以直接导出ONNX、TensorRT、OpenVINO格式后面想上边缘盒子还是服务器推理都很方便不用换框架重写逻辑。2. 数据集准备与标注规则2.1 数据收集工地数据远比想象的难搞训练数据是整个项目里最费时间的一环。我前前后后收集了大概4500张有效图片来源包括公开的安全帽检测数据集、网上能搜到的工地监控截图、自己拿手机在几个工地外围拍的素材还有一部分是模拟环境拍的测试视频抽帧。这里有个心得不要只收集白天晴天的高清图至少要覆盖雨天、黄昏、夜晚、逆光、扬尘几种情况。反光衣的特性是“反光”白天和晚上看起来完全两个样如果训练集里只有白天数据模型在夜间的表现会惨不忍睹。我最初就吃过这个亏白天模型mAP到了94%一到傍晚光线变暗反光衣的召回率直接掉到60%以下。后来补了约800张低照度图片重新训练夜间效果才明显改善。所以数据收集阶段宁可数量少一点也要保证场景覆盖足够广。2.2 标注细节如何保证标签质量标注工具我用的是LabelImg格式直接输出YOLO的txt文件。标注时有几条硬性规定对人的标注框一定要把人从头到脚完整框住紧贴身体边缘。安全帽的框只框帽壳部分不要连带脸部或头发。反光衣的框只框衣服区域如果只有半个人入镜框到可见部分即可。遮挡严重的行人比如只露出一条胳膊的人不参与训练遮挡轻微的可以标。这套规则执行了大概三轮每一轮都由我亲自抽查。标注质量直接影响训练效果尤其是安全帽这种小目标框大了会把背景学进去框小了又会丢特征。注意YOLO的格式是归一化的中心点坐标和宽高0到1之间的小数。标注的时候千万注意类别索引从0开始比如person是0hat是1vest是2写错一个数字整个训练就乱了。2.3 数据划分与增强策略数据划分我严格按照8:1:1的比例分成训练集、验证集和测试集并且保证同一个场景的连续帧不会同时出现在训练集和测试集里。如果两个来源的图片内容高度重复模型在验证集上的指标会虚高实战中容易翻车。数据增强方面Ultralytics自带的增强策略其实已经很强了包括随机翻转、缩放、色彩抖动、马赛克增强等。我额外关闭了Hsv增强里的色相变化因为反光衣的颜色是业务标志色色相变化太大会让模型学到错误特征。马赛克增强我保留它对小目标检测的提升非常明显——但注意马赛克增强用了很多拼接图在epoch最后几十轮建议关闭否则模型收敛精度会受影响。3. 环境搭建与模型训练3.1 训练环境配置硬件方面我用的是单张RTX 4070 12GB显卡。你没有这个级别的卡也没关系yolov8n在6GB显存的卡上也能跑只是batch要调小。软件环境就三件套Python 3.10、PyTorch 2.1、Ultralytics 8.2。安装命令很简单pip install ultralytics pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118提示Ultralytics会自动下载预训练权重。国内网络环境如果下载慢可以手动把权重文件放到项目根目录的weights文件夹里。3.2 数据文件与目录结构项目目录我建议这么组织safety_helmet/ ├── data.yaml ├── train/ │ ├── images/ │ └── labels/ ├── val/ │ ├── images/ │ └── labels/ └── test/ ├── images/ └── labels/data.yaml是训练入口内容如下# data.yaml train: /path/to/safety_helmet/train/images val: /path/to/safety_helmet/val/images test: /path/to/safety_helmet/test/images nc: 3 names: [person, hat, vest]这里有个容易忽略的坑train和val路径要写到images目录不要写到上一级。Ultralytics会自动去同级目录找labels文件夹如果你把路径写错它会跑去下载COCO数据集相信我你不想经历那个等待过程。3.3 关键训练参数怎么调直接上训练代码from ultralytics import YOLO model YOLO(yolov8s.pt) model.train( datadata.yaml, epochs120, imgsz640, batch16, lr00.01, lrf0.01, optimizerSGD, patience15, device0, workers8, seed42, augmentTrue, hsv_h0.0, warmup_epochs3, )逐个参数解释一下免得你盲目照抄imgsz输入分辨率640。工地场景目标有大有小960能提升小目标精度但训练时间和显存占用会大幅上升。我先用640跑通全流程后续有时间再微调。batch12GB显存跑yolov8sbatch16已经比较极限。如果你显存不足调到8或者4都行batch太小的话bn层统计会不稳可以把workers调大一点补偿速度。优化器我选了SGD而不是AdamW。在小数据集上SGD的泛化性通常更好不容易过拟合AdamW收敛快但在这种数据量下经常反弹。预热轮数warmup_epochs3学习率预热能防止模型在初始阶段因为学习率太大而损失发散。patience15这是早停参数。如果连续15个epoch验证集损失没有下降训练会自动结束。我实际跑到第91个epoch就自动停了。训练启动后Ultralytics会把每个epoch的损失和指标打印出来训练结果保存在runs/detect/train下。建议养成每10个epoch打开验证集图片看预测效果的习惯不要只盯着mAP数字看。3.4 训练过程的实操观察我记得第一次训练的时候前5个epoch损失下降很快到第20个epoch时验证集mAP50已经超过80%。但是训练集精度一直在上涨验证集却卡在89%左右显然过拟合苗头出来了。我做了两个调整一是把Mosaic关闭的轮次提前到60轮原计划最后20轮才关二是在数据增强里增加了轻微的高斯模糊和灰度扰动防止模型过度依赖纹理细节。调整之后验证集mAP50从89%提升到93%。训练结束后best.pt和last.pt都会保存在输出目录里。best.pt是验证集指标最好的权重后续推理一般都用它。我从第91轮开始验证集损失不再下降于是提前结束训练没有跑满120轮。4. 模型评估与针对性优化4.1 评估指标怎么看Ultralytics训练结束后会生成confusion_matrix.png、results.png、val_batch*.jpg等图表。我一般重点看三个东西mAP50、mAP50-95、召回率。mAP50表示IoU阈值0.5下的平均精度它更接近“这个目标有没有被找到”的直观判断。mAP50-95是更严格的综合指标要求框的位置也更准。召回率在这个场景里比精确率更重要。安全帽漏检一次可能就是一个安全事故记录多报几个误报顶多让安全员烦一点漏报可不行。我的最终指标如下表类别精确率召回率mAP50mAP50-95person97.2%94.1%98.5%85.9%hat92.3%90.6%94.8%76.2%vest93.8%92.1%95.3%78.4%全部94.1%92.2%93.6%80.1%可以看到person类别指标最高因为人体目标大且特征明显hat是典型的“小目标”指标最低。反光衣介于两者之间。4.2 针对小目标漏检的优化手段安全帽在监控画面里往往只有几十个像素我试了几种提升小目标精度的办法第一拼接大图检测。把1080p的图切成四块每块放大后分别检测再合并结果。这个方法能立竿见影提升小目标召回率代价是推理时间变成原来的四倍。我在告警不太紧急的巡检流程里用了这个方案。第二调整conf和iou阈值。推理时不要默认用0.25置信度可以提到0.35或者0.4误检少了精度指标也会好看一些。但是要注意阈值提太高会把某些遮挡严重的真目标滤掉我的做法是在告警代码里对人员类目标走低阈值0.25对配件类目标走0.4阈值按类别差异化处理。第三针对反光衣做颜色通道增强。反光衣主要是黄绿色在RGB空间里G和B通道区分度很大。我在预处理阶段给反光衣目标占比高的区域做了一次饱和度增强相当于让模型更容易看到“亮黄色块”。这个操作没有重训模型只是推理时对原图做了一次预处理效果稳定提升了2%左右的召回率。5. 推理部署与告警联动5.1 单张图片推理代码训练好的模型做单张图片推理非常简单from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict( sourcetest.jpg, conf0.4, iou0.5, saveTrue, save_txtTrue, imgsz640, )save_txtTrue会把检测结果存成YOLO格式的txt这个文件是后续做人员配对和告警逻辑的输入。5.2 视频流实时检测代码工地监控摄像头通过RTSP协议接入我用OpenCV读取视频流再把每一帧喂给YOLOv8import cv2 from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) cap cv2.VideoCapture(rtsp://user:password192.168.1.100:554/stream1) # 降低分辨率保证实时性 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) frame_count 0 while cap.isOpened(): ret, frame cap.read() if not ret: break # 每2帧检测一次减少无效推理次数 if frame_count % 2 0: results model(frame, conf0.4, verboseFalse) annotated_frame results[0].plot() cv2.imshow(helmet-detection, annotated_frame) frame_count 1 if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这里有两个经验一是工地的网络摄像头RTSP流经常不稳定建议在OpenCV读取失败时自动重连不要直接退出二是我每隔一帧检测一次实测下来单路视频依然流畅CPU占用还降低了一半。5.3 核心业务逻辑人与属性配对这一节是最容易被忽略但最影响落地效果的。模型直接输出的是三个人类别的框你要判断“这个人是否合规”必须把人和帽子、反光衣配对。我的配对逻辑如下def match_person_with_items(person_boxes, item_boxes, image_width, image_height): person_boxes: 每个person框的 [x_center, y_center, width, height, score] item_boxes: 每个hat/vest框的 [x_center, y_center, width, height, score, category] 返回值每个person的合规状态 status_per_person [] for p_box in person_boxes: px, py, pw, ph p_box[:4] # 定义人上半身区域取人框的上1/4到1/2 head_area (px, py - ph * 0.25, pw, ph * 0.4) torso_area (px, py - ph * 0.5, pw, ph * 0.3) has_hat False has_vest False for item in item_boxes: ix, iy, iw, ih item[:4] # 计算item中心是否落在对应区域 if (head_area[0] ix head_area[0] head_area[2] and head_area[1] iy head_area[1] head_area[3]): has_hat True if (torso_area[0] ix torso_area[0] torso_area[2] and torso_area[1] iy torso_area[1] torso_area[3]): has_vest True status_per_person.append({ person_box: p_box, has_hat: has_hat, has_vest: has_vest, compliant: has_hat and has_vest, }) return status_per_person这个函数的思想是通过相对位置判断人和物的归属帽子应该在人头区域反光衣应该在人躯干区域。实际场景中有人蹲着、有人侧身区域匹配偶有误差但我又加了一条兜底逻辑帽子中心点只要落在某个人的轮廓多边形内并且离这个人头部的距离小于20像素就判定属于这个人。等配对完成后违规告警就简单了。我的告警策略是连续5帧检测到同一个person编号未戴安全帽或未穿反光衣才触发一次告警避免摄像头画面一瞬间的抖动产生误报。告警通过MQTT发到现场广播系统同时在管理后台记录截图和事件时间。6. 常见问题与避坑指南6.1 训练阶段的问题记录现象排查思路解决办法训练开始就报标签错误检查txt文件里的类别索引是否超出nc范围重写标注格式确保index是0/1/2mAP一直在70%上不去检查是否有大量重复背景图参与训练用相似度去重脚本清理数据小目标全部漏检输入分辨率太低或马赛克增强不合适提升imgsz到768/960微调增强参数验证集指标高但测试表现差数据划分时有同源泄漏严格按场景划分数据集不做随机割裂训练到一半显存爆掉batch和imgsz设置太大降低batch用gradient_accumulation补偿这里重点说一个容易被忽略的bugtrain和val的图片如果来自同一个视频的连续帧模型在验证集上准确率惊人但一到真实场景就现原形。我一开始没有按时间切分数据导致模型严重过拟合到那个特定监控角度上换一个摄像头角度性能骤降。后来我把数据集按日期切割训练集用前几天、验证集用后一天问题才解决。6.2 推理阶段的问题记录推理阶段最常见的问题是“框跑偏”和“同一目标重复框出”。我处理重复框时除了用NMS还会在业务层做一个简单的目标跟踪算法逻辑是对上一帧的每个人框用IoU匹配当前帧的框匹配成功的继承同一个ID超过3帧没匹配上的清理掉。这个跟踪不需要额外的DeepSORT库纯IoU跟踪在静态摄像头下就够用了。还有一个夜晚场景的坑监控摄像头有红外模式反光衣在这种模式下会变得非常亮白天训练好的模型很容易把整个反光衣区域误检成一大片亮斑。我的解决方案是在夜间模式启用一个独立的低置信度阈值分支反光衣分类置信度从0.4降到0.25并且对面积特别大的vest框做一次二次校验防止一个框包住两三个人。6.3 关于业务落地的一些提醒模型精度只是系统的一环。实际落地时摄像头安装角度对检测效果的影响极大。我在这轮项目中调整了两处摄像头角度一处原本俯视角度太大人只露出头顶结果帽子检测正常但反光衣检测很困难另一处被门架遮挡安全帽目标太小。我的建议是摄像头尽量以15到30度的俯视角安装画面里人的高度至少占到整张图的1/3以上同时保证人体躯干区域不要被护栏、堆料遮挡。还有一类问题是工地环境干扰多在背景里有大量红色或黄色设备的情况下模型会把橙色围挡误检成反光衣。我的做法是把颜色相近的干扰物类别单独收集几百张图片标成背景类重新训练一轮。这里要注意不要直接在测试阶段加太多“黑名单过滤”因为环境和光照一变误检的形态也会变最根本的办法还是让训练数据里包含足够的负样本。6.4 关于推理速度的实测数据我实测量了一下不同档位推理耗时普通办公电脑RTX 3060和工地边缘盒子Jetson Orin Nano各测了一轮模型输入尺寸RTX 3060耗时Jetson Orin Nano耗时是否推荐yolov8n64012ms40ms边缘部署推荐yolov8s64022ms80ms实时性够用精度更佳yolov8s96045ms160ms精度高但边缘盒子吃力我最终在正式场景里用的是yolov8s、640分辨率、2帧检测一次配合一台普通的工控机就能跑通8路视频流。边缘盒子那边只部署了峰值负荷较小的4路并把分辨率降到640置信度阈值调到0.35勉强控制在100ms以内。7. 后续扩展思路与个人心得这个项目做完之后我的下一步计划是把检测结果与现场人员定位系统打通。现在的检测只是纯视觉识别如果配合一个低成本的蓝牙定位基站就能在告警时同时知道“哪个人、在哪个区域、没戴安全帽”这样安全员不用满工地找违规人员直接照着地图坐标过去就行。如果你想继续深挖我再给两个方向。一个是把代码迁移到无人机巡检场景无人机飞一圈俯拍图像里的行人和安全帽目标尺度更小可以考虑引入切片检测方案。另一个方向是模型蒸馏用小模型去学大模型的输出分布这样在工地现有的老旧设备上也可能获得更高帧率。最后说说我个人体会最深的一点这类安全检测项目里“数据工程”比“模型工程”重要得多。别急着调那个学习率、换那个骨干网络先用一周时间把训练数据的场景覆盖度、标注一致性做得扎扎实实模型的精度自然而然就上去了。我这次从78%提升到93%绝大部分功劳是数据层面的模型本身只做了一点点微调。如果你刚启动类似项目建议先把摄像头能拍到的所有角度、时间段、天气情况都摸一遍再动手训练绝对不亏。
阅读完成 · 觉得有帮助?