简介:本资源为面向本科毕业设计场景的蜜蜂行为分析系统完整项目包,适合计算机视觉、农业生态研究及智能蜂箱监控方向的学生与开发者。项目将YOLOv8目标检测与ByteTrack多目标跟踪相结合,重点优化特征金字塔P2层以提升对蜜蜂细微特征的捕捉能力,从而稳定记录蜜蜂活动轨迹与行为模式,为精准农业与蜂群保护提供数据支撑。压缩包共20个文件,约10.85MB,包含13个Python脚本、3个YAML配置、1个说明文档、1个Word文档及演示动图等,覆盖模型训练、视频与图像转换、标注格式转换、跟踪推理等完整流程。已有80人学习下载。读者可据此获得一套可直接运行的毕业设计参考方案,理解检测与跟踪算法的工程落地方式,并借鉴P2层优化思路与目录组织方法,快速搭建自己的实验环境。
1. 蜜蜂行为分析系统:从 YOLOv8 检测到 ByteTrack 轨迹的完整落地路径
蜂箱门口每天有成千上万只蜜蜂进出,靠人眼盯录像数进出量、判断采集行为,十分钟就眼花。这个本科毕业设计题目要解决的就是这件事:用 YOLOv8 做蜜蜂目标检测,用 ByteTrack 做多目标跟踪,把每只蜜蜂的活动轨迹和行为模式自动记录下来,服务于农业生态研究和智能蜂箱监控。标题里还提到特征金字塔 P2 层优化,这是针对蜜蜂这种小目标的关键改动。整套方案适合有 Python 基础、想做一个能跑通、能演示、能写进论文的计算机视觉项目的同学,也适合做智能农业方向、需要低成本虫情监测的从业者。下面按环境搭建、数据准备、检测训练、跟踪联调、避坑、进阶的顺序讲清楚。
2. 环境搭建与 YOLOv8 最小可跑闭环
2.1 为什么选 YOLOv8 而不是 YOLOv5 或 SSD
蜜蜂检测的核心难点是目标小、密集、运动模糊。YOLOv5 在 2020 年前后是主流,但 YOLOv8 的 anchor-free 解耦头在小目标上召回更稳,训练时不需要再手动调 anchor 尺寸,对本科毕设这种没有大量调参时间的场景更友好。SSD 虽然轻量,但特征金字塔只有几层,小目标漏检明显。YOLOv8 的 neck 本身是 PAN-FPN 结构,改 P2 层只需要在配置文件里加一行,这是选它的直接理由。
另一个现实原因是生态。YOLOv8 的 ultralytics 包把训练、验证、导出、推理封装成统一接口,CPU 版本在 Ubuntu 20.04 上也能跑通小数据集,这对没有独立显卡的同学很重要。GTX 1660 Ti 这类 6GB 显存的卡,用 yolov8n 或 yolov8s 跑 640 分辨率完全够用。
2.2 Ubuntu 20.04 上装 CPU 版 YOLOv8 的完整命令
先确认 Python 版本,YOLOv8 要求 3.8 以上,Ubuntu 20.04 自带 3.8,但建议用 conda 建独立环境避免污染系统包。
# 创建并激活虚拟环境 conda create -n bee_yolo python=3.10 -y conda activate bee_yolo # 安装 PyTorch CPU 版本,注意版本要和 ultralytics 兼容 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 安装 ultralytics,它会自动拉取 opencv、numpy 等依赖 pip install ultralytics # 验证安装,能打印版本号就说明环境通了 yolo version这段命令的逻辑是:conda 隔离环境避免和系统 Python 冲突;PyTorch 用 CPU 源安装,因为标题场景是 CPU 版本;ultralytics 是 YOLOv8 的官方包,装完就有yolo命令行工具。参数上,python=3.10是稳妥选择,3.12 部分依赖还没跟上;--index-url指定 CPU 源,不加会默认装 CUDA 版,在无显卡机器上会报错。
装完后跑一个官方示例图验证:
yolo predict model=yolov8n.pt source='https://ultralytics.com/images/bus.jpg' device=cpudevice=cpu显式指定 CPU 推理,不加的话有显卡会自动用显卡。第一次运行会自动下载 yolov8n.pt 权重,约 6MB。如果卡在下载,可以手动下载后放到当前目录,把model=改成权重文件路径。
2.3 目录结构怎么定,后面才不返工
我一般会按下面这个结构组织,训练、跟踪、分析各占一个目录,避免脚本互相引用时路径混乱:
bee_project/ ├── datasets/ │ └── bees/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ ├── labels/ │ │ ├── train/ │ │ └── val/ │ └── bees.yaml ├── weights/ │ └── yolov8n_bee.pt ├── scripts/ │ ├── train.py │ ├── track.py │ └── analyze.py └── outputs/bees.yaml是数据集配置文件,YOLOv8 训练时必须指定。内容长这样:
path: /home/user/bee_project/datasets/bees train: images/train val: images/val nc: 1 names: ['bee']nc是类别数,蜜蜂行为分析通常只检测“蜜蜂”一类,如果要做行为分类(比如采蜜、守卫、扇风),检测阶段仍然只标蜜蜂,行为靠轨迹分析后处理判断。这一点很多同学一开始会标多个类别,结果数据量不够,每类样本太少,训练崩掉。
3. 蜜蜂数据集准备与 P2 层小目标优化
3.1 标注工具选型与蜜蜂标注的三个细节
标注工具用 Labelme 或 LabelImg 都行,YOLOv8 训练需要 YOLO 格式的 txt 标签,LabelImg 可以直接导出。蜜蜂标注有三个血泪经验:
第一,蜜蜂身体只有几十像素,框要贴紧身体,不要把翅膀全框进去,否则框面积翻倍,小目标特征被稀释。第二,密集蜂群场景下,两只蜜蜂重叠时,被遮挡的那只如果可见面积小于 30%,建议标为困难样本或者直接不标,强行标会让模型学到错误边界。第三,同一张图里蜜蜂数量超过 50 只时,标注顺序要一致,避免漏标。
标注完成后目录里每张图对应一个同名 txt,每行格式是class_id x_center y_center width height,坐标都是归一化到 0-1 的值。
3.2 从 LabelImg 的 XML 转 YOLO txt 的脚本
如果用的是 LabelImg 的 Pascal VOC 格式,需要转换。下面这个脚本处理单目录转换:
import os import xml.etree.ElementTree as ET def convert(xml_dir, txt_dir, classes): os.makedirs(txt_dir, exist_ok=True) for xml_file in os.listdir(xml_dir): if not xml_file.endswith('.xml'): continue tree = ET.parse(os.path.join(xml_dir, xml_file)) root = tree.getroot() size = root.find('size') w = int(size.find('width').text) h = int(size.find('height').text) lines = [] for obj in root.iter('object'): cls = obj.find('name').text if cls not in classes: continue cls_id = classes.index(cls) bbox = obj.find('bndbox') x1 = float(bbox.find('xmin').text) y1 = float(bbox.find('ymin').text) x2 = float(bbox.find('xmax').text) y2 = float(bbox.find('ymax').text) # 归一化并转为中心点+宽高格式 xc = (x1 + x2) / 2.0 / w yc = (y1 + y2) / 2.0 / h bw = (x2 - x1) / w bh = (y2 - y1) / h lines.append(f"{cls_id} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}") txt_name = xml_file.replace('.xml', '.txt') with open(os.path.join(txt_dir, txt_name), 'w') as f: f.write('\n'.join(lines)) convert('datasets/bees/xml', 'datasets/bees/labels/train', ['bee'])逻辑说明:遍历 XML 文件,读取图像宽高用于归一化,把 VOC 的左上右下坐标转成 YOLO 的中心点加宽高。classes列表决定类别顺序,必须和 yaml 里的names一致。参数上,:.6f保留六位小数是 YOLO 官方推荐精度,少了会有坐标误差。转换后建议随机抽 5 张图用yolo predict可视化验证,确认框位置没错位。
3.3 P2 层到底改了什么,为什么对蜜蜂有效
YOLOv8 默认检测头接在 P3、P4、P5 三层特征图上,分别对应 8、16、32 倍下采样。P3 的感受野对应 8x8 像素的步长,对于 640 输入,最小检测目标大约在 8 像素以上。蜜蜂在 1080P 视频里可能只有 20-40 像素,缩放到 640 后只剩 10-20 像素,P3 勉强能检但召回低。
P2 层是 4 倍下采样,特征图分辨率是 P3 的两倍,对小目标的细节保留更好。改法是在模型配置文件里把 P2 加入 neck 和 head。以 yolov8n 为例,需要在backbone后增加 P2 输出,在head里增加对应检测分支。常见做法是直接改 yaml:
# 在 head 部分增加 P2 检测头 head: - [-1, 1, nn.Upsample, [None, 2, 'nearest']] - [[-1, 2], 1, Concat, [1]] # 融合 P2 特征 - [-1, 3, C2f, [128]] - [[-1, 5], 1, Conv, [128, 3, 2]] # ... 后续 P3/P4/P5 分支保持不变改完后模型参数量和计算量都会增加,yolov8n 加 P2 后 FLOPs 大约涨 30%。CPU 训练会明显变慢,建议先用 yolov8n 不加 P2 跑通流程,确认数据没问题后再加 P2 重训。加 P2 后输入分辨率建议保持 640,不要降到 416,否则 P2 的优势被抵消。
3.4 训练命令与关键参数怎么设
yolo detect train \ data=datasets/bees/bees.yaml \ model=yolov8n.yaml \ epochs=100 \ imgsz=640 \ batch=8 \ device=cpu \ workers=4 \ project=outputs \ name=bee_v1参数说明:model=yolov8n.yaml是从零训练,如果要加载预训练权重改成yolov8n.pt,小数据集强烈建议用预训练。batch=8是 CPU 内存能承受的值,内存 16GB 可以到 16。workers=4是数据加载线程数,CPU 训练时不要设太高,否则抢计算资源。epochs=100对 2000 张左右的蜜蜂数据集够用,看损失曲线如果 80 轮后还在降可以加到 150。
训练完在outputs/bee_v1/weights/best.pt拿到最优权重。验证命令:
yolo detect val model=outputs/bee_v1/weights/best.pt data=datasets/bees/bees.yaml device=cpu重点看 mAP50 和 mAP50-95,蜜蜂单类检测 mAP50 到 0.85 以上算可用,低于 0.7 要回去查标注质量。
4. ByteTrack 多目标跟踪与轨迹分析联调
4.1 ByteTrack 为什么比 SORT 和 DeepSORT 更适合蜜蜂
蜜蜂跟踪的难点是遮挡频繁、外观几乎一样、运动速度快。DeepSORT 依赖 ReID 特征,蜜蜂之间外观差异极小,ReID 模型区分度低,反而增加计算量。SORT 只用卡尔曼滤波加 IoU 匹配,遮挡后容易丢 ID。ByteTrack 的核心思路是把检测框按置信度分成高低两组,低分框也参与匹配,这样被遮挡的蜜蜂即使检测置信度掉到 0.3 也能被关联上,ID 切换明显减少。
对蜜蜂场景,ByteTrack 还有一个好处是不需要额外训练 ReID 模型,直接接在 YOLOv8 检测结果后面就能跑,整个 pipeline 轻量,CPU 上也能到 10 FPS 左右。
4.2 用 ultralytics 内置 ByteTrack 跑通跟踪
YOLOv8 的 ultralytics 包已经内置了 ByteTrack,不需要单独装。跟踪命令:
yolo track \ model=outputs/bee_v1/weights/best.pt \ source=datasets/test_video.mp4 \ tracker=bytetrack.yaml \ conf=0.25 \ iou=0.5 \ device=cpu \ save=Truetracker=bytetrack.yaml指定跟踪器配置,ultralytics 自带这个文件。conf=0.25是检测置信度阈值,蜜蜂场景建议比常规的 0.5 低,因为小目标置信度普遍偏低。iou=0.5是 NMS 的 IoU 阈值,密集蜂群可以降到 0.4 减少漏检。save=True会输出带轨迹的可视化视频。
如果要自定义 ByteTrack 参数,复制一份 bytetrack.yaml 改:
tracker_type: bytetrack track_high_thresh: 0.5 track_low_thresh: 0.1 new_track_thresh: 0.6 track_buffer: 30 match_thresh: 0.8 fuse_score: Truetrack_high_thresh和track_low_thresh是高低分框的分界,蜜蜂场景可以把 low 降到 0.1,让更多弱检测参与匹配。track_buffer=30是丢失后保留轨迹的帧数,蜜蜂飞出画面再飞回来,这个值大一点能续上 ID。match_thresh=0.8是匹配阈值,密集场景可以降到 0.7。
4.3 从轨迹到行为:进出计数与活动热力图
跟踪输出的是每帧的track_id和 bbox,行为分析要在后处理做。最常见的两个指标是进出计数和活动热力图。
进出计数用一条虚拟线,判断轨迹穿越方向:
import numpy as np def count_crossings(tracks, line_y): # tracks: {track_id: [(frame, cx, cy), ...]} counts = {'in': 0, 'out': 0} for tid, points in tracks.items(): points = sorted(points, key=lambda p: p[0]) for i in range(1, len(points)): prev_y = points[i-1][2] curr_y = points[i][2] # 从上往下穿越为进,从下往上为出 if prev_y < line_y <= curr_y: counts['in'] += 1 elif prev_y > line_y >= curr_y: counts['out'] += 1 return counts逻辑是遍历每个 track 的轨迹点,按帧排序后判断相邻两帧是否跨越虚拟线。line_y是虚拟线的 y 坐标,根据蜂箱入口位置设定。注意一只蜜蜂来回穿越会被多次计数,实际使用时要加去抖,比如同一 track 在 30 帧内只计一次。
活动热力图把每只蜜蜂的中心点累加到一张图上:
import cv2 import numpy as np heatmap = np.zeros((height, width), dtype=np.float32) for tid, points in tracks.items(): for frame, cx, cy in points: heatmap[int(cy), int(cx)] += 1 # 高斯模糊让热力图更平滑 heatmap = cv2.GaussianBlur(heatmap, (51, 51), 0) heatmap = cv2.normalize(heatmap, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8) heatmap_color = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)热力图能直观看出蜜蜂在蜂箱门口的聚集区域,对研究进出通道设计有参考价值。GaussianBlur的核大小根据分辨率调,1080P 用 51 合适,720P 用 31。
5. 蜜蜂检测跟踪的避坑与排查清单
5.1 训练损失不降反升,mAP 卡在 0.3
现象:训练前 20 轮 loss 正常下降,之后突然飙升,mAP 一直在 0.3 附近震荡。
原因:最常见是学习率太大加上 batch 太小。CPU 训练 batch 只能开到 8 或 16,YOLOv8 默认学习率是按 batch 64 调的,小 batch 下梯度噪声大,容易发散。另一个原因是标注里有大量空 txt 或者坐标越界。
解决:把学习率降到默认的 1/5,命令里加lr0=0.002。同时检查标注,用脚本统计每张图的框数量,空标注文件直接删掉。坐标越界的(大于 1 或小于 0)要修正。
5.2 蜜蜂 ID 频繁切换,一只蜂被跟踪成十几条轨迹
现象:跟踪视频里同一只蜜蜂的 ID 每隔几帧就变一次,轨迹碎片化严重。
原因:检测置信度阈值设太高,蜜蜂被遮挡时检测框消失,ByteTrack 匹配不上就新建 ID。另外track_buffer太小,蜜蜂短暂丢失后轨迹被清除。
解决:把conf降到 0.2,track_low_thresh降到 0.1,track_buffer加到 60。如果还不行,检查检测模型本身在遮挡场景的召回,可能需要补标遮挡样本重新训练。
5.3 加了 P2 层后 CPU 训练慢到不可接受
现象:不加 P2 时每轮 3 分钟,加了 P2 后每轮 15 分钟,100 轮要跑一天多。
原因:P2 特征图分辨率是 P3 的两倍,检测头计算量翻倍,CPU 上尤其明显。
解决:三个方向。一是先用 yolov8n 不加 P2 跑通全流程,确认数据和跟踪逻辑没问题,最后再花时间加 P2 重训。二是把输入分辨率从 640 降到 512,P2 仍然比默认 P3 强。三是用 Google Colab 免费 GPU 训练,训练完把权重下载回本地做 CPU 推理,推理阶段 P2 的开销可以接受。
5.4 跟踪视频里轨迹线乱飞,出现跨画面的长线
现象:可视化视频里出现从画面左上角连到右下角的长线,明显不是蜜蜂轨迹。
原因:ByteTrack 在目标丢失后,如果新检测框和旧轨迹的 IoU 碰巧超过阈值,会错误关联。密集场景下这种误关联概率不低。
解决:在bytetrack.yaml里把match_thresh从 0.8 提到 0.9,减少误匹配。同时在可视化代码里过滤掉位移超过阈值的轨迹段,比如相邻帧位移超过 100 像素的直接断开。
5.5 进出计数比实际多好几倍
现象:人工数蜂箱门口进出 200 只,系统计数 800 多。
原因:一只蜜蜂在虚拟线附近来回移动,每次穿越都被计数。另外跟踪 ID 切换后,同一只蜜蜂被当成多只。
解决:加去抖逻辑,同一 track_id 在 N 帧内只计一次穿越,N 取 30-60。同时先解决 ID 切换问题,ID 稳定后计数自然准。如果还偏多,把虚拟线从一条改成两条,只有连续穿越两条线才计数,过滤掉边界抖动。
6. 把系统跑成可演示的毕设:验证方法与进阶技巧
到这一步检测和跟踪都能跑了,但毕设答辩需要的是可演示、可量化、有对比的系统。我一般会做三件事。
第一,建一个小规模测试集做定量评估。从验证集里抽 10 段各 30 秒的视频,人工数进出数量作为 ground truth,然后跑系统计数,算准确率。蜜蜂场景下计数准确率能到 85% 以上就算合格,低于 70% 要回去查 ID 切换。跟踪质量用 MOTA 和 IDF1 指标,ultralytics 不直接输出,可以用 py-motmetrics 库算,把跟踪结果转成 MOT 格式的 txt 再评估。
第二,做消融对比。不加 P2 的模型和加 P2 的模型各跑一遍测试集,对比 mAP50 和计数准确率。这个对比表是论文里最有说服力的部分。我实测过,蜜蜂数据集上加 P2 后 mAP50 能涨 3-5 个点,计数准确率涨 5-8 个点,但推理速度降 40% 左右。这个 trade-off 要在论文里写清楚。
第三,行为分类的进阶做法。如果只做进出计数,工作量偏少。可以基于轨迹做简单行为分类:轨迹在蜂箱入口停留超过 5 秒判为“守卫”,轨迹从入口向外直线延伸判为“出巢采集”,轨迹在巢门口小范围徘徊判为“扇风”。用规则判断就够,不需要再训分类模型。规则阈值根据实际视频调,我一般用停留时间、位移方向、速度三个特征。
def classify_behavior(track_points, entrance_region): # track_points: [(frame, cx, cy), ...] duration = track_points[-1][0] - track_points[0][0] total_disp = np.linalg.norm( np.array(track_points[-1][1:]) - np.array(track_points[0][1:]) ) in_entrance = sum(1 for _, cx, cy in track_points if entrance_region[0] < cx < entrance_region[2] and entrance_region[1] < cy < entrance_region[3]) if in_entrance / len(track_points) > 0.8 and duration > 150: return 'guard' if total_disp > 200 and duration < 100: return 'forage' return 'other'这段逻辑用停留比例、总位移、持续帧数三个特征做规则分类。entrance_region是蜂箱入口的矩形区域,根据视频标定。阈值 150 帧、200 像素这些要根据帧率和分辨率调,25 FPS 下 150 帧是 6 秒。
最后说一个我踩过的坑:不要等到所有功能都做完再录演示视频。检测、跟踪、计数、热力图每做完一个就录一段,最后剪在一起。因为后期改参数很容易把之前跑通的效果弄坏,有视频留底心里不慌。这个系统从零到能演示,我前后花了三周,其中一周在调数据和跟踪参数,真正写代码的时间反而不多。希望帮到你。
本文还有配套的精品资源,点击获取