尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

YOLOv8+ByteTrack实时目标跟踪实战:从检测到轨迹的完整方案

YOLOv8+ByteTrack实时目标跟踪实战:从检测到轨迹的完整方案 简介目标检测与目标跟踪是计算机视觉中两个紧密相关但又截然不同的任务。检测解决单帧中“物体在哪里”而跟踪则解决连续帧中“同一个物体是谁”。基于检测的跟踪tracking-by-detection范式将两者串联其中ByteTrack算法仅依靠检测框的位置与置信度利用卡尔曼滤波预测运动轨迹并通过匈牙利算法进行两阶段匹配尤其有效利用了低分检测框显著减少ID切换。该方案无需额外ReID模型计算开销低在GTX 1660Ti等中端显卡上即可实现实时处理广泛应用于智能监控、交通流量统计、行人轨迹分析等场景。本文详细拆解YOLOv8与ByteTrack的集成方案涵盖环境配置、推理输出、跟踪逻辑、自定义数据集训练、性能调优及部署思路帮助你快速搭建完整的实时目标跟踪系统。 做视觉项目久了你会发现目标检测和跟踪从来都是两件事。检测是单帧的“找出人/车在哪”跟踪是连续帧的“这个ID是谁”。很多人拿到一堆检测框还是不知道怎么算轨迹直到我把YOLOv8和ByteTrack接在一起才把这两个环节真正跑通。这套组合不依赖额外的ReID模型只用检测框的位置和置信度就能完成跨帧关联在GTX 1660Ti这种级别的显卡上都能跑出实时帧率做监控、交通流统计、行人轨迹分析都非常顺手。今天就把这套方案的搭建过程、代码细节、踩坑记录和调参思路完整拆给你看。文章覆盖环境配置、YOLOv8推理输出、ByteTrack跟踪逻辑、训练自己的数据集以及最后的性能优化和部署思路。无论你是刚跑通YOLOv8检测、想给检测结果加上连续ID的新手还是已经在做跟踪、但对ID跳变和漏检不爽的老手这篇都能直接参考。1. 整体设计思路为什么把YOLOv8和ByteTrack绑在一起1.1 检测和跟踪的天然分工目标检测解决的是“这一帧里什么东西在哪”目标跟踪解决的是“上一帧里的那个人这一帧是不是同一张脸、同一个车牌、同一辆自行车”。如果只做检测每一帧的框都是独立的没有任何关联关系。你要统计一个路口10分钟内过了多少人就必须知道哪些框是同一个人的连续轨迹否则每一帧都会重复计数。跟踪算法通常分两类。一类是检测后关联tracking-by-detection先检测再匹配代表就是DeepSORT、ByteTrack、OC-SORT另一类是单目标跟踪SOT给定第一帧目标位置后持续跟踪代表是SiamRCNN、TransT。实时视频流场景基本都用第一类因为目标会不断进出画面靠检测器兜底最稳。而这个项目里选YOLOv8做检测端ByteTrack做关联端就是典型的tracking-by-detection模式。1.2 YOLOv8作为检测器的优势YOLOv8相比前代v5在模型结构上做了不少调整比如把C3模块换成了C2f结构Head从耦合检测头改成了解耦头Anchor从锚框机制改成了Anchor-Free。这些改动带来的直接好处是小目标召回率更高训练收敛更稳定推理速度也没落下。用ultralytics官方仓库几行代码就能加载模型并推理对做工程的人来说特别省事。你可以在同一个库里做检测、分割、姿态估计Pose甚至分类这意味着如果哪天你想从“检测人”升级到“检测人的关键点”不用换框架只要换一个训练好的pose权重前处理代码照样跑。后面我有单独一节讲Pose数据标注怎么做。1.3 ByteTrack的优势不靠外表特征靠位置和运动DeepSORT的思路是“检测 运动匹配 外观特征匹配”它需要额外接一个ReID模型提取每个框的外观特征。外观特征在遮挡严重、行人穿着相似的情况下确实有用但ReID模型本身就是个不小的工作量还要考虑光照、视角变化调参起来非常头疼。ByteTrack走的是另一条路不考虑外观纯粹用卡尔曼滤波预测运动轨迹再用匈牙利算法做框与轨迹的匹配。它的核心创新在于“低分框的利用”。传统检测关联只匹配高置信度检测框得分低于阈值的框基本就被丢弃了。但ByteTrack发现很多低分框其实是被遮挡的目标或者小目标把它们也纳入匹配池哪怕在低分阶段匹配也能显著减少ID切换次数。在很多公开数据集上ByteTrack用纯运动信息就能打过带ReID的DeepSORT而且逻辑简单、速度快特别适合落地。1.4 选型避坑什么情况下不推荐这套组合ByteTrack依赖检测器的质量如果检测器漏检严重跟踪轨迹自然断裂任何后处理都救不回来。所以如果你的应用场景里目标极度密集比如地铁闸机口几百人挤在一起、目标尺度过小比如体育场远端的观众建议先把检测器精度提上去或者考虑检测端加关键点辅助再上ByteTrack。除此之外如果你对同ID的外观一致性要求极高比如必须用同一套服装特征跨场景重识别ByteTrack就帮不上忙了还是得加重识别模型。2. 环境准备与依赖安装2.1 硬件与软件版本参考我实际跑这套方案的机器配置比较普通显卡是GTX 1660Ti 6GBCPU是i7-10750H内存16GB。6GB显存跑YOLOv8s的FP16推理很流畅跑YOLOv8m的batch推理也不至于爆显存只是在训练时需要控制batch size。官方建议的PyTorch版本是1.8以上我在项目里用了PyTorch 2.0.1配合CUDA 11.8跑得很稳。PyTorch 2.1到2.3也试过兼容性没问题但如果你只做推理不必追新版本稳定优先。2.2 安装ultralytics和ByteTrack依赖YOLOv8的官方实现在ultralytics包里一次安装就能覆盖训练、验证、导出、推理。ByteTrack本身是一个独立的开源项目核心代码依赖Python包不多但需要自己组织一下目录结构。# 创建虚拟环境避免和系统Python环境打架 conda create -n yolo-track python3.9 -y conda activate yolo-track # 安装PyTorch注意根据自己的CUDA版本选择 pip install torch2.0.1 torchvision0.15.2 --index-url https://download.pytorch.org/whl/cu118 # 安装ultralytics pip install ultralytics # 安装ByteTrack需要的依赖 pip install cython pip install onnxruntime-gpu pip install lap # 匈牙利匹配库也可以直接用scipy的linear_sum_assignment这里提一下lap库。ByteTrack原仓库用的是lap库做匈牙利匹配很多教程会建议用scipy替代因为lap在Windows上编译容易报错。如果你在Linux环境直接pip install lap没问题Windows下建议用scipy的linear_sum_assignment效果一样。我后面给的代码就用了scipy省得大家踩编译的坑。2.3 权重文件与测试视频准备YOLOv8官方提供了n/s/m/l/x五档预训练权重直接在ultralytics里下载即可from ultralytics import YOLO # 首次运行会自动下载 yolov8n.pt 到当前目录 model YOLO(yolov8n.pt)如果你网速不稳也可以去GitHub Releases手动下载把pt文件放到项目根目录。测试视频建议先用官方提供的示例视频或者自己用手机拍一段固定机位、人多一点的街道视频这样方便观察跟踪效果。不要一上来就用无人机航拍那种目标特别小的视频不然分不清是代码问题是数据问题。3. YOLOv8检测器实现细节3.1 加载模型与推理检测器这部分不复杂但有几个参数直接影响跟踪效果。先看一个最小可用的加载代码from ultralytics import YOLO model YOLO(yolov8n.pt) # 换成你自己的权重路径 # 单帧推理 results model.predict(frame, conf0.25, iou0.5, imgsz1280, verboseFalse)这里conf是置信度阈值iou是NMS的IoU阈值imgsz是输入分辨率。1024或1280的分辨率对密集小目标有明显提升但帧率会降低实际使用要在实时性和精度之间权衡。我一般用imgsz1280跑人流量统计用imgsz640跑一般物体追踪因为640下1660Ti能跑到40帧以上。3.2 输出格式与数据转换YOLOv8推理后的results对象很丰富但跟踪器需要的是一个统一的检测框数组[x1, y1, x2, y2, score, class_id]。注意坐标要归一化到图像像素坐标不要归一化到0-1之间因为卡尔曼滤波的观测值需要像素级尺度。import numpy as np def parse_yolov8_results(results, img_shape): boxes results[0].boxes if boxes is None or len(boxes) 0: return np.empty((0, 6)) xyxy boxes.xyxy.cpu().numpy() conf boxes.conf.cpu().numpy().reshape(-1, 1) cls boxes.cls.cpu().numpy().reshape(-1, 1) dets np.concatenate([xyxy, conf, cls], axis1) # 如果只要行人按类别过滤 # dets dets[dets[:, 5] 0] # COCO里类别0是person return dets这一步有个容易被忽略的细节YOLOv8的boxes.xyxy输出的是原始图像尺寸坐标但如果推理用imgsz1280坐标会自动映射回原图。ultralytics内部已经处理了所以不用自己做缩放。如果你在导出模型后用ONNX Runtime推理那就要自己做缩放和坐标映射这点后面部署一节再讲。3.3 网络结构速览与C2f改动YOLOv8的完整结构图业界已经画烂了这里用语言快速过一遍输入图像经过一个Focus/SiLU卷积和几层C2f模块组成Backbone提取空间特征然后通过SPPF做金字塔池化再进入PAN-FPN结构把高层语义信息与低层空间信息融合最后是三个不同尺度的Detect Head分别对应大中小目标。整个过程没有Anchor每个特征图上的位置直接预测与网格左上角的位置偏差和宽高。C2f是YOLOv8相对v5的C3模块最大的变化。C3把输入分成两条分支一条直接经过多个Bottleneck另一条只是恒等映射最后拼接在一起。C2f则把Bottleneck的数量分支做了更灵活的分裂和拼接信息流更密集理论上感受野和梯度传播都更好。网上有很多“改进YOLOv8”文章基本都盯着C2f里的Bottleneck做手脚比如加ECA注意力、EMA注意力。后面训练一节我会讲自己的实测经验。3.4 损失函数曲线怎么画训练的时候官方代码会在runs/detect/train*/results.csv里记录每一轮的box_loss、cls_loss、dfl_loss、precision、recall、mAP50和mAP50-95。用pandas直接读取画图import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/train/results.csv) plt.figure(figsize(12, 6)) plt.subplot(1, 2, 1) plt.plot(df[ epoch], df[ train/box_loss], labelbox_loss) plt.plot(df[ epoch], df[ train/cls_loss], labelcls_loss) plt.xlabel(epoch) plt.ylabel(loss) plt.legend() plt.subplot(1, 2, 2) plt.plot(df[ epoch], df[ metrics/mAP50(B)], labelmAP50) plt.plot(df[ epoch], df[ metrics/mAP50-95(B)], labelmAP50-95) plt.xlabel(epoch) plt.ylabel(mAP) plt.legend() plt.tight_layout() plt.savefig(loss_curve.png)这里有个坑ultralytics的CSV列名里带了很多空格直接按列名取会失败养成习惯先print(df.columns)看一眼再写代码。损失曲线如果出现“先下降后上升”大概率是学习率过大或训练轮数太多可以做早停。如果loss一上来就NaN检查数据标注有没有越界坐标。4. ByteTrack跟踪器集成全流程4.1 ByteTrack的两阶段匹配思想ByteTrack原论文的核心思想我再用大白话解释一遍。一般追踪器做匹配时只挑置信度大于阈值的检测框比如0.5以上。但现实中很多目标被遮挡时会输出0.2到0.4的低分框。这些低分框如果直接丢掉目标轨迹就会断如果不丢又会混入大量背景误检。ByteTrack的做法是分两步匹配第一轮用置信度高于track_thresh默认0.5的高分框去和已有轨迹做关联。第二轮用置信度在track_thresh和high_thresh之间默认0.1到0.5的低分框去和第一轮没有匹配上的轨迹再次关联。这样低分框只用于“挽救”那些暂时找不到配对的轨迹而不会主动建立新轨迹误检的影响被压到了最小。4.2 核心组件卡尔曼滤波与STrackByteTrack的轨迹状态机继承自StrongSORT每个轨迹STrack有四种状态New新产生、Tracked正在跟踪、Lost暂时丢失、Removed被移除。每个轨迹内部维护一个卡尔曼滤波对象状态向量是[x, y, a, h, vx, vy, va, vh]其中(x, y)是目标中心坐标a是宽高比h是高度后面的v是对应的速度分量。卡尔曼滤波做的事情简单理解就是“用历史速度推测下一帧目标在哪里”再加一个“观测到的新位置修正预测位置”的过程。它的好处是即使这一帧检测器漏检了滤波器也能给出一个合理的估计框让目标轨迹不至于立刻断掉。ByteTrack默认把Lost轨迹保留30帧这30帧内如果还能匹配上就把轨迹救回来。4.3 手写一个轻量集成代码这里我给出一个集成思路不需要引入ByteTrack仓库里那一整套复杂目录。核心是三个类KalmanFilter封装、STrack轨迹、BYTETracker管理者。我用scipy的线性分配代替lap代码量能压缩到150行左右。import numpy as np from collections import deque from scipy.optimize import linear_sum_assignment from filterpy.kalman import KalmanFilter as FP_KF class STrack: def __init__(self, tlwh, score, track_id): self.tlwh np.array(tlwh, dtypenp.float64) self.score score self.track_id track_id self.kf FP_KF(dim_x8, dim_z4) # 初始化卡尔曼滤波状态转移矩阵和观测矩阵 self.kf.F np.eye(8) for i in range(4): self.kf.F[i, i 4] 1 self.kf.H np.eye(4, 8) self.kf.P[4:, 4:] * 1000 self.kf.P * 10 self.kf.Q[-4:, -4:] * 0.01 self.kf.R[2:, 2:] * 10 # 状态 self.state New self.time_since_update 0 self.history deque(maxlen30) def predict(self): if self.kf.x is not None: self.kf.predict() self.time_since_update 1 return self.tlwh def update(self, tlwh, score): self.kf.update(self._tlwh_to_xyah(tlwh)) self.tlwh tlwh self.score score self.time_since_update 0 self.state Tracked上面只是filterpy的写法。filterpy库里已经实现了卡尔曼滤波pip install filterpy即可比手推矩阵省事。不过要注意ByteTrack原版用的是自研的卡尔曼滤波坐标变换和filterpy有一点点差异但核心思想一样不影响理解。接下来的核心是匹配函数和主循环。匹配函数计算每个轨迹预测框和每个检测框之间的IoU再通过匈牙利算法找总体代价最小的配对def iou_batch(bboxes1, bboxes2): bboxes2 np.expand_dims(bboxes2, 0) bboxes1 np.expand_dims(bboxes1, 1) xx1 np.maximum(bboxes1[..., 0], bboxes2[..., 0]) yy1 np.maximum(bboxes1[..., 1], bboxes2[..., 1]) xx2 np.minimum(bboxes1[..., 2], bboxes2[..., 2]) yy2 np.minimum(bboxes1[..., 3], bboxes2[..., 3]) w np.maximum(0.0, xx2 - xx1) h np.maximum(0.0, yy2 - yy1) wh w * h area1 (bboxes1[..., 2] - bboxes1[..., 0]) * (bboxes1[..., 3] - bboxes1[..., 1]) area2 (bboxes2[..., 2] - bboxes2[..., 0]) * (bboxes2[..., 3] - bboxes2[..., 1]) union area1 area2 - wh return wh / np.maximum(union, 1e-6)匈牙利矩阵的尺寸是“轨迹数 × 检测框数”每一格填IoU值IoU越大代表匹配越好但匈牙利算法默认求最小代价所以代码里通常用1 - iou作为代价。这就是很多人看代码会懵的地方为什么矩阵里装的是“1减IoU”。4.4 主循环关联、更新与状态流转每来一帧做四件事用所有轨迹的卡尔曼滤波做预测拿到预测框把检测框分成高分框和低分框高分框先和所有轨迹匹配低分框再和没匹配上的轨迹匹配。最后处理新出现的高分框把它们创建成新轨迹。def update(self, dets, img_info): if len(dets) 0: for track in self.tracked_tracks: track.predict() return self.output_tracks() scores dets[:, 4] remain_inds scores self.track_thresh inds_low (scores self.high_thresh) (scores self.track_thresh) dets_high dets[remain_inds] dets_low dets[inds_low] dets_high, dets_low dets_high[:, :5], dets_low[:, :5] # 1. 对当前跟踪中的轨迹做卡尔曼预测 for track in self.tracked_tracks: track.predict() # 2. 高分框匹配 track_pool self.tracked_tracks matched, unmatched_dets, unmatched_tracks self._match(dets_high, track_pool) for track_idx, det_idx in matched: self.tracked_tracks[track_idx].update(dets_high[det_idx], scores) # 3. 低分框挽救 lost 轨迹 unmatched_tracks [t for t in unmatched_tracks if t.state Tracked] if len(unmatched_tracks) and len(dets_low): matched_low, _, _ self._match(dets_low, unmatched_tracks) for track_idx, det_idx in matched_low: self.tracked_tracks[track_idx].update(dets_low[det_idx], scores) # 4. 新目标确认 for det_idx in unmatched_dets: tlwh xyxy_to_tlwh(dets_high[det_idx]) self.tracked_tracks.append(STrack(tlwh, scores[det_idx], self.next_id())) self.next_id 1 return self.output_tracks()这个循环写得简化了但流程是对的。实际跑的时候你会注意到两个细节一是匹配时不能把“New”状态的轨迹和“Lost”状态的轨迹混在一起否则新轨迹容易被旧轨迹抢占ID二是轨迹一旦变成Lost超过max_time_lost帧默认30就要从列表里移除否则轨迹池越来越大匹配速度越来越慢。4.5 参数调节速查表参数默认值作用调参方向track_thresh0.5高分框阈值决定哪些检测框参与主匹配目标遮挡严重时降到0.4high_thresh0.1低分框阈值下限小于此值直接丢弃误检多时升到0.2match_thresh0.8IoU匹配阈值高于此值才算匹配成功目标移动快时降到0.7max_time_lost30Lost轨迹保留帧数目标频繁出进画面时增大到60min_box_area10小于此面积的框不参与跟踪过滤运动估计的噪点框我自己的经验行人跟踪场景track_thresh0.4、high_thresh0.1、match_thresh0.8是相当稳的组合车辆跟踪场景因为车辆速度大、运动模型变化快可以把match_thresh降到0.7同时把卡尔曼滤波的Q矩阵速度项调大一点让预测框更“敢动”。5. 训练自己的数据集从标注到精度提升5.1 数据准备与Pose标注说明如果你想跟踪的目标不是COCO里的80类那就要自己训练检测器。最常见的两种方向一种是纯目标检测只需要画矩形框另一种是姿态估计需要标关键点。热搜词里出现了“yolov8 pose 数据标注具体操作”这里就顺带说一嘴。无论哪种标注现在常用的工具都是LabelImg检测和Labelme分割/关键点。LabelImg标注完保存为Pascal VOC XML格式再用脚本转换成YOLO的txt格式Labelme保存为JSON同样可以转。姿态标注的具体操作流程是先在图上框出目标整体区域再按顺序标关键点比如行人标鼻子、左右眼、左右肩、左右肘、左右腕、左右髋、左右膝、左右踝一共17个点。每个点要保证可见时才标被遮挡的点要么跳过、要么按数据集要求填0。YOLOv8的训练格式里每个目标一行内容为class_id, x_center, y_center, width, height, px1, py1, px2, py2, ..., p17_x, p17_y其中关键点坐标也都是归一化到0-1。注意关键点要和矩形框一起给定缺一个都训练不了。5.2 数据集目录结构与划分YOLOv8支持自定义数据集需要按下面结构组织datasets/ mydata/ images/ train/ img_001.jpg val/ img_002.jpg labels/ train/ img_001.txt val/ img_002.txt再写一个data.yaml放在和images、labels同级的目录下path: datasets/mydata train: images/train val: images/val nc: 2 names: [person, car]train和val的图片不要有重叠建议按8:2划分。我习惯在划分前先按场景分组比如同一个摄像头出来的连续帧尽量全放train避免val里出现和train几乎一样的画面否则mAP会虚高上线后又被真实场景打脸。5.3 训练命令与关键参数yolo detect train datadatasets/mydata/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch8 \ device0 \ ampTrue \ patience20在GTX 1660Ti 6GB上跑yolov8sbatch8比较安全用上AMP混合精度后显存占用大概4GB左右。如果爆显存优先降batch不要轻易把imgsz降到416以下不然小目标直接废了。patience20表示20个epoch内mAP没有提升就自动早停能省不少时间。训练一个自己的小数据集即使只有三四百张图只要标注认真、场景分布合理yolov8s也能过90的mAP50。但要注意mAP50高不代表跟踪效果好因为跟踪还依赖检测框的稳定性。检测框在连续帧里如果忽大忽小哪怕中心点没变卡尔曼滤波的观测值也会剧烈抖动导致ID切换。所以训练数据里尽量包含不同距离、不同遮挡程度下的目标。5.4 注意力模块改进的实测经验网上很多人把ECA、EMA、CA、SE这些注意力模块塞进C2f里做改进。我自己的实测结论是注意力模块对小目标的提升相对明显对正常尺度目标可能只涨0.3到0.5个点但推理速度会掉10%到20%。在实时跟踪场景里帧率敏感度远高于那0.3个点所以追求实时的项目不要盲目改进网络。真要改建议只改Backbone第一层或者最高层特征图不要在每一层都加。如果你还是想试EMA注意力可以插到C2f的Bottleneck之后ECA注意力可以直接替换掉某些标准卷积。改动之后先用一两百张图的子集做一个20轮的快速训练对比loss曲线是否下降更快再决定要不要完整训练。不要一上来就全量训练否则改崩了你都不知道是注意力的问题还是数据的问题。5.5 从训练日志看问题训练过程中我习惯盯两块一个是results.csv里box_loss的走势正常应该稳步下降然后趋于平缓另一个是验证集的F1曲线如果F1峰值在0.5附近说明置信度阈值设0.5可能不是最优后期推理时可以考虑把置信度降到0.35以下提高召回率再由ByteTrack去过滤误检。6. 实时性能调优与问题排查6.1 在GTX 1660Ti上榨出更高帧率我一开始跑这套组合YOLOv8n加ByteTrack在640分辨率下能到50帧上下换成YOLOv8s帧率降到35帧上下用YOLOv8m就只有20帧出头了。如果你只有1660Ti建议检测器用n或s档后面再靠以下几种方式提帧率。第一推理时开半精度。ultralytics里设置model.half()或者在predict时加halfTrue。FP16在1660Ti这种图灵架构上有额外加速实测能提升15%左右。第二把输入分辨率从640降到544。跟踪目标如果体型较大544和640的精度差距很小但帧率能多5到8帧。第三ByteTrack代码里本身有一点优化空间比如output_tracks里把所有轨迹转成数组返回可以用列表推导式避免循环内append。第四如果摄像头采集帧率本身就24到30帧检测推理没必要跑满60帧可以直接限制跟踪主循环的跳帧逻辑每隔一帧做一次检测中间帧用卡尔曼预测结果补上。6.2 常见问题排查表现象可能原因解决方案同一个目标ID频繁切换检测框不稳定/遮挡严重降低track_thresh到0.35调大max_time_lost跟踪框在背景上乱跳低分误检被当成低分框利用升高high_thresh到0.15检查检测器是否过拟合目标静止时ID也会变卡尔曼滤波速度模型不适合静止目标调大Q矩阵位置噪声让滤波器更信任观测帧率骤降轨迹池里有大量Lost轨迹没有清理检查max_time_lost是否过大及时remove画面抖动时跟踪丢失摄像机运动导致背景变化考虑用全局运动补偿或改用OC-SORT检测框和跟踪框错位输入分辨率缩放导致的坐标误差用letterbox后必须把坐标映射回原图再送跟踪器6.3 部署到嵌入式设备或手机端的思路YOLOv8训练好的模型部署到RK3588这类嵌入式板子或者手机端主要走一条路把PyTorch权重导出为ONNX再转成RKNN瑞芯微平台或者NCNN/TFLite手机端。热词里有人问“yolov8手机安装包”其实不管是Android还是iOS思路都是把模型跑在端侧再通过JNI或C接口调用前端写个视频流处理界面而已。导出ONNX的命令很简单yolo export modelyolov8s.pt formatonnx imgsz640 dynamicFalse simplifyTrue导出后注意检查输出节点。YOLOv8的ONNX输出是三维矩阵[batch, 84, 8400]其中84是4个框坐标加80个类别概率8400是三个特征层的候选框总数。你需要写一个后处理函数做解码和NMS这跟训练时的predict接口完全不一样。RK3588上用瑞芯微的rknn-toolkit2转换再叠加量化能跑到30帧以上手机端则转NCNNarm平台推理一个yolov8n大约能到25帧左右分辨率控制在320以下还能更快。ByteTrack的代码在嵌入式上也能跑但要注意两点一是卡尔曼滤波大量依赖矩阵运算在CPU上开销不小建议优先用C复刻核心匹配逻辑或者直接用纯Python的原型验证后再重写二是嵌入式端内存有限history队列和track_pool要控制上限避免长时间运行内存泄漏。7. 最后分享一个实际项目里的调整心得我在做厂区人员统计的时候最初检测器用YOLOv8s置信度阈值直接沿用默认0.25ByteTrack参数全部默认。结果现场一跑发现一个问题隔着挺远的工人被检测出来后跟踪ID经常在十几帧内变一次因为目标比较小框的抖动幅度相对相机位移来说很大IoU匹配经常失败。后来我把检测输入从640提到960又把confidence阈值降到0.2同时把ByteTrack的track_thresh调到0.3、match_thresh调到0.75情况立刻好转。原因在于检测器高分阶段输出的小目标框如果太严苛很多真实目标被当成低分框而低分框在大目标稀疏场景里反而变成噪声源。这两个阈值一个管检测召回一个管跟踪匹配配合着调才能既留住小目标又防止噪声轨迹。如果你也在做类似的视频监控类跟踪项目建议先录10分钟真实场景视频把检测框可视化出来观察低分框是否有价值再决定ByteTrack的阈值取舍。纯靠调参解决问题的效率永远比不上你对自己场景的理解。本文还有配套的精品资源点击获取
返回列表