
简介本资源面向深度学习与计算机视觉方向的学习者及开发者提供一套基于改进YOLOv5的人群密度检测系统完整实现方案。项目以FasterNet替换原主干网络提升推理速度并引入Soft-NMS与最优运输分配OTA优化损失函数缓解人群重叠漏检问题可应用于商场、车站、体育场等密集场景的实时人流统计与安全监控。压缩包共267个文件约19.77MB涵盖53个Python脚本、49个YAML配置、28个JavaScript与26个map前端资源以及JPG/PNG示例图、Dockerfile部署文件、ipynb实验笔记和模型权重等结构完整、模块清晰。目前已有205人学习下载。读者可据此掌握主干网络替换、损失函数改进与检测系统集成的完整流程并借助数据增强与部署配置快速复现实验、迁移到自有场景。1. 从“数人头”到“看密度”YOLOv5 人群密度检测到底在做什么地铁早高峰的进站口摄像头画面里人头攒动你盯了十分钟也数不清到底有多少人。传统做法是拿个计数器手动点或者用回归模型直接预测一个密度图但前者不现实后者在遮挡严重时误差大得离谱。基于 YOLOv5 的人群密度检测系统思路是把画面切成网格用目标检测的方式把每个人头框出来再根据框的数量和分布估算密度等级。它解决的不是“精确到个位数”的问题而是“当前区域是稀疏、正常还是拥挤”的快速判断。适合做安防监控、景区人流预警、商场客流分析的工程师也适合想拿 YOLOv5 练手但不想只跑 COCO 数据集的开发者。你不需要从零设计网络核心工作在于数据集标注策略、密度分级逻辑和部署时的后处理调参。2. 为什么选 YOLOv5 而不是密度图回归选型理由与数据准备2.1 检测框 vs 密度图两种路线的真实差异人群密度检测主流有两条路一是密度图回归用 CSRNet、MCNN 这类模型直接输出一张密度热力图积分得到人数二是目标检测用 YOLO 系列把人头框出来数框。密度图回归在极度拥挤、人头只有几个像素时理论上限更高但训练数据需要点标注生成高斯核标注成本高而且模型输出不可解释——你很难跟业务方解释为什么这里密度值是 0.8。YOLOv5 检测框的好处是每个框都看得见误检漏检能逐帧排查后处理灵活比如可以按框的置信度过滤、按区域统计。代价是当人头小于 16x16 像素时YOLOv5 的召回会明显下降。我的经验是如果画面里人头最小边大于 20 像素优先选 YOLOv5如果全是蚂蚁大小的人头密度图回归更合适。热搜词里“yolov5网络结构图”被频繁搜索说明很多人想改结构但人群密度场景下改 Backbone 的收益远不如把数据标注和 Anchor 调好。2.2 数据集标注头框怎么画才不坑自己人群密度数据集常见的有 ShanghaiTech、UCF-QNRF但它们给的是点标注不是框。你要么自己转成框要么直接标。我一般用 LabelImg 或 CVAT标注规则是以头顶为中心框的宽高取头部可见区域不要框到肩膀。如果人头严重遮挡只露一半框只画可见部分置信度标 1。注意不要因为遮挡就标成 0 或忽略否则模型会学到“遮挡背景”的错误关联。数据集划分按场景分不要随机分——同一场景的连续帧如果同时出现在训练和验证集验证指标会虚高。通常 7:2:1训练集至少 3000 张否则 YOLOv5s 容易过拟合。热搜词“yolov5训练自己的数据集”背后最大的坑就是标注不一致同一个人头今天画大明天画小模型会懵。2.3 用 YOLOv5 官方仓库跑通第一个 baseline先克隆仓库、装依赖然后用预训练权重跑一次推理确认环境没问题。命令如下git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt python detect.py --weights yolov5s.pt --source data/images --conf 0.25逻辑说明detect.py是推理入口--weights指定预训练权重--source可以是图片、视频或摄像头编号--conf是置信度阈值。参数说明--conf 0.25是默认值人群场景建议先调到 0.3 减少误检但会漏掉一些遮挡人头需要根据验证集 PR 曲线权衡。跑通后把data/coco128.yaml复制一份改成自己的数据集配置重点改train、val、nc类别数和names。人群密度检测通常只设一个类person或head。如果只检测人头nc1names: [head]。3. 训练自己的密度检测模型超参数、Anchor 与损失曲线3.1 超参数怎么调从 yolov5s 到 yolov5m 的取舍YOLOv5 提供 s/m/l/x 四个规模人群密度场景我推荐从 yolov5s 起步如果验证集 mAP 低于 0.6 再换 yolov5m。输入尺寸--img-size默认 640但人群密集时小目标多可以提到 960 或 1280代价是显存翻倍。Batch size 根据显存来8G 显存跑 640 尺寸用 16跑 960 用 8。学习率用默认的--lr0 0.01配合--cos-lr余弦退火但如果你加载预训练权重--lr0要降到 0.001 左右否则预训练特征会被破坏。热搜词“yolov5超参数”里很多人问--hyp文件其实人群密度检测最该改的是--anchor。默认 Anchor 是基于 COCO 的人头框偏小偏方用python train.py --data your.yaml --weights yolov5s.pt --img-size 640 --batch-size 16 --epochs 100 --anchor自动聚类生成新 Anchor。注意自动 Anchor 需要先跑一次--noautoanchor关闭默认再手动执行 k-means 脚本或者直接用--anchor让训练脚本在开始时聚类。3.2 训练命令与关键参数逐条解释python train.py \ --data data/head_density.yaml \ --weights yolov5s.pt \ --img-size 960 \ --batch-size 8 \ --epochs 150 \ --hyp data/hyp.scratch.yaml \ --lr0 0.001 \ --cos-lr \ --anchor \ --name head_exp1逻辑说明--data指向数据集配置--weights加载预训练--img-size 960提升小目标召回--batch-size 8适配 8G 显存--epochs 150人群数据通常 100-200 轮收敛--hyp用默认增强配置--lr0 0.001微调学习率--cos-lr余弦退火--anchor自动聚类--name指定输出目录。参数说明如果训练 loss 震荡把--lr0再降到 0.0005如果验证集 mAP 不升检查标注是否有漏标。训练过程中用tensorboard --logdir runs/train看曲线重点看val/obj_loss和metrics/mAP_0.5。人群密度检测的 mAP 通常比 COCO 低因为人头小且遮挡多0.5-0.7 算正常。3.3 数据增强 mosaic 和 mixup 在人群场景的副作用YOLOv5 默认开启 Mosaic 和 MixUp前者把四张图拼成一张后者两张图按比例混合。在人群密度场景Mosaic 能增加小目标数量但会引入不自然的边界导致模型在真实密集场景下把相邻人头误判为拼接边界。我的做法是前 100 轮开 Mosaic后 50 轮关掉让模型适应真实分布。MixUp 在人群场景容易把两个人头叠成半透明反而增加漏检建议关掉。在hyp.scratch.yaml里把mixup: 0.0mosaic: 1.0改成mosaic: 0.5或训练后期设 0。热搜词“yolov5后处理”也涉及 NMS人群密集时默认 NMS 的 IoU 阈值 0.45 会把人头挨得近的框误删可以调到 0.6但会引入重复框需要配合置信度阈值一起调。4. 部署到边缘设备树莓派、RK3568 与 ROS 小车的落地差异4.1 树莓派 4B/5 上跑 YOLOv5 的量化与推理速度树莓派 4B 跑 YOLOv5s 原生 PyTorch 模型单帧推理约 1.5-2 秒基本不可用。必须转 ONNX 或 NCNN再用 TensorRTJetson或 OpenVINOIntel加速。树莓派 5 的 CPU 提升明显但 GPU 驱动仍是坑。我一般用 NCNN 部署流程python export.py --weights best.pt --include onnx导出 ONNX再用onnx2ncnn转 NCNN最后用benchncnn测速。树莓派 4B 上 NCNN 的 YOLOv5s 约 300-500ms树莓派 5 约 150-250ms。如果要求实时建议输入尺寸降到 320但小目标召回会掉。热搜词“树莓派4b部署yolov5”和“树莓派5上部署自己训练的yolov5模型”核心难点在内存4B 只有 1G/2G/4G 版本跑 640 尺寸容易 OOM用--img-size 320并开--halfFP16能缓解。4.2 RK3568 量化从 ONNX 到 RKNN 的完整链路RK3568 带 NPU算力 0.8TOPS适合低功耗人群检测。量化流程先导出 ONNX再用 RKNN-Toolkit2 转 RKNN。关键步骤from rknn.api import RKNN rknn RKNN() rknn.config(mean_values[[0, 0, 0]], std_values[[255, 255, 255]], target_platformrk3568) rknn.load_onnx(modelbest.onnx) rknn.build(do_quantizationTrue, datasetquant_dataset.txt) rknn.export_rknn(best.rknn)逻辑说明config设置归一化参数load_onnx加载模型build开启量化dataset是量化校准集路径export_rknn输出 RKNN 模型。参数说明量化校准集需要 100-200 张真实场景图否则量化误差大。RK3568 上 YOLOv5s 量化后推理约 50-80ms但 mAP 可能掉 3-5 个点。如果掉太多改用混合量化对敏感层保持 FP16。热搜词“yolov5量化rk3568”的坑在于RKNN 对 Focus 层和 SiLU 激活支持不好导出 ONNX 时要把 Focus 换成 ConvSiLU 换成 ReLU否则转换失败。4.3 ROS 无人小车上的集成话题订阅与检测结果发布ROS 小车做人群密度检测通常用 USB 摄像头或深度相机。节点订阅/camera/image_raw推理后发布/density/result消息类型自定义Density.msg包含人数和密度等级。代码框架import rospy from sensor_msgs.msg import Image from cv_bridge import CvBridge import cv2 import torch rospy.init_node(density_detector) bridge CvBridge() model torch.hub.load(ultralytics/yolov5, custom, pathbest.pt) pub rospy.Publisher(/density/result, Density, queue_size10) def callback(msg): frame bridge.imgmsg_to_cv2(msg, bgr8) results model(frame) count len(results.pandas().xyxy[0]) density high if count 20 else low pub.publish(Density(count, density)) rospy.Subscriber(/camera/image_raw, Image, callback) rospy.spin()逻辑说明cv_bridge转换 ROS 图像到 OpenCVmodel加载 YOLOv5results.pandas().xyxy[0]获取检测框count是人数density按阈值分级。参数说明阈值 20 需要根据画面覆盖面积调整如果小车视野窄10 人就算高密度。ROS 小车的坑是推理耗时导致话题延迟建议用单独线程做推理或者用message_filters做时间同步。热搜词“yolov5 ros无人小车”和“yolov5锥桶”常一起出现因为无人小车既要检测人也要检测锥桶多类别时nc2names: [person, cone]但人群密度只统计 person 类。5. 避坑与排查标注、训练、部署中最容易翻车的 5 个点5.1 现象验证集 mAP 很高但实际画面漏检严重原因验证集和训练集来自同一场景的连续帧分布太像模型过拟合。解决按场景划分数据集验证集必须包含不同光照、不同密度的场景。如果已经训完用python val.py --data your.yaml --weights best.pt --task test在独立测试集上跑mAP 掉超过 10 个点就说明过拟合。5.2 现象训练 loss 不降或降了又反弹原因学习率太大或者 Anchor 不匹配。解决先把--lr0降到 0.0005如果还不行用--anchor重新聚类。人群密度检测的 Anchor 通常比 COCO 小比如[10,13, 16,30, 33,23]这种。检查runs/train/exp/weights下的best.pt和last.pt如果 best 出现在前 20 轮说明模型很快过拟合需要加数据或加增强。5.3 现象树莓派上推理报错 “Segmentation fault”原因NCNN 模型转换时输入尺寸不匹配或者内存不足。解决确认onnx2ncnn转换时--inputshape和训练时一致树莓派上把--img-size降到 320并开--half。如果还崩用dmesg看是不是 OOM换 4G 版树莓派。5.4 现象RK3568 量化后检测框全乱原因量化校准集和实际场景差异大或者 Focus 层没替换。解决校准集用真实场景图至少 200 张导出 ONNX 时把 Focus 换成 ConvSiLU 换成 ReLU。如果还乱关掉量化用 FP16 跑速度慢但精度保。5.5 现象ROS 小车检测延迟高话题不同步原因推理在主线程阻塞了回调。解决把推理放到单独线程用queue_size1丢弃旧帧或者用message_filters.ApproximateTimeSynchronizer同步图像和检测结果。如果小车算力不够把检测频率降到 5Hz密度等级不需要每帧更新。6. 进阶技巧用密度分级替代绝对计数以及一个验证习惯人群密度检测系统最容易被业务方问“到底多少人”但绝对计数在遮挡场景下永远不准。我的做法是输出密度等级稀疏5人/百平米、正常5-15、拥挤15-30、危险30。分级阈值根据摄像头覆盖面积换算比如 640x480 画面覆盖 50 平米那么 10 人对应 20 人/百平米属于正常。这样即使漏检 2-3 人等级不会跳变。验证时我习惯用一段 5 分钟的真实视频每 10 秒抽一帧人工数人头和模型输出对比算平均绝对误差。如果 MAE 小于 3且等级准确率大于 90%就算可用。另一个技巧是后处理加一个“密度平滑”连续 5 帧的计数取中位数避免单帧误检导致报警。代码片段from collections import deque counts deque(maxlen5) def smooth_count(new_count): counts.append(new_count) return sorted(counts)[len(counts)//2]逻辑说明deque固定长度 5每次追加新计数返回中位数。参数说明maxlen根据帧率调25fps 下 5 帧约 0.2 秒延迟可接受。这个习惯帮我省了很多后悔药——有次单帧误检 50 人平滑后直接压到 8 人避免了一次误报警。部署前一定用真实视频跑一遍别只看验证集 mAP。希望帮到你。本文还有配套的精品资源点击获取