做了这么多年目标检测有一个体会越来越深单张图片上的 YOLO 跑得再漂亮离真正可用的视频 AI 系统还有一大段路。YOLO 解决的是“这张图里有什么、在哪”的问题而生产环境里我们需要的是“这一路视频流里目标是什么、状态如何、要不要告警”中间隔着取流、跳帧、跟踪、事件判定、告警联动这一大堆脏活累活。我最近把一个基于 YOLO 的检测服务升级成了完整的实时视频 AI 管线核心是用 SmartMediaKit 做集成框架把模型推理和视频流处理、业务逻辑解耦开。这篇文章想把整条思路和踩过的坑完整记录下来从 YOLO 本身的技术要点讲到视频管线的工程化落地适合正在做目标检测但还没真正上视频流的同学也适合已经在跑视频分析但觉得架构越写越乱的人参考。1. 为什么单张图像检测还不够实时视频 AI 的第一个分水岭1.1 从静态检测到连续推理的思维切换刚开始做 YOLO 项目的人习惯的套路是准备一批图片数据标注训练然后拿测试集看 mAP最后写个脚本对单张图片做推理展示。这套流程本身没问题但一旦接上视频流所有预设都会被打破。视频里每一秒有 25 到 30 帧YOLO 在 GPU 上推理一张 640x640 的图大约需要 10 到 30 毫秒算下来单模型处理能力其实跟得上帧率问题是取流、解码、缩放、跟踪、业务判断这些环节全都会抢占时间。你以为是模型慢实际上瓶颈往往在管线的其他环节。视频 AI 和静态检测的第二个本质区别是时序信息。单帧检测看到的只是一个瞬间但很多业务场景需要的是“目标从哪来、往哪去、停留了多久”。比如监控场景下的吸烟检测单纯检测到烟头意义不大只有当烟头出现在人脸附近且持续一段时间才构成告警条件。这类逻辑必须建立在跟踪和时间窗口之上而跟踪又依赖每一帧的检测结果。所以当你决定做视频 AI 时实际是在搭建一个多模块协同的流水线YOLO 只是其中一个核心组件而不是全部。1.2 SmartMediaKit 的定位做视频与模型之间的管道市面上做视频分析的方案很多但大多数要么太重比如完整的视频管理平台要么太轻只是一个 SDK什么都得自己拼。我在选型的时候最头疼的是视频流接入、解码、抽帧、推理调度、结果回调这些基础设施到底是自己写还是用现成的。自己写意味着要处理 RTSP 断开重连、多路并发、内存管理、线程安全这一堆边界问题写出来的代码大概率又丑又难维护。用现成的大平台又往往被它绑定换模型、换推理后端都很费劲。SmartMediaKit 之所以合适是因为它正好卡在“视频接入”和“AI 推理”中间把媒体处理和智能分析之间的那段管道搭好了。思路是这样的SmartMediaKit 负责从各种视频源取流、解码、转码把视频帧以统一的格式推给推理模块推理模块比如 YOLO跑完检测后再把结果回传给 SmartMediaKit 做事件处理或推流叠加。两边之间是标准接口模型可以随便换视频源也可以随便换模块之间互不绑架。相当于 SmartMediaKit 是那个修高速公路的YOLO 是高速公路上跑的车你要换车不用重新修路。提示如果你现在只是想把 YOLO 跑在一张图片上确实不需要 SmartMediaKit 这种中间层。但只要有“多路视频流 实时检测 业务联动”这三个关键词就值得认真考虑把媒体层和推理层拆开。2. YOLO 核心技术底盘架构、损失函数与后处理2.1 YOLO 架构演进中的关键选择聊集成之前先把 YOLO 本身的技术底子过一遍因为后面所有工程决策都建立在对模型的理解上。YOLO 从 v5 开始基本奠定了“Backbone Neck Head”的经典结构。Backbone 负责提取特征比如 v8 里用的 CSPDarknet 结构通过跨阶段局部连接在减少计算量的同时保证梯度流动Neck 部分用 FPN PAN 结构做多尺度特征融合让小目标和大目标都能有自己的特征层Head 则负责在特征图上预测边界框、类别和置信度。到 v8 和 v9、v10 之后最大的变化是anchor-free 化。早期 YOLO 需要预设 anchor 框也就是一组固定宽高比的先验框模型预测的是相对于 anchor 的偏移量。anchor-free 模型直接预测目标中心点到四条边的距离省掉了 anchor 聚类和匹配的繁琐过程。从工程角度看这意味着模型的输入输出结构更干净了后处理逻辑也更简单。你在部署时无论选择哪个版本都要先弄清楚它的输出张量是什么格式这直接决定了后面的解码和后处理怎么写。架构选型还牵扯到训练成本。v8n 这样的小模型参数量只有 3 百万左右一张消费级显卡就能训适合快速验证v8x 参数量超过 6800 万精度更高但推理速度会明显下降。我的建议是先用小模型跑通整个管线确认业务效果没问题再根据速度余量去升级大模型而不是一上来就追求最强精度。2.2 损失函数里的门道损失函数决定了一个检测模型到底在优化什么这部分很多人训练时直接把默认参数拿过来用换了数据集效果不对也不知道去哪里找原因。YOLO v8 的损失主要由三部分构成分类损失用 BCEWithLogitsLoss边界框回归损失用 CIoU 或 DFLDistribution Focal Loss然后还有一个针对 anchor-free 的分布损失。CIoU 相比早期的 IoU Loss 做了三个改进把中心点距离、宽高比一致性都考虑进来了。举个直观的例子两个预测框的 IoU 完全一样但一个中心点偏移大一个偏移小IoU Loss 会认为它们一样差CIoU 则会明确惩罚中心点偏移多的那个这直接加速了训练收敛。DFL 的思想更有意思它不直接回归框的坐标值而是预测坐标落在预设区间上的概率分布然后取期望值作为最终坐标。这个设计让模型对边界框的预测更平滑不容易出现框的抖动。训练自己的数据集时如果感到收敛慢先把学习率和 batch size 的关系理清楚。batch size 翻倍学习率一般也相应调整。另外标签平滑label smoothing对分类头能起到正则化作用尤其在数据量不大、标注噪声比较多的时候能把过拟合压下来一点。损失这层弄明白了你会发现自己调模型时不再是瞎试参数而是能定位到是回归问题还是分类问题。2.3 后处理流程解析模型前向推理输出的是一堆原始张量离我们看到的检测框还差两步解码和非极大值抑制。解码负责把模型输出的相对值换算成原图坐标NMS 负责把重复检测同一个目标的框合并掉。这两步看起来简单但写得不好会让延迟翻倍。后处理这块一个常见的坑是在 CPU 上用 Python 循环跑 NMS。一张 640x640 的图模型推理只要 15 毫秒NMS 用纯 Python 写可能要 20 毫秒。所以工程上一般会把解码和 NMS 都放到 GPU 上用 TensorRT、ONNX Runtime 或者 OpenCV 的 CUDA 版 NMS 来做。Ultralytics 框架里已经集成了高效的 NMS 算子但如果你是自己导出的 ONNX 模型就要注意算子是否被推理后端支持。置信度阈值的设置也很讲究。阈值设太低大量误检框会进入 NMS导致计算量增大而且可能出现错误合并阈值设太高又可能漏掉真正的目标。我习惯的做法是做一个简单的双阈值策略用较低的阈值比如 0.25进 NMS 防止漏检NMS 的 IoU 阈值设在 0.5 到 0.7 之间输出后再用 0.4 到 0.5 的业务阈值做最终过滤。这样可调节的空间更大不至于为了降误检把召回也压没了。3. 数据这一关绕不过去标注、格式转换与数据集管理3.1 标注格式的“巴别塔”问题训练一个效果可用的 YOLO 模型最耗时间的往往不是训练本身而是数据准备。做过检测项目的都知道市面上标注工具输出的格式五花八门LabelImg 是 XMLLabelMe 是 JSONCVAT 有自己的格式KITTI 数据集又是另外一套。而 YOLO 训练要的是最简单的 txt 格式每一行是“类别ID 中心点x 中心点y 宽度 高度”所有坐标都用相对于图片宽高的比例值表示。这种格式简单但反直觉很多人第一次接触时会搞混。注意两点第一坐标是归一化比例值范围在 0 到 1 之间不是像素值第二数据格式是“中心点 宽高”不是左上角 右下角。如果从 COCO 的 JSON 格式转过来需要做一次中心点换算。COCO 里每个标注的 bbox 是 [x, y, width, height]其中 x、y 是左上角坐标转到 YOLO 格式就需要自己算中心点并除以图片宽高。3.2 KITTI 转 YOLO 的转换脚本思路很多做自动驾驶相关项目的人都会接触到 KITTI 数据集它的标注格式跟 YOLO 差别很大。KITTI 每个对象是一行文本字段特别多类别、截断程度、遮挡程度、观测角度然后是 bbox 的左上角和右下角像素坐标后面还有 3D 框信息。转成 YOLO 格式时只需要提取我们关心的字段。我给你一个核心转换逻辑的参考实现跑通后你完全可以根据自己的标注格式改import os def kitti_to_yolo(kitti_line, img_width, img_height): parts kitti_line.strip().split() if len(parts) 15: return None class_name parts[0] # KITTI bbox 字段是第5到第8个left, top, right, bottom left, top, right, bottom map(float, parts[4:8]) # KITTI 坐标越界修复 left max(0, min(left, img_width - 1)) right max(0, min(right, img_width - 1)) top max(0, min(top, img_height - 1)) bottom max(0, min(bottom, img_height - 1)) if right left or bottom top: return None box_w right - left box_h bottom - top x_center left box_w / 2.0 y_center top box_h / 2.0 # YOLO 格式归一化 x_center_norm x_center / img_width y_center_norm y_center / img_height w_norm box_w / img_width h_norm box_h / img_height # 类别映射需要自己维护比如 Car - 0, Pedestrian - 1 class_id class_mapping.get(class_name, -1) if class_id -1: return None return f{class_id} {x_center_norm:.6f} {y_center_norm:.6f} {w_norm:.6f} {h_norm:.6f}这段代码里我特别加了坐标越界修复和非法框过滤。KITTI 原始标注偶尔会出现 bbox 的 right 或 bottom 超出图片边界的情况直接拿去训练会出 NaN 或让损失骤增。这类坑不在数据里过一遍很难发现等训练到一半 loss 突然变成 nan你往回查的代价比现在大得多。转换完一定要做抽样可视化把标注框画回图片上人工确认这一步省不得。3.3 半监督与自动标注在视频场景的落地视频数据有个特殊优势相邻帧之间的目标位置变化很小。这意味着可以用训练好的模型对视频抽帧做自动标注然后人工只修正错误的部分能节省大量时间这就是半监督标注的核心思路。我在一个工厂质检项目里就用过这个方案先手工标注 500 张关键帧训练一个初版模型然后用模型对剩余 2000 张视频帧做预测把置信度高于 0.8 的检测结果直接转成标注置信度在 0.4 到 0.8 之间的留给人来检查低于 0.4 的当作漏检单独提出来人工补标。这样整体标注效率提升了至少三倍而且人对结果也更放心因为模型标注的高置信度框本身就比较准。自动标注需要注意一个陷阱模型对自己犯的错误是“自信”的。如果一个类别的特征跟另一个类别很像模型可能始终以高置信度给出错误预测自动标注就会把错误固化下来。所以自动标注出来的数据必须按类别做一次分布统计看看每个类别的数量是不是合理再抽一部分做二次人审。数据质量永远比数据数量重要喂进去一堆噪声训练出来的模型只会把噪声当真理。数据集管理方面有一个常被忽略的动作——数据集划分。YOLO 训练通常把数据按 8:1:1 分成训练集、验证集、测试集但要注意划分前先按类别做分层采样保证每个类别在三个集合里的比例大致一致。如果是视频抽帧数据还要注意按视频序列划分而不是按帧随机划分否则同一段视频的相邻帧会同时出现在训练集和验证集里验证集的指标会虚高得离谱。4. 用 SmartMediaKit 搭实时视频推理管线4.1 管线整体架构把 YOLO 和视频流真正结合起来的环节就是 SmartMediaKit 该上场的地方。我设计的整体管线是四层结构接入层SmartMediaKit 统一管理 RTSP、RTMP、GB28181 等不同视频源的接入屏蔽协议差异处理层解码、缩放、颜色空间转换把视频帧转成推理模块需要的张量格式推理层YOLO 模型加载、推理、后处理对外暴露标准接口可以切换不同的推理后端业务层跟踪、告警判断、事件推送、结果存储这四层中最容易被人忽略的是第一层。很多人一开始只接一两路视频用 OpenCV 的 VideoCapture 去读 RTSP 流很顺手但一上生产接到十几路就崩了——RTSP 流的网络抖动会导致 VideoCapture 阻塞一路卡住全盘卡住。SmartMediaKit 的价值就在这里它有专门的处理线程管理每路流的连接状态断流自动重连不会让一路故障拖垮整个进程。这是自己做解码接入时很难在短时间做稳定的部分。4.2 帧管理不要让推理拖垮取流实时视频管线里最核心的矛盾是取流解码的速度和推理的速度不一致。摄像头按固定帧率推流但模型推理不一定能跟上这个速度尤其当多路视频共享一张 GPU 时。解决方案是抽帧和缓冲池。SmartMediaKit 里可以配置每路视频的推理频率比如摄像头 25 帧每秒但我们只需要每 200 毫秒做一次检测那就每 5 帧取 1 帧送推理其他帧直接丢弃或跳过。这样做的好处非常明显GPU 负载直接降到原来的五分之一检测的实时性损失却几乎感知不到因为在安防和工业场景下200 毫秒的延迟完全在可接受范围内。缓冲池的设计同样关键。取流线程把帧写入一个有界队列推理线程从队列里取帧处理。这个队列必须是有界的否则当推理速度跟不上时队列会无限堆积内存不断上涨直到 OOM。我常用的配置是每个视频源一个容量为 10 到 20 帧的队列队列满了就丢最旧的帧保证进来处理的都是新鲜的数据。这里有个反直觉的经验与其让推理处理延迟了 2 秒的旧帧不如丢帧处理最新帧。对实时监控来说检测结果的实时性远大于连续性。一个处理两秒前画面的系统即使每帧都检测了给出的告警也已经失去意义。4.3 检测结果如何喂给后续模块YOLO 输出的检测结果只是一组坐标和类别置信度到业务可用还有距离。SmartMediaKit 的集成方式是把检测结果封装成统一的事件对象包含目标 ID、类别、置信度、边界框坐标、时间戳、视频源 ID 这些字段然后通过回调或者消息队列发给上层业务模块。这里我强烈建议引入目标跟踪模块。只用检测不做跟踪你会遇到一个特别尴尬的场景同一个目标在相邻几帧里被检测到了但没有办法知道它们是同一个目标。这导致无法统计目标数量、无法判断目标运动轨迹、无法做跨帧的状态判断。接入跟踪后每个目标会有一个稳定的 ID后续的行为分析才有基础。常见的选择是 ByteTrack 或 DeepSORTByteTrack 因为简单高效跟 YOLO 搭配得比较多。业务层拿到跟踪后的结果才能做真正的判断逻辑。比如在消防通道占用检测场景里我们需要的不只是“检测到一辆车”而是“同一辆车停在这个区域超过 5 分钟”。这个逻辑需要维护一个每个目标 ID 的首次出现时间配合当前时间计算停留时长。别看逻辑不难它是整个系统价值的直接体现。很多时候客户对算法精度要求没那么苛刻但对业务判断逻辑的合理性非常敏感。5. 边缘部署实战RK3588 上的模型转换与优化5.1 模型转换流程很多实际项目不能依赖服务器上的 GPU摄像头附近的边缘设备才是主力。RK3588 是现在边缘 AI 设备里性价比很高的一款芯片8 核 CPU 加上 6 TOPS 算力的 NPU跑轻量级 YOLO 模型做实时检测完全够用。而且 RK3588 的视频编解码能力很强硬解多路 1080p 视频很轻松这在边缘设备里是一个非常实用的组合。RK3588 上跑 YOLO 不能直接加载 PyTorch 的权重需要把模型转成 RKNN 格式这是 Rockchip 的 NPU 专用格式。转换链路是PyTorch 权重先导出为 ONNX再用 rknn-toolkit2 转成 RKNN。导出 ONNX 这一步要注意模型的输入输出节点名称Rockchip 的转换工具对输入输出节点的名称有要求不一致就会报错。还有一个更隐蔽的问题YOLO 模型里的某些算子 NPU 不支持转换时会自动落到 CPU 上执行如果落到 CPU 的算子刚好是性能热点整体推理速度就会大幅下降。我的经验是转换完成后直接用 RKNN 的模型推理跑一遍速度测试跟理论算力预期对比一下如果差距过大就回头检查算子兼容性。一键部署脚本在这种场景下特别有用。RK3588 的开发环境配置涉及交叉编译工具链、RKNN 驱动、运行时库、板端依赖这一套手工配置至少折腾半天。我把整个部署流程写成了一个脚本板子拿到手后只需要执行一条命令就能完成环境准备、模型拷贝、服务注册、自启动配置。脚本太长这里不完整贴了核心思路就是把板卡环境检测、依赖安装、模型分发、服务配置拆成四个函数每一步都有明确的日志输出。运维同学接手之后不需要了解 RKNN 和 YOLO 的细节也能独立完成设备替换和重建。5.2 量化与性能调优RK3588 的 NPU 原生支持 int8 量化计算浮点模型转过去时如果做 int8 量化推理速度通常能提升两到三倍但精度会有一定损失。量化分为训练后量化和量化感知训练前者简单但精度损失可能比较大后者精度好但需要改动训练流程。我的建议是先做训练后量化用验证集跑一下指标如果 mAP 损失在可接受的范围内比如 2% 以内就不要折腾量化感知训练了。如果损失太大再考虑用带量化感知的训练恢复一部分精度。量化还需要一个校准数据集也就是一批有代表性的图片用来统计每层激活值的分布范围。这个校准集不能随便用几张图凑数要覆盖模型在真实场景里可能遇到的各种光照、角度、目标尺寸。在监控场景下如果校准集里全是白天的画面晚上红外模式下的检测效果就会异常糟糕。校准集一般准备 200 到 500 张图就够了重点是分布有代表性。性能调优层面有一个工程取舍要提一下输入分辨率。YOLO 默认的输入是 640x640但边缘设备上可以考虑降到 416x416 甚至 320x320。分辨率每降一档推理耗时大约减少 40% 到 50%但小目标的检测能力也跟着急剧下降。所以如果是检测车辆、人员这类中等尺寸的目标降分辨率完全可行要检测烟头、小零件这类小目标就必须保持高分辨率或者用多尺度推理。这个决策必须在项目早期就做因为后期改输入分辨率意味着要重新校准量化、重新测试耗电发热成本不低。6. 从检测到更多任务实例分割、姿态估计与跟踪6.1 一个模型框架跑多种任务YOLO 的生态早就超出了纯目标检测的范畴。YOLOv8 系列同一个模型框架下既支持检测detect也支持实例分割segment和姿态估计pose。你只需要在训练时指定不同的任务头它就能输出不同的结果。实例分割输出的不是边界框而是目标的轮廓多边形姿态估计输出的是人体的关键点坐标。这些能力对于很多复杂业务场景是刚需比如工位操作规范性分析需要姿态估计或者货物堆叠检测需要分割轮廓。任务头的切换代价比想象中低因为 Backbone 和大部分 Neck 是共享的。这意味着你不需要为每个任务单独维护一个模型只要训练好一个主干再分别训练不同的任务头或者干脆用一个多任务模型同时输出检测框、分割掩码和关键点。这里面的资源节省是实打实的尤其是边缘设备上存一个模型和存三个模型的差距非常明显。我自己的经验是如果视频管线里确实需要多种任务优先考虑多任务统一模型而不是各跑各的单任务模型。多任务共享特征还有一个隐藏收益如果几个任务之间是相关的比如检测和分割联合训练反而能互相提升精度因为共享的特征表示学习到了更泛化的语义信息。不过多任务模型也有代价。推理耗时会比单任务检测高一些因为分割分支的解码和后处理明显更重。在 SmartMediaKit 里做集成时需要为不同的任务配置独立的输出回调检测结果的消费者和分割结果的消费者往往不是同一个模块。我在做智慧工地场景时就拆过两层检测结果直接送告警模块做安全帽佩戴判定分割结果送可视化大屏做人员密度热力展示。两边各取所需互不干扰消费端只关心数据的语义不关心数据是怎么推理出来的。6.2 多任务如何协同多任务协同的要义在于让结果之间能互相引用。比如姿态估计得到的关键点可以辅助检测结果做更精细的判定检测到一个行人框同时关键点给出了头顶位置那么安全帽是否佩戴的判断就精确到像素级了而不是简单看框内颜色。这种跨任务的协同效果是单独跑多个模型很难做到的因为它们的输出没有对齐。协同的工程基础是时间戳对齐。同一个目标在不同任务里的输出必须保证来自同一帧或相邻帧否则关键点和检测框会错位。SmartMediaKit 的帧管理在这里又体现了价值它给每一帧打上了唯一的 sequence ID检测结果和分割结果都携带这个 ID业务层拿到结果后按 ID 做关联就能严格保证数据一致性。还有一个容易忽略的点任务优先级。边缘设备算力有限不能什么任务都以最高频率跑。我的做法是给任务配置不同的推理频率检测每 150 毫秒跑一次分割每 500 毫秒跑一次姿态估计每 300 毫秒跑一次。这样总算力需求是可控的每个任务又能基本满足自身的实时性要求。调度的粒度比尺寸重要得多。7. 常见问题与排查实录7.1 推理延迟高怎么办延迟高的排查顺序应该是先确认是取流延迟、推理延迟还是后处理延迟。一个简单的方法是分别在取流回调、模型输入、模型输出、后处理结束四个位置打时间戳看耗时集中在哪一段。我遇到过很多次大家潜意识里觉得延迟是模型慢一测发现 80% 的时间花在视频解码上。如果是解码慢先检查是不是软解换硬解通常能解决如果是推理慢再看是不是输入分辨率太高、模型太大、量化没开。多路视频共享 GPU 时延迟高的原因常常是资源争抢。NVIDIA 的 GPU 默认支持多线程并发执行但如果多个推理线程同时提交大批量任务GPU 可能会出现 queue 堆积。用 Triton 这一类推理服务器可以很好地管理批量调度但对边缘设备来说太重了。简单方案是给各路视频设置推理时间片或者用模型实例的并发度控制工具。SmartMediaKit 的帧丢弃策略在这里也起作用GPU 忙的时候主动丢帧比硬撑着排队强。7.2 检测框抖动怎么处理检测框抖动通常不是模型的问题而是单帧检测的天然缺陷。相邻两帧中目标位置可能只有一两个像素的偏移但模型预测的框边缘偶尔会跳变几像素。人眼对这类高频抖动非常敏感尤其在做框选展示的时候观感很差。解决思路有三种第一种是跟踪平滑跟踪算法本身就有状态估计能力输出的是平滑后的轨迹而不是原始检测框第二种是后处理平滑对框坐标做指数滑动平均第三种是降低检测频率的同时用跟踪结果做插值让输出结果在时间上更均匀。我比较推荐第一种因为跟踪平滑不光解决了抖动问题还顺带解决了目标 ID 一致性问题。如果只是展示用可以只在显示层做平滑不做语义改变。有一个细节平滑系数不能设得太大否则检测框会明显滞后于真实目标运动速度快的目标会出现“拖影”效果。7.3 内存涨不停怎么排查内存持续增长几乎可以肯定是资源没释放。视频管线里最常见的内存泄漏点有三个线程不退出、队列无限增长、OpenCV Mat 没有释放。治本的方法是加监控在 SmartMediaKit 的管线上定期打印内存占用和线程数量。如果线程数只增不减说明有线程在异常退出后没有清理如果队列长度不断增长说明消费速度跟不上生产速度如果是 Mat 没释放就要去查是不是哪里在循环里创建了新对象而没有调用 release。信号量机制进程被手动杀掉后如果还有线程在读写共享内存会产生野指针这是最隐蔽的内存崩溃来源。所以正常退出流程一定不要只是 kill 进程要先通知管线停止推流确认推理线程全部退出再释放模型资源最后销毁媒体连接。这个顺序在 SmartMediaKit 的文档里其实有标注但很多人不看直接在终端 CtrlC 完事结果下一次启动时各种 memset 和内存越界。7.4 小目标检测效果差小目标检测是 YOLO 系模型的长期痛点因为小目标在特征图上的像素占比太小经过几次下采样之后特征基本没了。工程上能做的调整有提升输入分辨率、使用更大尺度的特征图P2 层、增加浅层特征的权重、用多尺度训练、专门用小目标样本做数据增强。但最立竿见影的办法往往是你没想到的——用视频的多帧信息做融合。连续几帧里同一个目标在移动如果把几帧的特征做对齐后融合小目标的信噪比会显著提升。这个方向已经有成熟的算法但对工程来说复杂度较高。如果你暂时不想上这么重的方案建议退一步想业务是不是真的需要检测那么小的目标很多时候可以通过调整相机安装位置和角度让小目标在画面里变大这比算法优化省力得多。8. 我自己在实践中的几个体会文章写到这说几个纯个人的经验和感悟不算什么总结就是踩坑之后沉淀下来的东西。第一管线的稳定性和模型精度一样重要。一个精度很高但三天两头断流、内存泄漏、线程崩溃的检测系统在生产环境里是没人敢用的。模型效果不够好至少能看到结果只是准确率低一点管线不稳定业务方会失去对你的信任这个恢复成本远远大于优化算法的成本。所以我做任何视频 AI 项目第一版一定是先把管线跑通、加好日志和监控、确认重试和断流恢复机制可靠然后才开始调模型。第二每一层都要做可观测性。SmartMediaKit 接入的每一路视频我都会记录取流帧率、解码耗时、推理耗时、队列积压量、丢帧数量这些指标。这几个数字一出来系统瓶颈在哪儿基本一目了然。比如丢了大量帧但队列积压不严重说明取流环节出了问题队列持续积压但 GPU 利用率不高说明推理线程的调度逻辑有毛病。数据比感觉可靠多了。第三部署时永远要准备一键恢复方案。边缘设备不像服务器出问题随时可以远程重启。一个真正好用的部署维护流程必须保证设备断电重启后系统能自动恢复运行模型、配置、服务全部自启动。我们项目里 RK3588 板卡都是做成开机自启脚本每隔几秒检测主进程是否存活挂了就拉起同时写日志。这套机制上线之后运维的参与度降到了几乎没有这是一个对外交付项目能不能收尾的硬指标。第四如果项目里涉及多家硬件和模型版本接口设计一定要预留好状态字段。YOLO 不同版本、不同推理后端输出的结构看似一样但实际坐标体系、置信度含义可能略有差异。我的做法是在 SmartMediaKit 的推理结果结构体里加一个 metadata 字段记录模型的版本号、输入分辨率、置信度阈值。这样当检测结果出现异常时能根据 metadata 快速定位是哪个模型版本、哪个配置引发的而不是对着结果猜。这个小改动在长期维护中节省的时间非常可观。这篇文章的主要内容和技术实践都已经完整记录在上面了。如果你正在 YOLO 往视频 AI 迁移的路上希望这些思路能帮你少走几步弯路。如果手头项目卡在某个具体环节欢迎按上面的问题定位思路去排查多数情况都能找到一个明确的解决路径。