news 2026/9/26 3:01:48

YOLOv8+状态机:老人服药提醒系统从检测到行为判定的实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLOv8+状态机:老人服药提醒系统从检测到行为判定的实战指南

简介:面向计算机相关专业学生及毕业设计开发者,基于YOLOv8的智能家居老人服药提醒系统提供了一站式可运行方案,解决智能药盒场景下的目标检测与提醒需求。资源内包含完整Python源码、预训练模型权重、配置文件、可视化页面及部署说明,共97个文件,压缩包仅24.21MB;其中py源码70个,pyc编译文件12个,pt模型4个,另有xml配置、txt说明和mp4演示视频,模块划分明确,便于按需查阅与二次开发。项目已通过功能测试,可生成混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果及标签分布图等核心指标图表,配合可视化界面能直观展示检测效果。已有39人学习下载,适合用于毕业设计、课程设计或目标检测方向的项目立项演示,功能完整、操作简单,可放心直接使用。

1. 这个毕设题目,本质上是「检测模型 + 业务逻辑」两件事

很多同学拿到《基于YOLOv8的智能家居老人服药提醒系统》这个题目,第一反应是「又要训练一个YOLO模型」。但真正动手之后会发现,模型只占整个项目三分之一的工作量。剩下三分之二,花在「怎么定义一次服药行为」和「怎么让提醒不烦人」上。

这个系统的核心价值不是「检测到药盒」,而是「检测到药盒被拿起、隔了多久被放下、这次交互算不算一次有效服药」。YOLOv8在这个项目里承担的是视觉感知层——识别画面里的人手、药盒、水杯,而提醒逻辑是在检测结果之上叠加的时序判断。如果你只想跑通一个demo,用预训练权重加一个界面就能交差;但想拿高分、能答辩、敢演示,就必须把「检测」和「行为判定」之间的缝隙填上。

适合用它做毕设的人有两类:一类是目标检测入门,想用YOLOv8做一次完整的「数据-训练-部署」闭环;另一类是系统集成方向,想展示自己能把模型包进可视化界面、对接硬件。下面所有内容都按「能复现」的标准写,从数据标注讲到界面打包。新手照做能跑通,熟手可以直接跳到第5章看避坑清单。

2. 先把场景拆清楚:服药提醒要检测的不是「药」,是「动作」

2.1 类别的设计直接决定标注工作量

如果按照「检测药盒」的思路去做,你会遇到一个很尴尬的问题:药盒的外观千奇百怪,有瓶装的、板装的、分格药盒、透明自封袋,而且老人家里同一款药盒会出现在餐桌、茶几、床头柜多个位置。让模型去学「所有药盒的样子」,等于把简单问题复杂化。

我建议把检测目标定为四类:person(人体)、hand(手部)、pill_box(药盒)、cup(水杯)。为什么不检测「药瓶」而是「药盒」——因为提醒系统的触发点是「盒子被打开/拿起」,而不是「瓶子存在」。这个类别设计直接决定了后续行为判定的复杂度:类别越少,模型越好训,但业务逻辑就要多写;类别越多,标注越累,推理也越慢。我一般用5类封顶,其中person只在画面里有老人出镜时才需要,如果你的部署场景是「摄像头对着桌面」而不是「对着人」,这一类的权重可以砍掉,少一类就少一批误检。

2.2 数据集的三种来源和真实占比

标题里写的「完整数据集」通常指某位作者自己采的桌面场景视频帧。但你在网上能找到的开源数据集,绝大多数是公共场景(厨房、客厅、办公室),和「老人坐姿服药」这个垂直场景有分布偏移。我的经验是:别指望一份现成数据集直接训出高精度。

常见做法是三层凑数据。第一层:公开的COCO子集里抽Hand类(COCO本身没有hand,可以用EGOHANDS或TV-Hand),占20%左右,用来让模型先见一见「手」的多样性。第二层:自己用手机拍,固定机位模拟摄像头高度,把药盒、水杯在桌面不同位置各放几十张,占60%。第三层:用Labelme对第二层数据做标注,把拍到的每一帧里出现的手、药盒、水杯框出来,占20%的迭代余量。整体规模控制在1500到3000张,每张平均1.5到2个目标,YOLOv8s完全够用。

标注的时候有个习惯必须养成:hand的框要贴着皮肤边缘,不要把整个手臂包进去;pill_box要把盒子完整框住,哪怕被手遮挡了一半也要框「完整的盒体估计范围」。YOLOv8对遮挡目标的回归能力取决于训练标签里有没有这种「被遮住也要框全」的数据。标注格式转成YOLO txt时,class_id顺序固定为0: person, 1: hand, 2: pill_box, 3: cup,后面转数据集配置直接复用。

3. 用 YOLOv8 训练自己的数据集:从环境配置到看曲线

3.1 环境搭建:CPU 和 GPU 两条路的真实差别

标题对应的部署教程里,常见的环境是ubuntu20.04 + YOLOv8 CPU版本,这说明作者默认目标机器是普通的笔记本或无独显台式机。CPU训练YOLOv8s不是不可以,但是有个前提:数据集别超过3000张,epoch别拉满100,想省时间就用YOLOv8n预热结构再续训。

先给出一套我在Ubuntu 20.04上踩稳的安装顺序(GPU版):

# 1) 创建虚拟环境,避免把系统Python搞乱 conda create -n yolo python=3.9 -y conda activate yolo # 2) 安装PyTorch。先查自己的CUDA版本,nvcc --version pip install torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cu118 # 3) 安装ultralytics。YOLOv8的官方实现已经并进这个包 pip install ultralytics==8.2.0 # 4) 验证环境 python -c "from ultralytics import YOLO; print('ok')"

第2步的CUDA版本一定要先确认,nvidia-smi显示的CUDA版本是驱动支持的版本,而PyTorch需要的是运行时CUDA,两者可以不同——只要PyTorch的运行时CUDA版本不高于驱动版本就能跑。如果你只看nvidia-smi就去装最新的cu121,很常见的结果是torch.cuda.is_available()返回False,白费半小时。

如果只有CPU,把第2步换成pip install torch==2.1.0(不指定index-url,默认装CPU版),训练时间会变成GPU的15到20倍。所以CPU跑的话,我的建议是直接换yolov8n.yaml+ 640输入 +epoch=50,先跑通全流程,后续再考虑要不要租GPU。

3.2 数据集配置文件和训练命令的四个关键参数

训练前要先准备好dataset.yaml,放在项目datasets/MedicineReminder/目录下:

# dataset.yaml path: /home/your_name/MedicineReminder/datasets # 数据集根目录 train: images/train # 训练图片目录 val: images/val # 验证图片目录 names: 0: person 1: hand 2: pill_box 3: cup

这里有个坑:path必须是绝对路径,不能用相对路径。Ultralytics在训练时会用path拼接train和val,一旦拼出来的目录不存在,它不会报「路径错误」,而是直接跑一个空数据集,最后loss曲线是平的,mAP@0.5直接是0。我第一次跑的时候,就是被这个「空数据集训练不报错」的设计坑掉了一个下午。

训练命令和参数说明:

yolo detect train \ --model yolov8s.pt \ --data /home/your_name/MedicineReminder/datasets/MedicineReminder.yaml \ --epochs 100 \ --imgsz 640 \ --batch 16 \ --device 0 \ --patience 20 \ --cache ram \ --project /home/your_name/MedicineReminder/runs \ --name train_small

参数里最值得解释的是patience和cache。patience=20表示验证集指标连续20轮不提升就提前停止,用来防过拟合,但如果你数据集很小(不到1000张),建议改成30,不然模型还没学够就被停了。cache ram是先把图片缓存进内存,能省大量磁盘IO,CPU训练时效果尤其明显——代价是吃内存,16G内存的机器建议改成cache disk。imgsz=640是精度与速度的平衡点,老电脑显存不够就降到480,精度会掉3到5个点,但显存占用少一半。

3.3 训练跑起来后,你应该盯着哪三张图

训练过程中和结束后,去runs/train_small/目录下看三个东西:results.png、confusion_matrix.png、val_batch*.jpg。

results.png里有train/loss和val/loss两条曲线。正常情况是训练损失一路下降,验证损失先降后平。如果验证损失在第30轮突然向上翘,说明过拟合了——解决方式是加数据增强(YOLOv8默认已经开了hsv_h、hsv_s、translate等),或者把dropout打开,或者回到第2章把数据量补到2000张以上。

confusion_matrix.png看的是类别混淆。最常见的错误是hand和pill_box互相误检——原因是标注框太紧、手和盒子挨得太近。如果混淆矩阵里这两类交叉严重,不要急着调模型,先回头检查标注框:hand标注是不是只框了两根手指?如果是,把框放宽到整个手背,一般能压下去一截误检。

val_batch*.jpg是验证集上的预测可视化。这里要看的不是「框得准不准」,而是「漏检的目标长什么样」。如果漏检的图集中在「手捏药盒、药盒半透明、光线暗」这三种情况,说明数据集里缺这些困难样本,从训练集里捞出来单独强化,比调任何超参数都管用。

4. 把模型接进可视化界面:服药提醒逻辑的三种写法

4.1 界面技术选型:桌面端用 PySide6,网页端用 Flask

标题里写「可视化界面」,对毕设来说,桌面端用PySide6(就是Qt的Python绑定),网页端用Flask,是两种最稳的方案。PySide6的优势是打包后能做成一个.exe,演示的时候双击就开,不用起服务;Flask的优势是手机上能开浏览器看画面,适合远程演示,但需要解释「为什么网页能看到摄像头」。

我个人推荐PySide6,原因很实际:答辩现场最容易翻车的环节就是「打开网页、输入地址、等模型加载」,任何一个环节犹豫五秒,评委的注意力就散了。PySide6把窗口、视频流、日志三个区域固定好,打开即是可用状态。

PySide6界面核心结构至少要有三块:左侧是摄像头画面(QLabel显示帧),右侧是状态面板(当前检测到的类别、最近一次服药时间、提醒记录列表),底部是按钮区(开始监测、停止监测、立即提醒)。不要做复杂的参数设置页,毕设演示场景里没人去调置信度阈值,把这些参数写成配置文件里的常量就行。

4.2 服药动作判定的三段式逻辑

这是整个系统里最容易被低估的部分。单纯的「检测到药盒」没有意义——药盒放在桌上,模型每帧都能检测到,难道每帧都提醒一次?真正要判断的是「一次拿起-放下-离开的完整过程」。

# monitor.py 核心判定逻辑 class DoseActionMonitor: def __init__(self, threshold=0.5, hold_frames=10, interval=3): self.threshold = threshold # 置信度阈值 self.hold_frames = hold_frames # 目标连续出现多少帧算「稳定存在」 self.interval = interval # 两次服药之间的最短间隔(分钟) self.state = "IDLE" # IDLE / HOLDING / TAKEN self.hold_counter = 0 self.last_dose_time = 0 def update(self, detections, frame_time): # 第一步:找当前帧里置信度最高的 pill_box 和 hand pill_box = self._find_best(detections, "pill_box", self.threshold) hand = self._find_best(detections, "hand", self.threshold) # 第二步:状态机转移 if self.state == "IDLE": # 手和药盒同时存在,且手的位置与药盒重叠 -> 进入 HOLDING if hand and pill_box and self._overlap(hand, pill_box): self.hold_counter += 1 if self.hold_counter >= self.hold_frames: self.state = "HOLDING" self.hold_counter = 0 elif self.state == "HOLDING": # 手消失但药盒还在,或者两个都不在 -> 认为一次拿起动作结束 if not hand: self.state = "TAKEN" self.hold_counter = 0 elif self.state == "TAKEN": # 进入 TAKEN 后,检查与上一次服药的时间间隔 if frame_time - self.last_dose_time >= self.interval * 60: self.last_dose_time = frame_time self.state = "IDLE" return "dose_taken" else: self.state = "IDLE" return None

这段逻辑看着简单,但跑起来会发现三个问题。第一,hold_frames=10的意思是「手和药盒连续重叠10帧才确认拿起」,如果视频流是15帧每秒,10帧就是0.67秒,老人动作慢一点、手抖一点,就可能永远进不了HOLDING状态——所以这个参数按目标人群调整,演示视频里动作利落就设8,实拍老人动作就设15。第二,interval=3表示「3分钟内重复拿起不算新一次服药」,这是防误报的关键,否则老人把药盒拿起来看一眼又放下,系统会连报三次。第三,_overlap函数算的是IoU,但要放宽一点:手和药盒的IoU大于0.1就算重叠,因为手捏着药盒时,检测框往往只有边缘相交。

4.3 提醒的「打扰分级」:从声音到推送的递进策略

老人服药提醒系统最怕的不是漏报,而是误报太多导致老人把提醒当成噪音。我常见的设计是三级递进:

第一级是界面内弹窗和语音播报,只针对「检测到拿起但超过N分钟没有检测到放下」的异常;第二级是微信推送,当连续两次漏服时发给监护人;第三级是短信或电话,只在超过半天没有交互记录时触发。每一级都要有「关闭提醒」的入口,而且要记录关闭时间和原因,这个日志在答辩时很有说服力——说明你考虑到了真实场景下「老人不在家、摄像头空转」的干扰。

界面里的状态面板建议展示当前状态机的值(IDLE/HOLDING/TAKEN),这样演示的时候,评委能看到「拿起-放下-记录」的完整过程,而不是只看到一个框在跳动。状态值可视化比检测框更能体现系统设计完整性。

5. 部署避坑:YOLOv8服药提醒系统最常见的五个翻车现场

5.1 现象:训练时 loss 为 nan,从第1轮开始就是 nan

出现这个现象,先别怀疑数据集。最常见的原因是学习率太大,YOLOv8默认lr0=0.01,如果你用了很小的batch(比如2或4),梯度噪声会被放大,早期训练就炸。解决方法是把lr0降到0.001,再把batch提到8以上。如果数据集里有一张全黑的图或者全白的图,也会触发nan——训练前写一段脚本扫一遍所有图片的std,把标准差小于1的图片直接删掉。

5.2 现象:摄像头预览很流畅,但模型推理一卡一卡

这是典型的「可视化界面拖累了推理」问题。PySide6里如果在主线程跑模型推理,界面绘制和推理互相抢GIL,帧率直接砍半。解决方式是把推理放到QThread里,主线程只负责把QPixmap贴到QLabel上。另一个隐蔽的坑是:摄像头采集分辨率默认1080p,推理输入是640,如果每一帧都先缩放再推理,缩放本身也要耗几十毫秒。建议采集端直接设置cv2.CAP_PROP_FRAME_WIDTH为1280,高度720,减少一次不必要的缩放。

5.3 现象:提示「No labels found in /datasets/images/train」

这句话看着像数据集为空,但实际原因通常是dataset.yaml的train路径写错了。YOLOv8的目录规范是images/train配labels/train,如果你只把图片放进了images,忘了放对应的labels文件夹,或者labelme导出的txt文件和图片文件名没对齐,就会报这个错。检查顺序:数据集根目录下是否同时有images和labels两个一级目录,每个目录下是否都有train和val子目录,两个子目录里的文件名是否一一对应。

5.4 现象:模型检测到手了,但一直不进 HOLDING 状态

这种情况几乎都出在坐标映射上。界面显示的是摄像头原图,检测框的坐标是640×640输入尺寸下的坐标,如果你在update()里用原图坐标和检测坐标计算IoU,永远是0。解决方式是在update()函数入口统一换算:x1_orig = x1 * orig_w / 640。另一个常见来源是检测框的xyxy格式被误当成xywh用了,results[0].boxes.xyxy输出的是左上角和右下角坐标,如果你按中心点加宽高去算IoU,数值也会诡异。

5.5 现象:打包成 exe 后双击没反应,或者闪一下就退出

PySide6 + ultralytics打包,最常见的坑是资源文件路径。yolov8s.pt权重文件在开发时被放在项目根目录,打包后路径变了,模型加载直接失败。解决方式是用sys._MEIPASS(PyInstaller运行时解包路径)拼接权重路径。第二个坑是ultralytics依赖的libgcc等动态库没被PyInstaller收进去,会报DLL load failed。规避办法是不要用--onefile,改用--onedir模式,然后手动把site-packages/ultralytics整个拷进dist目录,成功率最高。

6. 用一组「十秒动作视频」验证系统有效性,再决定要不要加功能

模型训练完、界面能跑、提醒能弹,这时候先别急着拍照截图写论文。我建议你先录一段十秒左右的视频,脚本固定为:手伸向药盒、拿起、停顿、放下、手离开。把这段视频在界面的「视频回放」模式里跑一遍,看三个指标:拿起动作有没有在2秒内被确认、放下后有没有在1秒内回到IDLE、整个过程有没有触发多余提醒。这三个指标能过,再开始写论文的实验章节。

如果这段视频跑下来暴露了问题,常见的补充手段有三个,按性价比排序:一是给hand类别加一个「距离药盒中心越近权重越高」的后处理,这是见效最快的;二是在dataset.yaml里把hand类的mosaic增强概率调低,因为手部目标太小,马赛克增强容易把半个手切碎;三是加一帧差分——如果画面里连续10帧没有任何目标变化,直接把状态机冻结,避免光照缓慢变化的干扰。

我自己的习惯是:在DoseActionMonitor里加一个debug_log列表,每次状态转移都记录frame_time, state, conf, iou四个值。这个日志平时不显示,但只要出了奇怪的误报,回放日志三分钟就能定位是检测问题还是状态机问题。这个习惯救过我很多次,也建议你保留。

这套系统的真正难点不是YOLOv8,而是「检测结果如何转换成一件具体的事」。把状态机写清楚、把数据集做扎实、把界面整理干净,这个方向就是能拿得出手的完整项目。希望这些步骤和踩坑记录能帮你少走几段弯路。

提示:如果你用的是Windows系统,把第3章的环境搭建命令里的source activate换成conda activate即可,其余无差别。CPU训练的机器,建议在train命令里加一句--workers 0,否则Windows下多进程数据加载偶尔会卡死。

本文还有配套的精品资源,点击获取

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

基于Python+Vue的协同过滤图书推荐系统:从算法选型到前后端联调实战

简介:这份资源是面向高校计算机相关专业毕业设计的完整项目包,主题为PythonVue基于协同过滤算法的图书推荐系统,适合正在准备毕设、需要机器学习与前后端分离实战案例的学生参考。系统涵盖用户模块、图书模块、推荐算法模块与推荐结果展示模块…

作者头像 李华
网站建设 2026/9/26 2:59:57

团子翻译器字体适配指南:让仿宋等中文字体真正可用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/26 2:59:31

从告警研判到入侵溯源:HW攻防演练防守岗位实战解析

又到了安全圈最热闹的时段,各个群里都在聊排班、夜班和监控大屏。你要是刚入行,或者从开发/运维转岗过来,最容易被塞过去的岗位,一个叫监控研判,另一个叫应急溯源。很多人分到HW相关任务时,脑子里全是问号&…

作者头像 李华
网站建设 2026/9/26 2:58:06

基于YOLOv5与ResNet18的骨龄检测双模型方案:从数据到部署

简介:本资源面向计算机视觉方向的本科与研究生毕业设计需求,提供一套基于YOLOv5与ResNet18联合实现的骨龄检测完整工程,适合需要完成高分毕设、课程设计或医学影像入门实践的同学。项目将目标检测与图像分类串联,先定位手骨关键区…

作者头像 李华