news 2026/10/19 9:02:08

YOLOv8+状态机:打造食堂排队人数监控与拥挤预警系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLOv8+状态机:打造食堂排队人数监控与拥挤预警系统

1. 毕业设计选这个题,到底在做什么

先说点实际的。每年毕业设计季,总有人对着题目列表发愁,觉得要么太水、要么太难。食堂排队情况监控这个选题,乍一看像是个“用摄像头数人头”的简单活儿,但真正做下来你会发现,它把一个完整的计算机视觉系统该有的环节全涵盖了:图像采集、目标检测、人数统计、状态判断、数据可视化、前后端联调、模型部署。说白了,这是一个五脏俱全的工程题,特别适合拿来做本科毕设,也适合想系统入门目标检测应用的人练手。

我当年选这个题的时候,导师只说了一句话:“别做花架子,做出来能用的。”这句话后来成了我整个项目的准则。很多人做类似题目,最后交上去的Demo就是在测试视频上画几个框,标几个数字,一看就是糊弄的。真正的食堂排队监控,要解决的核心问题是:在食堂这种拥挤、光线复杂、人员密集且相互遮挡的场景下,如何尽可能准确地估计当前排队人数,并判断出“拥挤程度”。

这里涉及的技术点包括但不限于:

  • 目标检测模型的选择与训练(YOLO系列是主流)
  • 数据集的采集与标注(没有公开的食堂场景数据集,得自己搞)
  • 队伍状态的判定逻辑(排队、空闲、拥挤这三个状态怎么界定)
  • 软硬件架构设计(摄像头部署、服务端推理、前端展示)
  • 实时性与准确率的平衡(不能为了准而慢到没法用)

这篇博文我会把整个流程拆开讲,从方案选型、环境搭建、数据处理,到模型训练、核心逻辑实现、系统联调,再到答辩时可能被问到的坑,全部过一遍。我不打算写成那种“一步一步照做就行”的教程,因为那样你遇到问题照样不会解决。我会把每一步背后的选择和理由也说清楚,这样你换一个场景、换一个模型,也能迁移这套思路。

先说结论:这个题目的难度上限很高,下限也可以很低。如果你只想混个及格,下载一个人脸检测模型,写个计数脚本,拍两段食堂视频交差,确实能过。但如果你想拿个不错的分数,或者真的想学点东西,那就得把“排队状态判定”这个环节做好——这才是区分平庸和优秀的分水岭。

2. 方案选型:为什么我选了YOLOv8加自定义状态机

2.1 目标检测模型选型对比

目标检测是排队人数监控的核心前置环节。你需要先知道画面里哪里有“人”,才能谈得上数人数和判断排队状态。当前可用的方案大致分几类:

方案优点缺点适用场景
传统图像处理(背景差分/肤色检测)速度快、无训练成本抗干扰极差、食堂场景基本报废固定机位、背景静止、人少
OpenCV自带HOG+SVM行人检测无需深度学习环境检测率中等、密集场景漏检严重老电脑跑demo用
基于深度学习的目标检测(YOLO/SSD/Faster R-CNN)精度高、泛化好需要标注数据、需要GPU训练本项目主力方案
商用API(云服务商的人体检测接口)省事、精度高联网依赖、按量收费、毕设不合适快速验证想法

我直接排除了前两类。食堂打饭窗口前,人的密度经常高到互相遮挡,传统的背景差分一旦有人长时间站着不动,背景模型就会被污染,误报率直接失控。HOG+SVM在密集场景下的表现更是惨不忍睹——我实际测过,漏检率能到四成以上,基本没法用。

剩下就是深度学习方案里挑具体的模型。Faster R-CNN精度好但速度太慢,在普通显卡上跑视频流会很吃力。SSD速度快一些,但在小目标上的表现一般。YOLO系列是目前综合平衡最好的选择,尤其是Ultralytics维护的YOLOv8,API简洁、训练流程成熟、部署方便,而且自带模型压缩和导出工具,对毕设这种体量的项目非常友好。

我最终选定的是YOLOv8nano版本。很多人会觉得,干嘛不用更大的模型,比如YOLOv8large,精度不是更高吗?这里有个关键考虑:食堂监控是实时系统,摄像头的视频流按25到30帧每秒进来,如果每帧推理要花200毫秒以上,画面肉眼可见地卡顿,排队人数刷新滞后,系统就失去了实用价值。我实测过,v8nano在GTX 1660上单帧推理大概7到12毫秒,v8large则要50毫秒以上。nano的检测精度在食堂场景下足够用,密漏检可以通过后处理逻辑弥补一部分。记住,工程上从来不是选“最准的模型”,而是选“在约束条件下最合适的模型”。

2.2 排队状态判定:不能只靠数人头

这是整个项目里最容易被低估的部分。我见过不少同题目的毕业设计,做法就是:人头数超过20就显示“拥挤”,少于5就显示“空闲”,中间就是“正常”。这种阈值法在固定机位、固定时段、固定角度下或许勉强能看,但一旦画面中的排队区域范围、队伍走向、人员分布发生变化,阈值就得重新调。更麻烦的是,食堂高峰期队伍不是规整的一列,而是三四人并排、前后交错、来回走动,单纯的计数完全无法反映真实的拥挤程度。

我的做法是引入一个简单的状态机:把排队区域划分为若干个检测分区,每个分区独立统计人数,再根据分区人数和时间维度上的变化趋势,综合判定当前是“空闲”“正常排队”还是“高峰拥挤”。这听起来高大上,其实核心就是几个if-else加上滤波逻辑,关键在于怎么设计这个状态机的输入信号。

具体来说,我把食堂打饭窗口前的排队区域按纵深方向切成三段:靠近窗口的取餐区、中部的排队区、远处的入口缓冲区。每个区域用透视变换映射到俯视视角后统计人数密度。然后定义三个状态:

  • 空闲:取餐区无人,排队区人数低于阈值A,且持续超过N秒
  • 正常:排队区人数在阈值A到B之间,或取餐区有人但排队区不长
  • 拥挤:排队区人数超过阈值B,或取餐区排队人数超过阈值C,持续M秒

这里有一个容易踩的坑:直接用单帧人数判断状态,会导致界面上的状态文字疯狂跳动,一会儿拥挤一会儿空闲。必须加上时间滤波——比如用滑动窗口取平均人数,或者要求某个状态必须连续若干帧满足条件才切换。我最后用的是十帧滑动平均加两秒持续确认,效果立刻稳定下来。

2.3 为什么选择RTSP摄像流而不是本地视频

毕设演示时很多人的做法是拿一段录好的视频循环播放,这是必要的,因为答辩现场一般没有真实摄像头。但整个系统的架构设计必须从真实摄像头输入出发。我采用的是RTSP拉流方案:摄像头通过RTSP协议输出视频流,服务端用OpenCV的VideoCapture或FFmpeg拉取,逐帧送入检测模型推理。

选RTSP的好处有三个。第一,真实部署时食堂用的网络摄像头基本都支持RTSP,这套代码可以直接迁移到真实环境。第二,本地视频也可以通过RTSP服务器软件转成RTSP流,演示时两者通用,不需要写两套采集逻辑。第三,RTSP流天然支持多端拉取,意味着我可以同时跑一个实时展示页面和一个录像存档进程,互不干扰。

这里提醒一句:如果你用的OpenCV拉RTSP流,建议设置一下缓冲区大小和超时重连。有次我测试时摄像头断了几秒,整个主循环直接卡死,排查了半天发现是VideoCapture内部阻塞了,前端数据源断供。后来加了个心跳检测,定期检查帧间隔,超过阈值就重新建立连接,这个问题才彻底解决。

3. 环境搭建与数据准备:这两件事能拖垮一半人

3.1 开发环境与依赖清单

我的开发环境是这样的:Ubuntu 22.04系统,Python 3.10,PyTorch 2.x配合CUDA 11.8,YOLOv8使用Ultralytics库。显卡是一块GTX 1660,6G显存,训练yolonano完全够用。如果你是纯CPU环境,也不是不能做,YOLOv8支持CPU推理,只是速度慢很多。训练环节我强烈建议至少搞到一块NVIDIA显卡,哪怕是用云服务器租GPU,也别用CPU硬扛——一个大模型训练十几个小时甚至几天,CPU环境下你得等疯掉。

依赖清单如下,不需要多,要精简:

pip install ultralytics torch torchvision opencv-python flask flask-cors numpy

解释一下每个东西的用处:ultralytics是YOLOv8的官方库,负责模型加载、推理、训练;torch是底层深度学习框架;opencv-python负责视频帧读取和图像预处理;flask和flask-cors用来搭轻量级后端服务,向前端页面提供人数和状态数据;numpy做数组运算。

这里有个容易踩的坑:OpenCV和Ultralytics的版本兼容问题。我之前用OpenCV 4.5的老版本跑ultralytics的某些新版本,导入时直接报错找不到模块。解决办法是先装ultralytics,让它自动拉取匹配的依赖,再单独装OpenCV时用pip install opencv-python-headless而不是带GUI的完整版,因为服务器上没有显示环境,带GUI版本反而容易出问题。

3.2 数据集采集与标注:没有现成数据集怎么办

食堂排队场景没有一个公开的、标注好的标准数据集,这是这个题目和常规目标检测题最大的区别。自己标注数据听起来很劝退,但实际操作下来并没有想象中那么恐怖。我统计过自己项目的最终数据量:训练集3400张图片,验证集400张,测试集200张。对于单类别“人”的检测任务,这个量级完全够用。

数据来源分三部分:

  1. 自己拿手机在不同食堂、不同时段拍的视频,截帧得到约2000张。关键点是要覆盖多种光照条件——中午窗户边的强光、晚上室内的偏暗灯光、天花板日光灯直射下的顶光,这些都会影响模型在真实场景的表现。
  2. 从公开数据集(比如COCO中person类别的图片,或者华中科技大学那边的公开行人数据集)筛选出适合食堂场景的,大概补充了800张。
  3. 数据增强在线生成。Ultralytics库内置了马赛克增强、随机翻转、色彩抖动这些操作,不需要手动处理,训练时自动做。这一份等于白送的样本多样性。

标注工具我用的LabelImg,没有别的理由,就是简单。标注时只有一个类别:person。这里有个细节值得说一下:标注框到底该框住整个人,还是只框上半身或者头部?

在食堂排队场景里,人群密集时下半身经常被桌子、前面的人挡住,如果你标注的是完整人框,遮挡严重的样本会让模型学到错误的特征。我实验过两种标注策略,最终采用的是“能看清多少就标多少”原则:被遮挡超过三分之二的人,如果还能明显分辨出是一个人,就标一个只包含可见部分的人形框;如果只剩半个头或一角衣服,直接不标。这样的好处是模型不会被各种奇形怪状的遮挡框搞混,坏处是密集场景会漏掉一些检测——但这部分漏检正好由排队状态判定逻辑兜底,因为状态判断不需要精确到每个人,只要数量级对就行。

3.3 从原始视频帧到YOLO格式的训练集

标注完成后是一堆XML文件(LabelImg默认格式),需要转成YOLO格式的txt标注文件。YOLO格式的要求是:每个图片对应一个txt文件,每一行是“类别id 中心点x坐标 中心点y坐标 框宽 框高”,所有坐标值都是相对于图片宽高的比例值,范围0到1。

转换脚本不难写,核心就几步:解析XML里每个object的bndbox坐标,除以图片宽高得到归一化坐标,然后写进txt。这里我提醒一个容易错的点:LabelImg标注时,如果图片是训练人员手动旋转过方向的,导出XML里的坐标还是基于原始图片的,你的转换脚本必须使用原始图片尺寸做归一化,不要用你在屏幕上看到的缩放后尺寸。

我做事的习惯是,所有数据文件放在固定的目录结构下:

dataset/ images/ train/ # 训练图片 val/ # 验证图片 test/ # 测试图片 labels/ train/ # 对应的txt标注文件 val/ test/

train和val的比例我按8:1划分,并且保证同一个摄像头机位拍摄的连续视频帧不会被同时分到train和val里——否则验证集准确率会被严重高估,因为你可能用高度相似的帧去验证模型,它等于变相“见过”了这些数据。我把同一个视频抽出的帧全部放进一个集合,再对集合做划分,这样才真实。

4. 模型训练与精度调优:跑通很容易,跑好需要抠细节

4.1 训练启动与参数选择

Ultralytics封装好了训练入口,命令相当简洁:

yolo detect train data=my_dataset.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16

但这里的每个参数都值得认真想一下,不是随便填的。

  • model=yolov8n.pt:这里填的是预训练权重。Base模型提前在COCO上训练过,知道“人”长什么样,从它开始做迁移学习,比从头训练收敛快得多,而且需要的样本量少很多。记住,毕设项目不是发论文,没必要从零开始训练。
  • imgsz=640:输入图像的缩放尺寸。食堂监控画面一般是1920x1080或2560x1440,直接塞进网络肯定不行,得缩放。640是速度和精度的平衡点。把尺寸调到1088会提升一些精度,但推理速度会慢,对实时系统来说不值。
  • epochs=100:训练轮数。我用的是早停机制,实际训练到70多轮就停了,因为验证集上的mAP已经连续多轮不涨。你要是不想手动看,可以直接在命令里加patience=15,让它在连续15轮没改善时自动停。
  • batch=16:显存6G的情况下,batch=16配合imgsz=640刚刚好,再大就爆显存。

训练过程中留意一下输出的loss曲线和PR曲线。不要只看最终arrival的precision和recall数字,要特别关注置信度阈值的选择。YOLO会输出每个框的置信度分数,你需要设定一个阈值,高于它才被认为是“人”。阈值高了误报少但漏检多,低了漏检少但误报多。我在训练完成后用验证集扫了一遍不同置信度阈值下的F1值,最后选了0.4,这个值在食堂场景下比较稳。

4.2 精度调优三步走:哪些手段真实有效

我分享一下实际试过且有效的调优手段,排掉那些网上吹得天花乱坠但实际没用或者性价比极低的操作。

第一步,增加数据来源多样性。前面说了数据要覆盖不同时段、不同光照,这一步对精度提升的效果最大。我第一版模型只用了一个中午时段的视频训练,晚上一测,漏检率肉眼可见地上升。后来补充了晚上和阴天的数据,这个问题基本消失。

第二步,调整标注策略。把框得死死的完整人框改成“可见部分标注”,模型mAP反而涨了2到3个百分点。道理不复杂:模型学到的是“这个物体长这样”的一致性特征,而不是被各种遮挡形状干扰。

第三步,处理类别不平衡。我的数据集里只有person一个类别,不存在传统意义上的类别不平衡问题。但存在尺度的不平衡:远处的人像素小、标注框小,模型在小目标上的表现天然弱于大目标。Ultralytics提供了multi-scale训练参数,开启后训练时每次随机缩放图像尺寸,相当于变相增加小目标的样本权重。我开了这个选项,小目标的检测率确实有改善。

有几件事我劝你别浪费时间:超参数自动搜索(AutoTune)听着高级,但一跑就是几百轮训练,毕设时间耗不起;模型剪枝和蒸馏对nano这种小模型来说收益极低;换更大的预训练模型做初始化(比如用yolov8s.pt),提升不明显,但训练和推理时间都增加。

4.3 从pt权重导出ONNX:部署时不能少的一步

模型训练完成后,我们要把它接到后端服务上实时推理。直接用ultralytics的Python API加载pt权重也能跑,但有两个问题:一是导入整个ultralytics框架会增加约2秒的启动时间和额外内存开销;二是如果你后续想用C++或者嵌入式设备部署,pt权重没法直接用。

我选择了导出ONNX格式:

from ultralytics import YOLO model = YOLO('best.pt') model.export(format='onnx', opset=12, dynamic=True)

然后推理时用onnxruntime代替ultralytics加载:

import onnxruntime as ort import cv2 import numpy as np session = ort.InferenceSession('best.onnx') input_name = session.get_inputs()[0].name def infer_frame(frame): img = cv2.resize(frame, (640, 640)) img = img[:, :, ::-1].transpose(2, 0, 1) # BGR转RGB并调整通道顺序 img = np.ascontiguousarray(img, dtype=np.float32) / 255.0 preds = session.run(None, {input_name: img[None]}) # 解析preds,过滤低置信度框,非极大值抑制 return boxes

导出ONNX后推理速度快了大概10%到20%,更重要的是启动时间大大缩短。我实际测试中,普通ultralytics加载模型加首帧推理大约2.5秒,换成onnxruntime后只需200毫秒介入,整个服务启动几乎秒开。

这里有个细节:导出的ONNX模型如果使用了dynamic shape,显存和内存占用会比固定shape略高一点,但优势是输入分辨率可以不固定。我建议导出时直接固定shape到640x640,因为实际推理时我们会把每一帧缩放到这个尺寸,dynamic没有任何收益。

5. 排队逻辑与状态判定:从“数人头”到“判断拥挤”的核心设计

5.1 排队区域的划分与透视变换

状态判定不是全局统计,而是分区域的。我在系统初始化时,读取一张不带头部检测框的干净背景图,用鼠标手动标定三个区域:取餐区、排队区、入口区。每个区域存为一组多边形顶点坐标。

标定完区域后,有个问题:直接用原始俯视角度下的坐标框统计人数,镜头近处的人框大,远处的人框小,容易导致远近密度判断失真。我做了透视变换,把每个区域都映射成俯视矩形,然后统计映射区域内的检测框中心点数量。

透视变换的矩阵计算用OpenCV就行:

pt_src = np.array([[x1, y1], [x2, y2], [x3, y3], [x4, y4]], dtype=np.float32) # 原图中的区域多边形四个顶点 pt_dst = np.array([[0, 0], [width, 0], [width, height], [0, height]], dtype=np.float32) matrix = cv2.getPerspectiveTransform(pt_src, pt_dst)

然后每个检测框的中心点做一次透视变换,变换后的坐标在哪个矩形区域内,就归属哪个分区。这个逻辑稳定可靠,而且非常直观,答辩时画出来也好看。

注意,食堂的排队区域不是一成不变的。卖饭窗口前可能摆着保温餐台,高峰期窗口前排的队伍走向也会变。我当时标定的区域是静态的,运行期间不自动调整。如果要做自动适配,那会牵扯到更多视频分析的内容,对毕设来说性价比太低。所以我选择把区域标定做成一个配置文件,允许离线微调,而不是追求全自动。

5.2 状态机实现细节与防抖

状态机的实现我完整跑过了,这里给出核心代码框架,方便你直接参考:

class QueueStateMachine: def __init__(self, low_thresh=5, high_thresh=20, confirm_frames=15): self.state = 'idle' self.low = low_thresh self.high = high_thresh self.confirm = confirm_frames self.full_window = deque(maxlen=confirm_frames) def update(self, counts: dict): region_people = counts['queue'] + counts['pickup'] self.full_window.append(region_people) avg = np.mean(self.full_window) if self.state in ('idle', 'normal') and avg >= self.high: self.state = 'crowded' elif self.state in ('crowded', 'normal') and avg <= self.low: self.state = 'idle' elif self.state == 'idle' and avg > self.low: self.state = 'normal' elif self.state == 'crowded' and avg < self.high: self.state = 'normal' return self.state, avg

核心逻辑就两句话:状态切换必须满足“平均人数越过阈值”和“持续确认帧数填满窗口”两个条件。注意我用的是滑窗均值而不是瞬时值,因为瞬时值会被偶然的一两个人经过影响很大。

防抖还有另一个层面:检测模块偶尔会出现一帧漏检导致人数骤降。我见过热闹的排队区域突然从25人变成10人,再接一帧又回到24人。后来在检测输出层加了一个简单的卡尔曼滤波式平滑,对每个检测框的坐标和后续帧的匹配做跟踪。这其实就是多目标追踪的简化版。如果你不想引重依赖,Ultralytics也提供了model.track()内置跟踪器,但它会引入额外的超参数,测试时需要小心调试。我最后放弃了内置跟踪,改用细一点的统计窗口做人数平滑,效果完全不差。

5.3 高峰期到底怎么判断:区分“排队”和“拥堵”

我做了统计,在非高峰时段,队伍一般不会连续超过10个人且队伍形状规整。但在高峰时段,队伍可能14个人,可还是会来回晃、有人在窗口前站着不走、有人并排插队,密度分布不均匀。“人数”和“拥挤程度”并不完全等价。

所以我的状态判断除了绝对人数,还加了一个相对变化量的信号:用最近30秒的平均人数减最近5秒的平均人数。如果这个差值稳定为正且数值大,说明队伍在快速增长,即使当前绝对人数不高,也应该提示“预计进入高峰”。反之,如果人数在高位但在下降,状态依然可以是“拥挤”但展示文案加一句“正在缓解”。

这个补充逻辑答辩时特别有用。评委问到“你的系统只是数人头吗?”,你就可以拿出这套多维判断的思路,解释你是如何把实时状态和历史趋势结合起来做预测的。我记得模拟答辩时,这边讲完就有一个老师点头了。

6. 可视化与前后端联调:把算法结果变成能看懂的界面

6.1 后端接口与数据流设计

整个系统的后端我用的Flask,没有选FastAPI或Django。理由很简单:这个项目后端的任务只有两个,一是持续跑视频流推理,二是向前端提供最新的统计结果。Flask足够轻量,起服务快,写接口直观。FastAPI也好,但多一个异步层的概念,演示和调试时没Flask直白。

后端需要开三个接口,分工明确:

接口路径返回内容调用频率
/api/status当前状态(idle/normal/crowded)、各区域人数、平均时间戳前端轮询,每2秒一次
/api/snapshot当前帧的JPEG图片,叠加了检测框和区域标注前端拉取,每1秒一次
/api/history过去一小时的排队人数时间序列前端页面刷新时拉取一次

推理主循环和Flask服务如何并行?我用的是多线程方案:主线程跑VideoCapture读取和YOLO推理,最新的推理结果写入一个带锁的共享字典;Flask的每个接口请求只是读取这个共享字典,不参与推理。这里有个细节容易出问题:Python的GIL会让多线程在纯计算任务中变得很卡,但我们的推理本身就发生在ONNX Runtime的C++层面,GIL影响很小。实测下来,单线程推理加多线程Flask平均延迟不到30毫秒,完全够用。

历史数据用SQLite存。这个选择最省事,不需要额外安装数据库服务,Python内置支持,读写足够快。我建了一张表记录每分钟的统计快照:

CREATE TABLE queue_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TEXT, pickup_count INTEGER, queue_count INTEGER, state TEXT );

6.2 前端的展示设计:要给评委“一眼看懂”的体验

前端我用的原生HTML加一点轻量JS,没有上Vue或React。毕设系统不是产品,功能展示清楚是第一优先,框架徒增复杂度。

页面分为两个核心区块:

一是实时视频区。左边是视频画面,画面上叠加了检测框、区域标色(取餐区绿色、排队区蓝色、入口区黄色)、状态角标(空闲/正常/拥挤分别灰/蓝/红)。为了画这些标记,我后端在返回snapshot时已经把检测框和区域多边形画在JPEG里了,前端不需要做任何计算,直接把图片扔进img标签就行。

二是统计面板区。右边从上到下依次显示当前状态、各区域人数、近一小时人数曲线、队伍变化趋势的文字说明。人数曲线我用的是Canvas自绘折线图,没有引Chart.js。原因很简单:自定义代码只有几十行,没必要为一个折线图拉一个第三方库。如果你自己写起来费劲,用Chart.js也不丢人,这只是审美取舍。

还有一个细节值得提:页面顶部放了一个醒目的“演示控制”栏,包含暂停/恢复视频流、手动切换视频文件、重置统计数据这三个按钮。这玩意儿非常好使——答辩的时候,评委可能要求你重新演示某一时段,你有控制权,总比系统自己跑完全失控强。而且“暂停/恢复”能直接展示模型在单帧上的表现,很受评委喜欢,因为他们可以仔细看你画的框到底有没有准。

6.3 钉在墙上的电视和攒主机:一次真实部署实录

我说过毕设系统要“能落地”,所以我真去食堂模拟环境部署过一次。用了一台旧笔记本连着一个小电视,把前端页面投上去,后面接一个普通的USB摄像头对准食堂测距区域。简单的部署比我想象中暴露了更多问题,列出来给你参考。

第一个问题是摄像头安装高度和角度。摄像头必须尽量高,俯视角度大才能减少遮挡。低了的话,前排的人会把后排全挡掉,模型再怎么强也没用。我后来想办法把摄像头绑到墙上的一个支架上,离地将近三米,角度向下倾斜30度左右,效果才勉强能看。

第二个问题是GPU设备发热和掉线。用笔记本连着跑了两小时,显卡温度逼近85度,然后明显降频,推理帧率掉了一半。后来垫了个散热底座,问题缓解。如果你是真在机房部署,建议留意一下机器通风。

第三个问题最隐蔽:电视上显示的检测框和真实世界的延迟。从摄像头采集到画面显示,大约有0.8秒延迟,这本来没问题。但从运营角度看,如果排队的那个人已经走了,屏幕上还画着他的框,就会有人对着电视说“这个系统不准”。所以要加上时间戳和帧号提示,让观众明确知道这是缓存了不到一秒的实时画面,而不是卡住的旧画面。

7. 测试方法与答辩准备:这一环节决定了你的分数段

7.1 性能评估指标怎么算

目标检测算法的常规指标是mAP(mean Average Precision),这个Ultralytics训练完会自动输出,不用你自己算。但答辩时光说mAP是不够的,因为评委关心的是你系统在真实场景中做了什么级别的输出。

我第一次测系统的时候,站在真实场景里数了10次排队人数,和系统显示人数对比,平均绝对误差大约是2.7人。这个数字看起来不大,但在总人数只有18人的队伍里,误差已经超过百分之十。后来调了置信度阈值和区域标定,误差降到1.8人左右。我建议你也做一次这样的真人实测,把这个误差数据放进展汇报PPT,比任何mAP数字都有说服力。

另一个指标是系统状态预测的迟到程度。我统计过,在状态切换的瞬间,系统确认一个新状态的延迟稳定在2到4秒。这个延迟主要是状态机里的确认帧数窗口导致的。我在PPT里用了一段带记录的视频来演示,从画面中队伍明显变长到状态文字从“正常”切到“拥挤”,大约3秒。评委看到这个延时是能接受的,不会觉得反应慢。

7.2 演示脚本:从进场到离场,十分钟讲完关键技术

书面报告和答辩演示是两码事。评委没有时间逐个功能看你的系统,你需要准备一个带有明确节奏的演示脚本。

我建议这样组织时间:

前两分钟:展示整体系统界面——视频区、状态面板、历史曲线,让评委建立“这是个完整系统”的认知。

中间五分钟:故意制造一个“拥挤事件”。具体操作是,播放一段事先录好的、从空闲到拥挤再到缓和的食堂视频,配合系统状态切换的实时反馈。讲清楚你这套状态机在人数突增时是怎么响应的,强调你的防抖逻辑和区域判定,让评委感觉到这不是一个静态阈值脚本。

最后三分钟:切换到一个带明显遮挡的密集画面,展示检测框的动态变化。可以诚实地说:“这里有两三个人被遮挡,系统漏检了,但我们的状态判定仍然显示为拥挤,因为排队区域的人数和趋势信号已经达标。”这比刻意回避缺陷的效果好得多,反而显得你有工程判断力。

答辩环节大概率被问的问题包括:

  • 为什么选择YOLO而不是其他模型?回答关键是速度、精度、生态成熟度三方面平衡。
  • 如果食堂布局变化,区域标定需要重新做吗?回答是的,需要重新标定,但标定过程是配置化的,不需要改代码。
  • 你的系统在实际部署中存在哪些限制?不要避重就轻,可以坦白说光照突变、人群密集遮挡、镜头运动影响。

7.3 我踩过的三个典型坑,以及如何快速修复

第一个坑是OpenCV读取RTSP流时偶发花屏和卡顿。原因是我没有设置超时参数,VideoCapture在流不稳定时会无限等待。修复方案是设置cv2.CAP_PROP_OPEN_TIMEOUT_MSEC和cv2.CAP_PROP_READ_TIMEOUT_MSEC,并定时检查帧间隔超过1.5秒就重新拉流。

第二个坑是多人并排时检测框抖动导致人数小幅波动。这是不可避免的,因为模型稍微动一点角度,同一批人的检测结果就可能出现漏检和重复检测。修复方案是我前面提到的滑动窗口平均加卡尔曼式坐标平滑,简单有效。

第三个坑是食堂背景和桌面反射导致误检为“人”。这个比较隐蔽,出现在下午阳光斜射时,光滑桌面反光加上餐盘摆放,会被模型误认为半个人形。解决的方法是扩充训练数据集,加入一些没有人的、只有桌椅和餐盘的背景图作为负样本。Ultralytics的检测训练也支持负样本(图片没有标注,相当于告诉模型这些不是目标),加了大约200张负样本后误检明显下降。

8. 复盘:做这个毕设,我到底学会了什么

毕设做完,我最大的感受不是目标检测有多深奥,而是一个工程系统要真正能跑起来,牵扯到的细节远超想象。模型训练只是其中很小的一环,更多的精力花在了数据采集、标注、后处理逻辑、前后端联调、真实场景适配这些不起眼但决定成败的事情上。

题目里那些看起来很“水”的关键词——监控、排队、食堂——其实每个词背后都有一个技术纵深。监控意味着实时性,排队意味着空间关系,食堂意味着遮挡和光照条件。如果你还是一名在校学生,建议在做完之后把这套系统留着自己继续扩展,比如加入对队列长度趋势的预测,或者接入食堂的就餐人数统计做菜品供应预测。这个题目天然是开放的,往后延伸的想象空间很大。

最后分享一个实在的建议:所有核心配置文件(区域坐标、阈值、模型路径)宁可写重一点,也要写得语义明确。有一次我改了区域标定忘了改阈值,结果系统一直把几十个人的区域判成“空闲”,排查了半小时才发现是两个配置文件参数不匹配。这种小坑谁也躲不掉,但把配置拆分清晰,排查速度会快很多。毕设是一次难得的完整历练,认真做完,收获的一定不止一份成绩单上的分数。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/19 9:01:02

open-code-review:开源代码审查协作协议

1. 项目概述&#xff1a;这不是一个“代码审查工具”&#xff0c;而是一套可嵌入开发流程的开源协作协议open-code-review 这个名字乍看像某个 GitHub Action 或 VS Code 插件&#xff0c;但实际它代表的是一类正在快速演进的工程实践范式——把传统意义上由人主导、发生在 Pul…

作者头像 李华
网站建设 2026/10/19 8:59:04

Matlab GUI文件数据处理工具:读取、处理与显示全流程实现

简介&#xff1a;Matlab GUI交互工具资源&#xff0c;用于文件读取、数据处理与结果显示&#xff0c;面向需要提升数据分析效率的科研或工程用户。压缩包共2个文件&#xff0c;包含DataProcessing.fig界面布局文件与DataProcessing.m逻辑代码文件&#xff0c;大小仅22KB&#x…

作者头像 李华
网站建设 2026/10/19 8:59:04

open-code-review:基于Git语义与AST分析的确定性AI代码审查工具

1. 这不是又一个“AI代码审查工具”&#xff1a;open-code-review 的真实定位与设计哲学你点开 GitHub&#xff0c;看到一个叫open-code-review的仓库&#xff0c;README 第一行写着 “A CLI tool for AI-powered code review”。你心里一咯噔——又来了&#xff0c;第 37 个打…

作者头像 李华
网站建设 2026/10/19 8:58:48

开源本地代码审查 CLI 工具:Git 原生集成 LLM

1. 项目概述&#xff1a;一个真正能嵌入日常开发流的开源代码审查工具“open-code-review”这个名字乍一听有点抽象&#xff0c;但拆开来看就非常实在&#xff1a;open&#xff0c;不是指“开源”这个属性&#xff0c;而是指“开放接入、开放协议、开放上下文”&#xff1b;cod…

作者头像 李华
网站建设 2026/10/19 8:58:08

AI辅助学术专著写作:从搭框架到交付的全流程实战指南

写一本学术专著&#xff0c;过去在我眼里是一件既神圣又有些可望不可即的事。前些年我帮某高校的导师整理过项目成果&#xff0c;光是核对引用文献和统一术语就耗了将近两个月&#xff0c;而真正动笔写核心章节反而没那么累。直到最近这一年&#xff0c;我完整地走完了一本专著…

作者头像 李华