尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

YOLOv11轨迹跟踪与热力图:零售客流统计实战

发布时间:2026/9/19 1:48:16

资讯中心
01
ARTICLE

YOLOv11轨迹跟踪与热力图:零售客流统计实战

YOLOv11轨迹跟踪与热力图:零售客流统计实战
简介这份PDF文档面向零售行业数据分析人员、计算机视觉初学者及目标检测工程实践者系统讲解如何用YOLOv11完成客流量统计中的轨迹跟踪与热力图生成。文档共43页支持目录章节跳转与阅读器左侧大纲快速定位内容完整、图表清晰压缩包内仅含1个PDF文件大小约2.24MB便于随身查阅。已有94人学习。内容从零售业客流量统计的现状与挑战切入依次展开YOLOv11网络结构、训练与推理流程、轨迹跟踪算法分类与实现、核密度估计与网格计数法生成热力图并给出系统整合、性能优化及实验结果分析。读者可据此掌握从数据标注、模型训练到多目标跟踪评估指标MOTA、MOTP、ID Switches的完整链路理解遮挡、光照变化与实时性问题的应对思路并参考大型超市、时尚专卖店、便利店与购物中心等应用案例将检测结果转化为店铺布局优化与营销决策依据。1. 零售客流统计为什么绕不开 YOLOv11 加轨迹跟踪这条链路一家 300 平米的社区超市门口装了两个摄像头老板想知道「今天进店多少人、哪个货架停留最久、收银台排队有没有堵住通道」。传统做法是人工数人头或者用红外对射计数器前者费人后者只能给出一个进出总数分不清谁在货架前站了 3 分钟、谁只是路过。零售业客流量统计真正要的是「人—时间—空间」三者的绑定关系而 YOLOv11 轨迹跟踪加热力图生成恰好能把这条链路串起来检测负责找到人跟踪负责给每个人一个稳定 ID热力图负责把 ID 的坐标累积成空间分布。这条链路的价值不在于单帧检测有多准而在于跨帧的一致性。YOLOv11 相比前代在骨干网络和检测头上做了调整小目标召回和推理速度更适合门店这种 1080P、人流密集、遮挡频繁的场景。但只做检测同一帧里两个人挨着就会来回跳 ID热力图会糊成一片。所以标题里的「轨迹跟踪」不是可选项而是热力图能不能用的前提。适合读这篇的人做过目标检测但没落地过客流统计的算法工程师、需要给门店做数字化改造的 IT 负责人以及想把 YOLOv11 用到实际业务里的开发者。2. YOLOv11 检测与轨迹跟踪的工程化拆解2.1 YOLOv11 网络结构与零售场景的适配点YOLOv11 的网络结构延续了「骨干—颈部—检测头」三段式但在骨干里用了更轻量的跨阶段连接颈部做多尺度特征融合检测头改成解耦式分类和回归分开算。对零售场景来说真正影响落地的是三点一是小目标分支对远处货架前的人更敏感二是解耦头让遮挡下的分类置信度更稳三是整体参数量下降后单张 1080P 图在消费级显卡上能跑到实时。我一般会先确认输入分辨率。门店摄像头通常是 1920×1080如果直接缩到 640 训练远处的人会变成十几个像素小目标优化就无从谈起。常见做法是训练用 960 或 1280推理时保持同样尺度或者用切片推理把大图切块再合并。下面这段是加载模型并做一次推理的最小代码重点看输入尺寸和置信度阈值这两个参数。from ultralytics import YOLO # 加载 YOLOv11 检测模型这里用官方预训练权重起步 model YOLO(yolo11m.pt) # 对单张门店图片做推理 results model.predict( sourcestore_01.jpg, imgsz1280, # 输入分辨率门店远景建议不低于 960 conf0.35, # 置信度阈值人流密集时可降到 0.25 减少漏检 iou0.5, # NMS 的 IoU 阈值遮挡多时适当调高 classes[0], # 只保留 person 类COCO 里 person 的 id 是 0 saveTrue, # 保存推理结果图方便肉眼核对 projectruns/detect, namestore_01 )逻辑说明imgsz决定特征图尺度直接关系到小目标能不能被检测到conf控制召回和误检的平衡零售场景宁可多检一点因为后续跟踪会过滤掉瞬时误检classes[0]把非人目标全部丢掉减少跟踪器的无效计算。参数改动的判断依据是如果热力图边缘出现大量孤立点说明conf太低如果货架前的人经常丢说明imgsz不够或iou太低。2.2 轨迹跟踪从 ByteTrack 到 BoT-SORT 的选型检测只给框跟踪才给 ID。YOLOv11 官方生态里集成了 ByteTrack 和 BoT-SORT 两种跟踪器调用方式很简单但选型有讲究。ByteTrack 靠两次关联先匹配高置信度框再用低置信度框补漏速度快适合人流中等、遮挡不极端的门店。BoT-SORT 在 ByteTrack 基础上加了相机运动补偿和 ReID 特征适合摄像头有轻微晃动、或者人走出画面再回来需要重新识别的场景。跟踪器关联策略是否用 ReID适用场景额外开销ByteTrack高低分两次匹配否固定机位、人流适中低BoT-SORT匹配 运动补偿可选机位微晃、回头客识别中BoT-SORT-ReID匹配 外观特征是遮挡严重、跨镜追踪高调用跟踪的代码和检测几乎一样只是把model.track换上去并指定跟踪配置。from ultralytics import YOLO model YOLO(yolo11m.pt) # 对视频流做跟踪persistTrue 表示帧间保持跟踪状态 results model.track( sourcestore_entrance.mp4, imgsz1280, conf0.3, iou0.5, classes[0], trackerbytetrack.yaml, # 可换成 botsort.yaml persistTrue, # 视频流必须开启否则每帧重新初始化 streamTrue, # 逐帧返回避免一次性占满内存 saveTrue ) for r in results: if r.boxes.id is not None: # boxes.id 就是每个行人的跟踪 ID ids r.boxes.id.cpu().numpy() xyxy r.boxes.xyxy.cpu().numpy() print(ids, xyxy)逻辑说明persistTrue是视频跟踪的关键不开的话每帧都被当成新序列ID 会疯狂跳变streamTrue让结果逐帧产出长视频不会爆内存r.boxes.id是跟踪器分配的 ID热力图就是靠这个 ID 把同一人的坐标串起来。参数上tracker指向 YAML 配置文件里面能改track_high_thresh、track_buffer这些值track_buffer决定人消失多少帧后 ID 才注销门店场景建议设到 30 帧以上避免人蹲下拿货就被判离场。2.3 环境配置与训练自己的模型YOLOv11 环境配置的坑主要集中在 CUDA 版本和 PyTorch 的匹配上。常见做法是先用nvidia-smi看驱动支持的 CUDA 上限再去 PyTorch 官网找对应版本的安装命令最后装 ultralytics。不要直接pip install ultralytics就完事它会把 CPU 版 PyTorch 一起拉下来训练时才发现用不了 GPU。# 查看显卡驱动和 CUDA 版本 nvidia-smi # 按官方矩阵装对应 CUDA 版本的 PyTorch例如 CUDA 12.1 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 # 再装 ultralytics pip install ultralytics # 验证 GPU 是否可用 python -c import torch; print(torch.cuda.is_available())训练自己的模型时数据标注用 YOLO 格式每张图一个 txt每行是类别 x_center y_center width height坐标归一化到 0 到 1。门店场景建议至少标 3000 张覆盖早中晚不同光照、节假日不同人流密度。训练命令里epochs和batch要按显存调imgsz和推理保持一致。yolo detect train \ datastore_person.yaml \ modelyolo11m.pt \ epochs100 \ imgsz1280 \ batch8 \ device0 \ patience20逻辑说明patience20表示 20 轮没提升就早停省时间batch8是 1280 分辨率下 8G 显存的保守值显存够可以往上加data指向的 YAML 里写清训练集、验证集路径和类别名。训练完看runs/detect/train下的混淆矩阵如果 person 的漏检率高优先补远景和遮挡样本而不是盲目加轮数。3. 热力图生成从轨迹坐标到空间分布3.1 坐标累积与高斯核的数学处理热力图的本质是把每个人的脚点坐标投到一张和画面同尺寸的累加图上再对每个点做高斯扩散最后归一化成颜色。脚点一般取检测框底边中点因为人头会动脚的位置更接近人实际站的地方。高斯核的大小决定热力图的「糊」的程度核太小会看到一个个孤立亮点核太大整个画面变成一团。import numpy as np import cv2 def build_heatmap(frame_shape, points, sigma25): frame_shape: (h, w) points: [(x, y), ...] 脚点坐标列表 sigma: 高斯核标准差控制扩散范围 h, w frame_shape[:2] heat np.zeros((h, w), dtypenp.float32) for x, y in points: # 在脚点位置累加 1 if 0 int(y) h and 0 int(x) w: heat[int(y), int(x)] 1 # 高斯模糊做扩散ksize 取 sigma 的 6 倍左右保证覆盖 ksize int(sigma * 6) | 1 heat cv2.GaussianBlur(heat, (ksize, ksize), sigma) # 归一化到 0-255 并转成伪彩色 heat heat / (heat.max() 1e-6) heat (heat * 255).astype(np.uint8) heat_color cv2.applyColorMap(heat, cv2.COLORMAP_JET) return heat_color逻辑说明先累加再模糊顺序不能反否则每个点的高斯核会互相干扰sigma是核心参数门店通道宽度大概 1.5 米对应画面里约 100 像素sigma取 20 到 30 比较合适COLORMAP_JET是常见配色蓝低红高如果要叠加在原图上用cv2.addWeighted按 0.4 的透明度混合。3.2 用跟踪 ID 过滤避免热力图被误检污染直接拿检测框做热力图最大的问题是误检和瞬时抖动。一个人被检测到 10 帧其中 2 帧误检在货架边缘热力图就会在货架上多出一块红斑。用跟踪 ID 过滤的思路是只有同一个 ID 连续出现超过 N 帧才把它的坐标计入热力图。这样瞬时误检因为 ID 存活时间短自然被排除。from collections import defaultdict # 记录每个 ID 的轨迹和出现帧数 tracks defaultdict(list) min_frames 15 # 至少连续出现 15 帧才计入热力图 for r in results: if r.boxes.id is None: continue ids r.boxes.id.cpu().numpy().astype(int) xyxy r.boxes.xyxy.cpu().numpy() for tid, box in zip(ids, xyxy): # 取底边中点作为脚点 cx (box[0] box[2]) / 2 cy box[3] tracks[tid].append((cx, cy)) # 过滤掉帧数不足的轨迹 valid_points [] for tid, pts in tracks.items(): if len(pts) min_frames: valid_points.extend(pts) heat_color build_heatmap(frame_shape, valid_points, sigma25)逻辑说明min_frames是过滤强度的旋钮设太小过滤不掉误检设太大短时间停留的人会被漏掉门店场景 15 到 25 帧比较稳tracks用字典按 ID 聚合最后统一出图适合离线分析如果是实时看板就改成滑动窗口只保留最近 5 分钟的轨迹。3.3 相关热力图阈值与信号热力图的调参思路热力图出来之后怎么判断「哪个区域算热」需要一个阈值。常见做法是取热力值的 80 分位数作为高亮阈值超过阈值的区域标红低于阈值的保持冷色。这个阈值不是固定的早高峰和晚高峰的人流基数不同固定阈值会导致早高峰全红、晚高峰全蓝。更稳的做法是按当天总客流做归一化或者用滑动窗口的动态分位数。信号热力图的思路类似只是把「人」换成「停留时长」。每个人在某个区域的停留时间累加得到的是时间维度的热力图能看出哪个货架真正留住了人而不是只是路过。相关热力图阈值在这里的作用是区分「经过」和「驻足」一般把停留超过 3 秒的点才计入信号热力图阈值就设在 3 秒对应的帧数上。4. 落地排错与进阶技巧4.1 ID 跳变和热力图拖尾的排查顺序ID 跳变是轨迹跟踪最常见的故障表现是热力图上同一个人被画成两条断开的轨迹。排查顺序是先看检测框是否稳定如果同一帧里人的框忽大忽小跟踪器匹配就会失败这时候要回去调conf和iou再看track_buffer是否太小人短暂被遮挡后 ID 注销重新出现就是新 ID把track_buffer加到 30 以上最后看画面里有没有相似外观的人并排走ByteTrack 不带 ReID这种情况会互换 ID换成 BoT-SORT-ReID 能缓解。热力图拖尾是另一个典型问题表现为热区边缘有一条细长的尾巴。原因是脚点坐标在帧间抖动尤其是人走动时框底边会上下跳。解决办法是对脚点做滑动平均或者用卡尔曼滤波平滑轨迹。我一般会在轨迹层面加一个简单的均值滤波窗口取 5 帧拖尾基本就消失了。4.2 小目标优化与推理结果保存的实用配置远景小目标是零售场景的老大难YOLOv11 的小目标优化可以从三个方向入手提高输入分辨率、增加小目标检测头、用切片推理。提高分辨率最直接但显存和速度会涨增加检测头需要改网络结构适合有训练资源的团队切片推理是把大图切成带重叠的小块分别检测再合并对远处货架效果好代价是推理时间翻倍。推理结果保存方面除了saveTrue存图更实用的是把每帧的 ID 和坐标存成 CSV 或 JSON方便后续做停留时长统计和报表。下面这段把跟踪结果逐帧写进 CSV。import csv with open(tracks.csv, w, newline) as f: writer csv.writer(f) writer.writerow([frame, track_id, x1, y1, x2, y2]) for frame_idx, r in enumerate(results): if r.boxes.id is None: continue ids r.boxes.id.cpu().numpy().astype(int) xyxy r.boxes.xyxy.cpu().numpy() for tid, box in zip(ids, xyxy): writer.writerow([frame_idx, tid, *box])逻辑说明frame是帧序号配合视频帧率能换算出时间track_id是跟踪 ID同一个 ID 的多行就是一条轨迹坐标存原始像素值后续要换算成实际距离再除以每米像素数。这份 CSV 是热力图和停留时长报表的共同数据源比只存图片有用得多。4.3 从热力图到经营指标的最后一步热力图本身是给人看的真正要进报表的是指标。常见的三个指标是进店人数去重后的 ID 总数、区域停留时长按 ID 在区域内的帧数累加除以帧率、通道拥堵指数单位时间内某区域的人数峰值。把热力图和这三个指标结合才能回答「哪个货架该补货、哪个通道该拓宽」。一个具体技巧是给热力图叠加区域掩膜。门店的货架、收银台、通道在画面里是固定位置提前画好多边形掩膜统计时只算掩膜内的点就能把「路过门口」和「在货架前停留」分开。掩膜用cv2.fillPoly生成和热力图做按位与再统计非零像素的分布。这样出来的数据才能直接对接 BI 系统而不是一张只能看的彩色图。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。