基于YOLOv8的智慧牧场牲畜异常行为识别系统设计与部署
简介基于YOLOv8的智慧牧场牲畜异常行为识别系统是一套面向计算机视觉、深度学习及物联网相关专业学生的毕业设计或课程设计资源。项目以目标检测为核心涵盖牲畜异常行为识别、模型训练、视频推理与可视化展示等环节并配有完整数据集和部署说明便于直接运行与二次开发。压缩包共8个文件包括3个Python脚本、3个模型权重文件.pt和2个说明文档txt整体大小15.91MB其中训练脚本负责模型迭代推理脚本支持视频检测可视化脚本提供交互界面权重文件可直接加载用于实际识别。项目采用YOLOv8n模型附带训练好的best.pt权重不必从零训练即可快速验证效果同时提供完整数据集便于进一步微调与扩展。代码经运行验证支持输出混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果及标签分布图等评估指标可为答辩与评审提供有力支撑。目前已有55人学习下载尤其适合需要快速完成毕业设计、课程设计或项目演示的在校学生。1. 为什么是YOLOv8智慧牧场牲畜异常行为识别从选题到落地做计算机视觉方向的毕设最常见的失败方式不是模型不会写而是整套流程断在半路训练脚本能跑但精度上不去数据集是网上随便下的通用子集界面停在 print 打印框坐标。这套基于 YOLOv8 的智慧牧场牲畜异常行为识别系统把一条完整链路打包了YOLOv8 负责检测牲畜位置行为判定逻辑负责区分正常与异常动作可视化界面负责把结果变成可演示的监控面板再配一份能照做的部署教程。对毕设和课程设计来说它的价值在于不需要从零拼装而是拿到一条能跑的流水线后把精力花在改数据集、调参数、换网络结构上。对想快速做原型验证的工程师这套路同样成立先跑通再优化。2. 系统架构与YOLOv8模型结构从网络结构图到行为识别链路整套系统的核心不是把 YOLOv8 当作随插随用的检测器而是先搞清楚“异常行为”在算法层面到底是什么问题。牲畜异常行为没有统一标准不同牧场的饲养密度、围栏朝向、机位高度都不一样“异常”的定义必须落到可计算的检测框特征上。这一章先把识别链路拆开再讲 YOLOv8 结构里 C2f 和 Head 为什么适合这个场景最后给出一条可以直接跑通的单机推理流水线。2.1 异常行为在算法上是分类还是时序问题“异常”大体分两类一类是姿态异常单帧就能看出来比如奶牛长时间躺卧不起另一类是行为异常必须看连续帧比如跛行、争斗、围栏边反复踱步。先给行为定义一个可计算的基线比直接套视频分类模型实惠得多。行为类别典型特征单帧可判定常用判定手段站立身体竖直检测框高宽比正常是框宽高比 类别置信度躺卧身体贴地框高明显压缩是框宽高比阈值进食头部靠近食槽或地面部分头部子区域 IoU争斗两框重叠且运动速度突变否跟踪轨迹 框间距离变化跛行步态不对称移动速度异常否轨迹点周期分析注意这张表是建模起点不是公开标注标准。实际交付时我会把“躺卧”拆成“正常躺卧”和“长时间躺卧”后者由时序模块根据躺卧帧连续数量判定。单帧能判的姿态直接用检测框几何特征不引入额外模型单帧判不了的才需要把检测框接进跟踪器做时序分析。这个取舍在毕设答辩里也是可以说清楚的亮点不是所有行为都要上时序网络。单帧姿态判定用检测框宽高比就能落地代码如下def posture_from_bbox(box, threshold0.65): x1, y1, x2, y2 box w x2 - x1 h y2 - y1 aspect h / max(w, 1e-6) if aspect threshold: return lying return standing这个函数只做一件事把检测框的高宽比映射成站/躺。牛站立时身体竖直高宽比通常在 1.0 以上卧倒时框会明显变扁aspect 掉到 0.6 以下。threshold 是经验值摄像头装得越高、俯角越大正常站立的高宽比也会变小所以换机位后要重新标定。提示单阈值容易抖动。我一般会把 aspect、目标类别置信度、目标在画面中的绝对像素高度一起做加权投票而不是只比一个阈值。2.2 YOLOv8 网络结构图中的 C2f 与 Head 在牧场场景里意味着什么YOLOv8 的 backbone 由 Conv、C2f、SPPF 堆叠而成neck 用 PAN-FPN 做多尺度特征融合head 是 anchor-free 解耦头。很多人对着网络结构图只看层名区分不了 C2f 和旧版 C3 的差别C3 在 Bottleneck 输出后做一次拼接C2f 则把中间层的输出反复 split 再 concat梯度路径更密浅层特征在传递时不易被稀释。对牧场这种目标尺度跨度大的场景远端牲畜可能只有 20 像素近处牲畜占满半屏浅层特征直接决定小目标召回率C2f 不是锦上添花是刚需。Head 从 anchor-based 改成 anchor-free 后模型输出三个尺度的特征图80×80 管小目标、40×40 管中目标、20×20 管大目标每个位置直接预测类别和框偏移省掉了 anchor 聚类步骤。三个尺度分别对应远端小牛、中景羊群、近景单牛这也是为什么换个模型结构就要重出网络结构图的本质原因。模型参数量约COCO mAP50-95约典型部署场景YOLOv8n3.2M37.3边缘盒子、CPU 演示YOLOv8s11.2M44.9GTX 1660 Ti 级别 GPUYOLOv8m25.9M50.2训练机充裕、高精度需求上表是 COCO 预训练权重的参考值换到自己的牧场数据集后数值会整体变化但相对关系不变。压缩包这类“简单部署即可运行”的项目默认模型通常是 n 或 s。识别“跛行”这种需要看步态的细节行为s 比 n 可靠因为小模型对小目标的特征表达上限有限。如果显卡只有 6GB 显存s 是性价比最高的起点。2.3 用 YOLOv8 拼出完整识别链路检测、跟踪与告警数据流是视频帧进 YOLOv8得到检测框检测框交给跟踪器获得跨帧 ID行为判定模块消费每个 ID 的轨迹和姿态特征输出行为标签告警模块对异常标签做计数与冷却。这里的关键是跟踪不是每帧重新检测后裸比较框位置——没有跨帧 ID就无法统计“这头牛躺了多久”。假设 video_stream 是已经通过 cv2.VideoCapture 打开、按帧 yield 的迭代器完整链路代码可以这样写from collections import defaultdict, deque from ultralytics import YOLO model YOLO(best.pt) abnormal_frames defaultdict(deque) last_alert {} ALERT_WINDOW 5 COOLDOWN_FRAMES 900 # 30fps 下约 30 秒 for frame_id, frame in enumerate(video_stream): results model.track(frame, persistTrue, conf0.3, iou0.5, verboseFalse) for box in results[0].boxes: track_id int(box.id) cls_name model.names[int(box.cls)] if cls_name in (fighting, limping): buf abnormal_frames[track_id] buf.append(frame_id) if len(buf) ALERT_WINDOW and frame_id - last_alert.get(track_id, -10**9) COOLDOWN_FRAMES: trigger_alarm(track_id, cls_name, frame) # 占位函数实际换成写库或推送 last_alert[track_id] frame_id逻辑说明model.track返回的是带跟踪 ID 的检测结果persistTrue让跟踪器在帧间保持同一目标的 ID 不变异常类目标出现后每个 track_id 维护一个异常帧序号队列连续 5 帧命中才告警避免单帧误检导致刷屏last_alert记录上次告警帧实现同一目标 30 秒冷却。参数上conf 不建议设太高异常行为尤其是跛行中的牲畜经常只有低置信度框能抓到先保召回把误报交给告警冷却消化。trigger_alarm是占位函数接入时替换成写 MySQL、推送钉钉或弹窗即可。提示box.id在只做单帧检测时不存在必须先跑 track。如果目标在画面里频繁遮挡导致 ID 跳变告警会漏这一步只能靠换跟踪器ByteTrack、BoT-SORT解决调 IoU 没用。3. 数据集构建与标注用 YOLOv8 训练自己的数据集要先过的三道坎标题里带“完整数据集”但拿到手之后几乎不可能原封不动开训。要么类别是英文标注、要么机位和自己的场景不一致、要么异常样本数量少到训练曲线直接崩。这一章按数据获取、格式转换、难例增广三个步骤讲每一项都对应一个能直接落地的操作。3.1 数据来源、视角与“数据集下载”的三个检查点网上搜“牛/羊检测数据集下载”能找到不少打包资源但下载前先看三件事拍摄视角、分辨率、标注格式。COCO 里虽然有 cow 类别但大多是自然场景平拍照不是牧场常用的俯视监控视角直接用会造成域偏移模型认得牛的纹理认不出俯瞰视角下的牛背。理想数据是监控视频抽帧每 5 秒抽一帧同一条围栏的连续帧洗牌后分到 train/val避免相邻帧信息泄漏导致验证集虚高。数据量上的经验是正常类每类在 800 张以上异常类每类尽可能到 500 张以上。异常类实在凑不齐先用检测模型把站/躺/进食跑通跛行和争斗降级为“规则 人工复核”这本身就是一条完整的毕设交付路径。合成数据可以补量在正常帧上贴合成贴图或做简单 copy-paste但合成样本要控制比例超过总量 30% 后验证集会失真。3.2 标注格式与目录结构训练自己的数据集必须先过格式关YOLOv8 通过 ultralytics 框架读取标注要求每张图对应一个同名 txt路径在 data.yaml 里声明。目录结构按下面的约定组织datasets/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml 是训练的入口格式如下path: datasets train: images/train val: images/val test: images/test names: 0: standing 1: lying 2: eating 3: fighting 4: limping说明path 是相对 data.yaml 所在位置的根路径train/val 填相对于 path 的目录names 的索引必须和 txt 文件里的类别 id 严格对齐这是训练自己的数据集最常见的一类报错来源——图片能读但标签全丢因为 names 顺序和标注 id 对不上。每个 txt 的一行标注是class x_center y_center width height坐标是归一化到 0~1 的小数。手上的标注如果是 COCO 格式需要先转换import json def coco_to_yolo(json_path, img_w, img_h, out_path): with open(json_path) as f: data json.load(f) lines [] for ann in data[annotations]: x, y, w, h ann[bbox] cx, cy x w / 2, y h / 2 lines.append(f{ann[category_id]} {cx/img_w:.6f} {cy/img_h:.6f} {w/img_w:.6f} {h/img_h:.6f}) with open(out_path, w) as f: f.write(\n.join(lines))参数说明函数假设 COCO 的 bbox 是(x, y, width, height)并且在调用前已经完成类别 id 到 data.yaml names 索引的映射img_w/img_h 是原图尺寸用于归一化。最常见的坑是标注软件导出的是(x1, y1, x2, y2)格式按这个函数算出来会整体偏移。转换后随手抽 10 张图把 txt 数据画回图上检查一遍比训练完才发现标签错位省一天时间。3.3 数据增强与类别不均衡异常样本永远是少数怎么办YOLOv8 内置的增强在 ultralytics 的配置里按概率和强度控制我通常这样起步hsv_h: 0.015 # 色相扰动适应早晚光环境 hsv_s: 0.7 hsv_v: 0.4 degrees: 10.0 # 旋转扰动模拟围栏机位微调 translate: 0.1 scale: 0.5 fliplr: 0.5 mosaic: 0.8 mixup: 0.2参数说明这些值不是模型超参数是数据增强开关。hsv_h 调色相能缓解不同牧场光照差异mosaic 把四张图拼成一张对小目标尤其有效mixup 做两张图线性混合。mosaic 不是越高越好比例调到 0.8 以上时小目标会被拼图裁剪吃掉表现为训练集 loss 正常、验证集 recall 上不去。异常类别少时先离线把异常帧做水平翻转、亮度抖动复制进数据集比调 mixup 更可控。YOLOv8 官方训练流程没有直接的类别权重开关靠代码里改 loss 加权的做法维护成本高我一般不推荐在毕设阶段碰。4. 训练与可视化界面从命令行训练到实时监控面板训练和界面是毕设答辩里最容易出彩也最容易翻车的两段。训练翻车多半是参数和显存不对付界面翻车多半是拿了一个重框架却只画了个按钮。这一章先给一条能在 GTX 1660 Ti 上完整跑完的训练命令再讲 loss 曲线的读法最后给一个同时满足“快速演示”和“能向监考老师讲清结构”的可视化界面方案。4.1 训练命令与参数GTX 1660 Ti 能跑完的 YOLOv8 配置假设数据已经按 3.2 的目录放好预训练权重从 ultralytics 官方渠道下载训练命令如下yolo detect train \ datadatasets/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ device0 \ patience20 \ projectruns/train \ namelivestock_v1参数说明data 指向 data.yamlmodel 写预训练权重的文件名框架会自动下载并继承主干权重imgsz 是训练输入尺寸640 是速度和精度的平衡点batch 受显存限制GTX 1660 Ti 的 6GB 显存跑 yolov8s 时batch 16 需要开启默认的 AMP混合精度才不爆显存报 CUDA OOM 就把 batch 降到 8或者 imgsz 降到 512patience 是早停参数验证集 20 轮不涨就停避免半夜训练空转。参数推荐起点调参方向epochs100数据少就降到 60早停会自动截断imgsz640小目标多可以试 768显存不够优先放弃batch8~16OOM 先降 batch别直接砍 imgszdevice0CPU 训练用 devicecpu但时间翻三倍以上patience20验证 loss 抖得厉害时提到 304.2 loss 函数曲线图与训练监控结果不是等训练完才知道的训练结束后runs/train/livestock_v1/ 下会生成 results.csv 和 weights/。除了用 TensorBoard我习惯直接读 results.csv 画图因为列名固定、可以在任意机器上复现tensorboard --logdir runs/trainimport pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/train/livestock_v1/results.csv) df.columns [c.strip() for c in df.columns] fig, axes plt.subplots(1, 3, figsize(15, 4)) axes[0].plot(df[train/box_loss], labeltrain box_loss) axes[0].plot(df[val/box_loss], labelval box_loss) axes[1].plot(df[train/cls_loss], labeltrain cls_loss) axes[1].plot(df[val/cls_loss], labelval cls_loss) axes[2].plot(df[metrics/mAP50-95(B)], labelmAP50-95) for ax in axes: ax.legend() ax.grid(True) plt.tight_layout() plt.savefig(training_curves.png, dpi150)逻辑说明results.csv 的列名首行带空格先 strip 再取值每组曲线都画 train 和 val 两份val 在验证集上评估train 是训练集本身。判断训练质量看三点box_loss 缓慢下降但 val 曲线在第 40 轮后回升是过拟合需要回调 epochs 或加大增强cls_loss 下降但 mAP 不涨多半是类别不均衡mAP 曲线震荡剧烈是 batch 太小或验证集图片太少。曲线图画出来基本就能在答辩前把“为什么用这个配置”讲明白。4.3 可视化界面选型监控面板和答辩演示要分开考虑可视化界面有两条路一条是 PyQt5 做桌面程序界面原生、能装成 exe答辩演示最有把握另一条是 Streamlit 做 Web 页面半小时能出界面适合课程设计和快速原型。Tkinter 零依赖但画不出像样的监控面板我一般不做推荐。选型可以直接对比方案上手成本演示效果适合场景PyQt5中高可作为桌面应用交付毕设答辩、最终交付物Streamlit低中浏览器访问课程设计、快速验证Tkinter低低只做界面占位Streamlit 版的核心代码可以短到几十行import streamlit as st from ultralytics import YOLO from PIL import Image import numpy as np st.set_page_config(page_title智慧牧场牲畜异常行为识别) model YOLO(runs/train/livestock_v1/weights/best.pt) uploaded st.file_uploader(上传监控截图, type[jpg, png, jpeg]) conf_th st.slider(置信度阈值, 0.1, 0.9, 0.4) if uploaded is not None: image np.array(Image.open(uploaded).convert(RGB)) results model.predict(image, confconf_th) st.image(results[0].plot(), channelsBGR)逻辑说明model.predict直接接收 numpy 数组conf 由滑块实时控制不需要重启页面results[0].plot()在原图上画出检测框、类别和置信度内部实现了标签叠加不用手写绘制。注意一个隐藏坑推理输入是 RGB但 OpenCV 的绘图函数和 matplotlib 的 imshow 默认按 BGR/RGB 混用处理plot()输出的数组给 Streamlit 时要声明 channelsBGR否则画面偏蓝。要改成 PyQt5 版本思路不需要换把推理结果接进 QLabel 的 setPixmap再套一个 QTimer 定时刷新监控逻辑和训练权重完全共用。5. 部署验证与调参本地部署教程里最容易忽略的细节“简单部署即可运行”的承诺最后都卡在环境、模型格式和阈值上。这一章按部署顺序讲三个动作导出可交付的模型、用验证集确认模型没被导出过程改坏、用三个阈值把误报压到可接受范围。这套系统的源码到手后先别急着改网络结构真正要动的地方通常只有三个参数。5.1 模型导出ONNX 和 TensorRT 不是导完就能用的先导出 ONNX 保底再决定要不要出 TensorRT engineyolo export modelruns/train/livestock_v1/weights/best.pt formatonnx imgsz640 yolo export modelruns/train/livestock_v1/weights/best.pt formatengine device0 halfTrue说明formatonnx 产物给 CPU 机器或 ARM 盒子用配合 onnxruntime 推理formatengine 是 TensorRT 专用格式只能在同一型号 GPU 上运行halfTrue 启用 FP16推理能快一倍左右。部署教程里最常被跳过的一步是导出后重新验证ONNX 的 NMS 实现和 PyTorch 原版不完全一致框和置信度会有小偏差导出后必须拿 20 张有标注的图跑一次比对确认偏差没有造成漏检。5.2 端到端验证跑通预测不等于系统可用yolo predict modelruns/train/livestock_v1/weights/best.pt sourcetest_video.mp4 saveTrue yolo val modelruns/train/livestock_v1/weights/best.pt datadatasets/data.yamlpredict 命令跑一条从未进过训练集的监控视频确认整条链路从读取、推理、画框到输出视频文件没有断点val 命令在验证集上输出每类的 precision/recall/mAP。对异常行为系统看的重点不是总体 mAP而是 fighting、limping 这两类的 recall。总体 mAP 高但异常类 recall 低告警系统就是摆设。5.3 上线前要调的三个参数置信度、NMS IoU 与告警冷却参数默认值牧场场景建议confidence0.250.35~0.5误报多就抬高NMS IoU0.70.5~0.6牲畜重叠时降低告警冷却无30~60 秒避免同一目标反复弹窗confidence 直接决定 precision/recall 的取舍异常行为样本少置信度天然偏低先按 0.3 跑一天统计误报来源是检测错还是行为判定错再逐步调。NMS IoU 在围栏密集的镜头里要降因为相邻牲畜的框重叠严重时默认值会把两个目标合并成一个。告警冷却按帧数实现注意换算成真实秒数时别漏了帧率。先高阈值少报跑几天收集日志再往低压比一开始追求“全检出”要可靠。把 conf 从 0.25 抬到 0.5 重新导出一次 ONNX 再跑一遍那条测试视频看漏检和误报的平衡点变化比你盲调一天网络的收益更直接。本文还有配套的精品资源点击获取