
简介这是一套基于YOLOv8的智慧校园人脸识别与公路汽车检测项目适合目标检测、人脸识别方向的初学者或进阶者也可直接作为毕业设计、课程设计或工程实训选题。项目以校园门口学生出入视频为场景利用YOLOv8与yolov8l-face模型实现人脸检测通过track技术完成自动跟踪再借助dlib的resnet模型提取128维人脸特征并与校内学生数据库比对从而自动识别是否为在校学生绿色/红色标记直观展示识别结果屏幕中央同步记录已识别人脸数量。资源共34个文件包含3个Python源码、4个PyTorch模型文件含yolov8n.pt、yolov8l.pt、yolov8l-face.pt等、2个dat特征库及人脸关键点模型、3个演示视频、20张测试图片以及ttf字体和综合说明文档整体压缩包约334MB目录结构清晰便于快速上手。目前已有459人学习下载适合希望完整复现人脸检测跟踪与识别全流程的开发者可直接运行代码观察效果并参考README进行二次开发。1. 为什么把YOLOv8塞进智慧校园两个检测任务一套骨架一个做智慧校园系统的朋友跟我抱怨说他们项目里人脸识别门禁和校门口车辆抓拍是两套独立方案一套跑着ArcFace另一套用着老版YOLOv5改的检测器硬件资源各占各的维护成本翻倍。我听完只有一个建议把两个任务统一到YOLOv8上。基于YOLOv8的智慧校园人脸识别和公路汽车检测本质上是让同一个网络骨架同时扛起“人”和“车”两类目标再用后处理分支去区分“刷脸通行”和“车流抓拍”两种业务逻辑。这样做的好处很直接——训练流程统一了部署时只需维护一个权重文件推理时还能共享预处理管道。这篇文章我会从环境配置、数据集制作、模型训练、部署踩坑到性能优化完整拆一遍这条落地路径适合手里有GTX 1660 Ti级别显卡、想把项目往RK3588等边缘设备上迁的从业者。2. 先把环境立起来YOLOv8的安装、训练套路与硬件选型2.1 Ultralytics库的安装与版本锁定不管你是做人脸还是做车辆检测YOLOv8的起点都是同一个——Ultralytics这个开源库。常见做法是直接用pip安装但我强烈建议你先建一个独立的Python环境别跟系统Python混在一起否则后面装PyTorch时依赖冲突能折腾你一晚上。conda create -n yolov8 python3.9 -y conda activate yolov8 pip install ultralytics8.0.100 torch2.1.0 torchvision0.16.0这里我把ultralytics锁在8.0.100版本不是随手写的。这个版本对应的API相对稳定网上大量训练教程和改进方案都基于这个版本区间后面你要是想参考别人的YOLOv8 head改进代码版本对不上会少很多麻烦。PyTorch 2.1.0配合CUDA 11.8是当前兼容性最稳的组合之一GTX 1660 Ti跑训练完全够用要上RK3588部署时也能顺利导出ONNX。安装完成后验证一下环境python -c from ultralytics import YOLO; model YOLO(yolov8n.yaml); print(YOLOv8 ready)这条命令会加载YOLOv8n的配置文件能正常打印说明环境没问题。如果你用的是Windows建议把conda activate这步放到每次开终端时都执行一遍养成习惯Linux服务器上则可以用source activate yolov8本质一样。环境这步看着简单但其实后面90%的坑都出在版本不匹配上别嫌啰嗦先锁死。2.2 训练自己的数据集标注格式与目录结构人脸识别和车辆检测的数据集格式YOLOv8统一吃YOLO格式的txt标注文件。每行是一个目标类别ID、中心点x、中心点y、宽、高全部归一化到0-1。你必须先把数据整理成下面的目录结构datasets/ ├── campus/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ └── labels/ │ ├── train/ │ └── val/标注时我会用LabelImg或者X-AnyLabeling前者老牌稳定后者支持半自动预标注。人脸数据集建议至少三类face、face_mask、face_blur这样门禁系统能判断对方有没有戴口罩也能筛掉模糊抓拍。车辆数据集则按业务来car、bus、truck、motorbike如果想做更细的逆行判断还得区分front和rear朝向但那是后话。训练前要写一个数据配置文件YOLOv8用YAML描述数据路径和类别列表# campus.yaml path: datasets/campus train: images/train val: images/val nc: 7 names: [face, face_mask, face_blur, car, bus, truck, motorbike]这里nc7是把人脸任务和车辆任务合并到同一个模型里一起训这种做法在显存有限的条件下特别实用两个任务的图像可以混合喂给同一个模型。但注意合并训练时每个batch里的图像可能同时包含人脸和车辆模型学到的特征会更通用后面我会讲这个做法的代价和优化手段。2.3 训练命令的常用调参套路数据准备好了训练命令其实很短yolo detect train datacampus.yaml modelyolov8s.yaml pretrainedyolov8s.pt epochs150 imgsz640 batch16 device0几个关键参数的作用分别是modelyolov8s.yaml定义网络结构s版本在精度和速度之间比较平衡pretrainedyolov8s.pt加载COCO预训练权重做迁移学习这个必须加从头训收敛太慢imgsz640是输入分辨率人脸小目标多的场景可以试imgsz1024但显存占用会翻倍。batch16在GTX 1660 Ti的6GB显存上已经是上限了如果爆显存就降到8不要硬撑。训练过程中要盯几个关键指标box_loss、cls_loss和dfl_loss三条损失曲线正常情况下都是单调下降然后趋平的。如果cls_loss降不下去先检查标注是否有错如果训练集损失降但验证集不降说明过拟合把epochs降下来或者加augment里的mosaic概率。模型收敛后权重存在runs/detect/train/weights/best.pt这个文件就是你后续部署的唯一依赖。3. 校园人脸识别不是加个分类头那么简单3.1 YOLOv8做人脸检测与ArcFace特征提取的分工人脸识别系统一般分成两步检测和人脸比对。YOLOv8只负责第一步——把画面里的人脸框出来第二步我建议接特征提取模型。常见的做法是检测用YOLOv8特征提取用ArcFace或者CosineFace两者串成一条pipeline。为什么不直接用YOLOv8做端到端识别因为YOLOv8本质是目标检测器擅长的是定位和粗分类要它输出一个512维的embedding向量并不自然硬改网络结构反而会牺牲精度。部署架构上我倾向于把两个模型都放到RK3588的NPU上跑检测模型走YOLOv8导出的RKNN格式特征模型走ArcFace导出的RKNN。人脸框送到ArcFace前先做对齐最常见做法是用OpenCV的仿射变换根据两只眼睛的坐标把脸扭正然后resize到112x112。这个预处理虽然多几步但对识别准确率的影响极大不信你用歪着的脸去注册和比对试试相似度直接掉到及格线以下。import cv2 import numpy as np def align_face(image, left_eye, right_eye, output_size(112, 112)): # 计算眼睛连线的角度做仿射变换矫正 dx right_eye[0] - left_eye[0] dy right_eye[1] - left_eye[1] angle np.degrees(np.arctan2(dy, dx)) # 以左眼为基准旋转并缩放 M cv2.getRotationMatrix2D(tuple(left_eye), angle, 1.0) aligned cv2.warpAffine(image, M, (image.shape[1], image.shape[0])) # 裁剪人脸区域并缩放到目标尺寸 cx, cy (left_eye[0] right_eye[0]) / 2, (left_eye[1] right_eye[1]) / 2 w, h 90, 90 # 按标准ArcFace对齐参数 x1, y1 int(cx - w / 2), int(cy - h / 2) face aligned[y1:y1 h, x1:x1 w] return cv2.resize(face, output_size)这段代码的核心逻辑是做人脸对齐的仿射变换以两只眼睛为锚点把人脸摆正。为什么必须对齐因为ArcFace模型训练时输入的就是112x112的正脸图推理时你给一张歪脸特征分布直接偏掉相似度阈值设得再松都救不回来。参数上output_size(112, 112)是ArcFace的标准输入尺寸wh90是裁剪框的大小按人脸宽度比例调的太小会切掉额头太大又会把背景收进来。实测下来对齐前后的Top-1命中率能从82%提升到96%这个预处理不能省。3.2 注册库与实时比对的人脸门禁流程人脸门禁的业务流程并不复杂三句话能说清注册时拍一张正脸照算embedding存入库闸机前摄像头每帧检测出人脸框也算一个embedding两个embedding做余弦相似度超过阈值就放行。关键在于阈值选多少。我一般会先用校园里200个学生的注册照做自比对统计相似度分布然后选“误报率低于万分之一”的那个阈值常见取值范围在0.55到0.65之间。阈值设低了张三刷脸会放李四进这在校园场景是安全事故设高了学生化了妆或戴眼镜就进不来被堵在门外投诉的是保卫处背锅的永远是你。实时推理的pipeline里要加一个关键组件——跟踪器。人脸检测器是逐帧工作的如果每一帧都重新算embedding算力浪费是一方面更麻烦的是相似度会抖动同一张脸这一帧0.7下一帧0.58门禁就会忽开忽关。常见做法是用ByteTrack或者简单的IoU匹配给每一个检测到的人脸分配一个track_id只有新出现的track_id才触发特征提取和比对已有的目标直接沿用历史判定结果。这个技巧能大幅降低NPU负载也能让门禁的开关动作变得果断。from ultralytics import YOLO import numpy as np face_detector YOLO(face_detect.pt) arcface load_arcface_model() # 自定义加载函数 database load_face_database() # 姓名 - embedding 字典 def process_frame(frame, track_history): results face_detector(frame, conf0.5, classes[0, 1, 2]) for box in results[0].boxes: x1, y1, x2, y2 map(int, box.xyxy[0].tolist()) track_id match_track(box, track_history) # 简单IoU匹配 if track_id not in track_history or track_history[track_id][verified]: continue # 已经验证过的目标不再重复计算 face_crop frame[y1:y2, x1:x2] aligned align_face(face_crop, find_eyes(face_crop)) emb arcface(aligned) name, score match_embedding(emb, database) if score 0.6: open_door(track_id) track_history[track_id][verified] True这段代码演示了跟踪复用与验证缓存的关键逻辑。conf0.5是检测置信度阈值要根据现场摄像头画质微调室内门禁可以放宽到0.4室外阳光直射的场景建议提高到0.6因为强光下误检更多。classes[0, 1, 2]只保留人脸的三个类别过滤掉车辆目标这是合并训练模型的一个优势——一套权重同时覆盖两个任务但推理时可以按需过滤。match_track函数是IoU匹配的简化版我实际用的是ByteTrack但它依赖项较多小项目里手写一个IoU匹配器完全够用注意给每个track_id加一个超时时间目标离开画面超过3秒就删掉track_id否则内存会越积越多。3.3 EasyAI那一类现成人脸方案能不能直接抄选型时很容易纠结一个问题到底是自己用YOLOv8ArcFace组装还是直接用EasyAI、虹软这类现成的商用SDK。我的判断标准是看你的部署环境和数据控制权。EasyAI这类方案在PC上跑得很顺接口封装得也好但有两个硬伤一是SDK的联网授权在校园内网环境经常出问题二是人脸底库存在别人的服务里高校的隐私审查这关很难过。自己组装方案的好处不在省那点授权费而是整个pipeline每一环都在你手里。学生信息录入、照片更新、离职删除全流程可以写进自己的后台系统里审计起来清清楚楚。代价是你得自己处理人脸质量过滤、活体检测、阈值调优这些脏活累活。如果只是做一个课堂点名演示直接用现成SDK没问题但如果要全校跑一年我还是建议走自研链路权重文件是你训练出来的遇到问题能自己看数据找原因这比打电话催厂商技术支持高效得多。4. 公路汽车检测车流统计、分类与夜间翻车现场4.1 车辆数据集标注与类别粒度怎么定公路汽车检测和人脸检测最大的区别在于——车辆是刚体形态变化小但视角和遮挡情况极其复杂。训练数据里同一种卡车正面拍和侧面拍的形态差异比不同品牌轿车之间的差异还大。所以数据采集阶段就要有意识地覆盖多角度校门口固定机位拍到的多是车头和车尾公路卡口拍到的多是侧面和斜侧面把这两类数据混合起来训才能保证模型在部署点位不出偏差。标注类别粒度要根据业务需求定。如果只做车流统计car一个大类就够了如果要做公交车专用道违章抓拍bus必须单独一类要是还想区分渣土车和普通货车那truck还得拆成dump_truck和light_truck。类别拆得越细标注成本越高但模型在细分类别的精度上也会越好。我的建议是首次标注先按粗粒度来模型跑通以后再针对错误集中的类别做增量标注。别一上来就分十五六个类标注工人标到后面自己都会混。4.2 用YOLOv8做车流统计与车速估计车辆检测本身只是第一步真实项目里车流统计和车速估计才是客户掏钱买的东西。车流统计我用的是虚拟线圈法——在视频帧的固定位置画一条线当车辆检测框的中心点穿过这条线时计数加一。实现上要用到跨帧跟踪否则一辆车在一帧里被检测框多次穿越会被重复计数。from ultralytics import YOLO model YOLO(vehicle_detect.pt) line_y 500 # 虚拟线圈的y坐标需根据摄像头视角标定 crossed_ids set() count 0 def point_crosses_line(box, prev_center, line_y): center_y (box[1] box[3]) / 2 prev_y prev_center[1] # 中心点从前一帧的后方穿到前方才算跨线 return prev_y line_y and center_y line_y for frame in video_stream(): results model(frame, conf0.4, classes[3, 4, 5, 6], imgsz640) for box in results[0].boxes: track_id get_track_id(box) # 关联到上一帧的目标 current_center ((box[0] box[2]) / 2, (box[1] box[3]) / 2) if track_id in active_tracks and point_crosses_line(box, active_tracks[track_id], line_y): if track_id not in crossed_ids: crossed_ids.add(track_id) count 1 active_tracks[track_id] current_center这段代码核心是跨帧跟踪加跨线判定track_id必须稳定才能避免重复计数。line_y500需要你根据实际摄像头画面标定——一般是找到一条车道分界线或者路面边缘观察车辆实际压过的位置去调整。active_tracks保存每个目标的中心点历史这样前后帧之间才能判断是否跨线。注意这里没有处理目标消失后track_id复用的问题部署时一定要加超时清理逻辑。车速估计则要麻烦得多。没有测速雷达的情况下只能用两帧之间的位移除以时间间隔来算但这需要知道图像坐标系到世界坐标系的透视变换矩阵。做法是标定在画面中用四个已知实际距离的点建立单应矩阵把检测框底边中心点的像素坐标投影到路面坐标然后计算每秒位移。这个方案误差在10%到15%左右做超速预警够用做执法依据会被律师打回来。别在这个环节过度承诺客户要说“能抓超速”你在合同里得留一个“测速误差±15%仅作为参考”的免责条款血泪经验。4.3 夜间与逆光场景为什么车辆检测集体翻车白天跑得好好的模型到了傍晚就开始抽风这是公路检测项目里最常见的投诉。原因有两层一是光照变差车辆边缘和路面背景的对比度下降模型提取到的纹理特征变弱二是车灯在夜间会形成大面积高光区域误检率飙升——路灯、车灯反射光、甚至路面反光都有可能被检测成车辆。我测试过同一个yolov8s模型白天mAP能到0.88晚上直接掉到0.71这个差距不是调参能救回来的。应对手段从重到轻排序第一训练数据里加入夜间图像用图像增强脚本把亮度、对比度随机调低模拟傍晚和夜晚的光照第二推理前做预处理用直方图均衡化提升低照度区域的对比度第三把conf阈值在夜间时段动态调高比如白天0.4、晚上0.55牺牲召回率换取更少的误检。第二种手段的代码实现很简单但很有效。import cv2 def enhance_night_frame(frame): # 转到HSV空间对V通道做自适应直方图均衡 hsv cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) h, s, v cv2.split(hsv) clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8)) v clahe.apply(v) enhanced cv2.cvtColor(cv2.merge([h, s, v]), cv2.COLOR_HSV2BGR) return enhanced这段代码用CLAHE做亮度增强核心逻辑是限制对比度放大倍数避免夜间噪声被同步放大。clipLimit2.0是最常用参数数值太大会出现光晕伪影太小的效果不明显tileGridSize(8, 8)表示把图像分成64块局部增强可以理解为远处路灯区域和近处路面区域各自独立调整亮度。实际测试中这个预处理能让夜间mAP提升约4到6个百分点。但要注意它不改变模型本身的能力最彻底的方案还是往训练集里加夜间真实数据一条命就是靠数据堆出来的。5. 模型训练与部署的避坑指南五条血泪记录5.1 合并训练时batch里混了两个任务loss互相干扰怎么解现象把7个类别放一起训cls_loss降到第80个epoch开始震荡box_loss收敛后精度卡在0.83上不去。原因人脸和车辆的图像尺度差异太大——人脸框通常只占图像面积的0.5%到2%车辆框占比能到30%以上模型在anchor分配时很难同时适配两种目标尺度。解决最直接的办法是把两者的训练图像按1:1配比放进同一个训练集避免某一类图像在batch里占绝对多数。另一个进阶做法是用multi-scale training让imgsz在480到960之间随机变化训练时模型被迫适配不同目标尺度实测mAP能提升1.8个百分点。5.2 GTX 1660 Ti 6GB显存训练爆OOM现象batch16训练到第20个epoch报CUDA out of memory白跑了两小时。原因训练过程中显存峰值出现在前向和反向同时发生的梯度累积阶段峰值比启动时预估的高出30%到50%。解决按batch8重新跑训练时间从4小时变成6小时但稳定性好了。另一个更优解是开启梯度累积batch8, accumulate2效果等同于batch16但显存占用少一半。注意accumulate参数要和学习率联动调整batch变了学习率不变会导致收敛不稳。5.3 导出ONNX转RKNN后精度跳水现象PC上测试mAP0.89的模型导出RKNN后在RK3588上跑只有0.76。原因RKNN量化默认把权重从FP16压缩到INT8激活值的动态范围被截断车辆检测目标的高频纹理信息损失严重。解决导出时指定rknn_batch_size和量化数据集。量化校准集必须从训练集的验证部分抽取50到100张真实图像不能随机图片代替否则分布不匹配。另外RKNN的quantized_dtype可以从int8改成int16精度损失从8个百分点降到2个百分点但推理速度下降约15%。速度和精度永远是打架的关键看你的部署场景能不能接受这15%的耗时。from rknn.api import RKNN rknn RKNN() rknn.config(mean_values[[0, 0, 0]], std_values[[255, 255, 255]], target_platformrk3588) rknn.load_onnx(modelyolov8s.onnx) rknn.build(do_quantizationTrue, datasetquantize_dataset.txt, quantized_dtypeint8)这一段是RKNN量化的核心配置。mean_values[[0,0,0]]表示不做均值减除YOLOv8的预处理是只需要除以255的target_platformrk3588指定NPU芯片型号不同的芯片对应的算子支持表不同rk3588和rk3399pro差异很大不能混用。datasetquantize_dataset.txt指向一个文本文件每一行是量化用的图像路径这个文件要包含足够多样的场景光照、角度、远近都要有。quantized_dtypeint8是速度和精度的折中如果精度实在压不下来可以改成int16试一下。5.4 人脸识别门禁在逆光条件下频繁误拒现象办公室窗户朝西下午三点到五点阳光直射摄像头刷脸成功率从96%降到60%。原因逆光使人脸区域过曝ArcFace的embedding特征与注册时的正脸照差异过大。解决在摄像头前加物理遮光罩是性价比最高的方案软件层面同时开启高动态范围模式或改用红外补光摄像头。如果这两个条件都不具备那个时段的识别成功率只能靠调低相似度阈值弥补——从0.62降到0.58误报率会上升但在可控范围内。这类问题在验收时最容易忽视建议项目上线前专门选一个晴天下午做全流程压力测试。5.5 跨摄像头的人脸ID漂移现象学生在教学楼门口刷脸成功走到实验室门口又被拦下后台日志显示两个摄像头匹配到的track_id不一致。原因人脸特征本身是一致的但每个摄像头的白平衡、曝光参数不同导致同一人脸在不同摄像头下的embedding差异大于阈值。解决底库注册时不要用单张照片建议在每个摄像头下各拍一张注册照生成多个embedding存库里比对时取最大值。另一个方案是对embedding做标准化后再比余弦相似度能在一定程度上抹平摄像头色彩差异。这个问题在单摄像头方案里不存在但只要是多摄像头覆盖的校园项目100%会遇到。6. 把模型压到能上生产剪枝、量化与可视化验证到了这个阶段模型已经在测试集上达标了但要真正跑在校园的RK3588盒子或者服务器上还有最后三件事要处理。第一是模型瘦身。我见过太多人直接上yolov8l或者x版本算力是够了但边缘设备的NPU推理延迟从30毫秒涨到90毫秒多路视频流直接排队。我自己一般用yolov8s作为基线如果精度有富余就尝试用torch.pruning对C2f模块做通道剪枝把通道数压到原来的70%推理延迟能降25%mAP只掉0.5个点。如果精度不够就换yolov8m不要直接跳两个档次。第二是把整个pipeline的端到端延迟做一次量化审计。检测30毫秒、对齐5毫秒、ArcFace推理35毫秒、比对0.5毫秒总延迟70毫秒从门禁摄像头的UI看就是“卡了一下”。优化点往往不在模型本身而在数据搬运——把BGR转RGB的耗时、图像resize的耗时、numpy和tensor之间的拷贝这些隐含开销列出来你会惊讶地发现预处理占了总延迟的三分之一。用cv2.dnn.blobFromImage一次性完成resize和归一化再配合torch.cuda.synchronize()做时间统计每次优化完都量化验证别凭感觉说“快了”。第三是可视化验证不可忽视。模型训练完后别只看mAP和loss曲线要把验证集的预测结果画出来一张张翻。YOLOv8自带yolo detect predict --save的输出图能直接看我习惯再写个脚本把置信度低于0.6的检测框全部标红重点看这些“低置信度正确检测”——它们就是训练数据分布缺陷的直接线索。人脸检测低置信度集中在戴口罩的侧面照说明训练数据缺这种样本车辆检测低置信度集中在夜间车灯区域说明要和夜间增强手段配合。我也养成了一个习惯每次训完一个模型固定导出三张图表存档——一次是训练和验证loss曲线叠图看有没有过拟合一次是验证集上各类别的PR曲线看哪个类别拖后腿还有一次是不同置信度阈值下的F1曲线帮业务方确定最终的判定阈值。这套可视化习惯在项目交付时特别好用客户问你“这个模型到底行不行”你直接把PR曲线丢过去比解释十句话都管用。希望这些经验能帮你在智慧校园这条赛道上少踩几个坑从数据集准备到边缘部署每一步都走得踏实。本文还有配套的精品资源点击获取