
简介目标检测是计算机视觉领域的核心任务之一其原理是通过深度学习模型在图像中定位并分类物体。在灾害监测等实际工程应用中目标检测技术面临小目标、复杂背景和边缘设备算力等多重挑战。基于YOLO的实时检测方案结合数据增强、切片推理和跟踪过滤等优化手段能够有效提升小目标召回率并抑制误报。该类系统在边坡监测、山体滑坡预警、工业视觉等场景具有重要价值可实现7×24小时实时运行。本文将深入解析一套基于YOLO的山体滑坡落石检测系统的完整实现过程涵盖数据构建、模型训练、误报抑制到TensorRT边缘部署的工程实践。 去年接了某个山区公路边坡监测的项目要做的就是从监控摄像头画面里实时识别山体滑坡和落石。当时团队里有人提了一嘴直接用YOLO现成的框架拿来就能用。结果真做下去才发现所谓“拿来就能用”只是幻觉数据、小目标、误报、边缘设备算力每一步都有坑。这套基于YOLO的山体滑坡落石检测系统的完整实现过程我想把它写出来——不仅是模型怎么训练更重要的是整条链路怎么从“实验室能跑”变成“现场真能用”。希望这篇东西能帮到正在做灾害监测、工业视觉或者任何小目标检测项目的朋友尤其是那些准备拿YOLO快速出活但还没踩过一遍坑的人。1. 为什么落石检测不能照搬通用目标检测方案1.1 落石检测问题的特殊性目标小、速度突变、背景干扰强先说结论落石检测和常规的目标检测任务差别比大部分人想象中大得多。通用目标检测里待检目标通常是行人、车辆、猫狗这类占据画面比例较大、外观特征清晰的物体。YOLO在这些任务上的表现确实可以做到开箱即用。但落石检测完全不同。第一个麻烦是目标尺度。监控摄像头架在山体对面画面里一块30厘米大小的落石往往只占几十个像素甚至不足整张图像面积的1%。这种极端小目标在YOLO的设计体系里天生吃亏——标准YOLO模型的主干网络下采样32倍一个640×640的输入图像特征图就是20×20每个格子对应原图32×32像素区域。几十个像素的落石在特征图上基本没有任何响应。第二个麻烦是运动特性的突变。行人和车辆的运动轨迹相对平滑而落石从静止到滚落速度变化剧烈同时伴随弹跳、碎裂、遮挡。在一个监控视角下落石可能在某一帧突然出现下一帧就改变了方向。这意味着单帧检测之外还需要结合运动分析来辅助判断。第三个麻烦是背景干扰。山体表面有大量岩石纹理、植被阴影、雨水反光这些形态在视觉上和落石有很强的相似性。如果简单地用一个通用目标检测模型去跑晴天上午阳光直射时每帧都能给你检出几十个“疑似落石”全是误报。1.2 场景对模型的硬性约束7×24小时、边缘部署、实时响应灾害监测系统和一般物体识别项目还有一个巨大差异它要求事故前预警而不是事后分析。这意味着系统不能像离线批量识别那样把视频抽几帧传给服务器处理。它需要7×24小时不间断运行在监控站或者路侧机房的设备上实时处理多路视频流。我这次部署的边缘设备是一台Jetson Orin Nano 8GB算力有限跑一个标准YOLOv8s在1080P视频上就已经很吃力更别说还要同时跑跟踪、预警逻辑和视频推流。实时性要求也极高。落石从开始滑落到滚到路面往往只有几秒到几十秒的时间。系统从检测到预警再到联动声光报警或者阻断道路整个链路必须控制在1到2秒以内。这就决定了模型不能太复杂预处理不能太重后处理逻辑不能有太多阻塞点。1.3 为什么最终选择YOLO而不是其他方案在做方案选型时我对比过传统视觉方案帧差法、光流法、背景建模、双阶段检测器Faster R-CNN系列和单阶段检测器YOLO系列、SSD。传统视觉方案首先被排除。背景建模在光照突变、雨雾天气下几乎完全失效帧差法和光流法能检测到运动区域但无法区分是落石、飞鸟还是树叶晃动误报率会让人崩溃。双阶段检测器精度确实更高但速度是硬伤。Faster R-CNN在边缘设备上处理1080P视频基本只能跑到个位数FPS根本满足不了实时预警需求。YOLO系列最终胜出的原因有三点第一速度和精度的平衡在单阶段检测器里最优YOLOv8s在Orin Nano上经过TensorRT加速后能达到30到40FPS完全满足实时要求第二生态成熟从训练到部署的工具链非常完整团队不需要花大量时间做工程适配第三社区活跃针对小目标检测、特定场景优化的方案多遇到问题能找到参考。2. 数据从哪来落石检测数据集构建的完整链路2.1 公开数据集里几乎没有现成答案这个项目前期最痛苦的事就是落石检测没有现成的大规模公开数据集。我能找到的相关资源非常有限有做山体滑坡发生区域检测的遥感数据集比如LEVIR-CD针对的是卫星影像上的大范围地表变化和我们要做的“监控画面里检测正在滚落的石头”不是一回事有些学术论文里附带少量落石测试视频但标注质量参差不齐样本数量少得可怜直接拿来训练几乎没有意义。所以落石检测的数据工程必须在项目内部从零开始搭。我的做法是分三步走视频采集、数据抽帧、半自动标注最后再用合成数据做关键补充。2.2 现场视频采集与抽帧策略如何在几万帧里选出有效样本现场监控摄像头通常覆盖一段几十米长的边坡区域拍摄角度固定。视频采集不难难的是从连续几十小时甚至几天的视频里筛出包含落石事件的片段。我的做法是先用运动检测算法做第一层粗筛。这一步不需要深度学习直接用OpenCV的帧差法或MOG2背景建模检测画面里是否有“突然出现的运动区域”。正常天气下边坡背景非常稳定一旦有落石滚落运动区域会非常显著。把包含运动区域的时间段提取出来再人工确认哪些是真的落石事件哪些是飞鸟、昆虫、车辆干扰。筛选策略上有一个经验宁可多留也不要漏粗筛阶段允许大量误检因为后续人工确认成本相对可控但漏掉一个真实落石事件整个数据集就少了一条宝贵正样本。实际项目里我抽了几万帧候选帧人工逐帧确认后留下约3000帧有效落石画面。抽帧时还要注意一个问题不要总从同一段视频里均匀抽帧否则数据集里会充满大量高度相似的画面模型很容易过拟合到特定场景。我的策略是同一段落石事件的连续帧里隔几帧抽一张确保一段时间内最多保留十张左右增加样本的多样性。2.3 标注规范小目标、遮挡情况下怎么打框标注工具用的是LabelImg和Roboflow但真正关键的不是工具而是标注规范。落石检测的标注最核心的规范是“框住可见的、独立的石块单位”。落石滚落过程中可能碎裂成多块如果多块石头紧密靠近我倾向于单独框每一个可见石块而不是用一个大框套住整体。因为模型学到的目标边界越清晰检测定位越准确。小目标标注还有一个容易忽略的细节YOLO格式的bbox坐标是归一化的如果目标只有几个像素标注时稍微偏差一点归一化后的坐标误差就会被放大。我要求标注员在标注时把标注框紧贴目标边缘宁可略微内缩也不要向外扩展——向外扩展容易把背景岩石纹理圈进框内增加误检。对于严重遮挡的目标比如一块石头被树干挡了一半我的做法是照常标注但打上遮挡标志。后期训练时根据遮挡比例决定是否丢弃样本。实测下来保留部分遮挡样本反而有利于模型在实际场景中的泛化完全不遮挡的“标准样本”训练出来的模型遇到遮挡情况很容易漏检。还有一个很多人会忽略的点负样本也要标注。这里的“负样本”不是指空图而是指那些“看起来像落石但实际不是”的画面区域比如固定不动的岩石、雨滴飞溅、光影变化形成的假目标。我单独建了一个负样本目录把这类区域标注为background类别训练时混合进数据集。这一步对控制误报率帮助极大。2.4 数据增强策略模拟雨雾、扬尘、光照突变野外场景的天气复杂度远超训练集里能覆盖的范围雨雾、扬尘、逆光、夜间红外模式切换每一个都会让模型的检测效果明显退化。数据增强是解决这个问题的关键手段。除了常规的随机翻转、旋转、缩放、HSV调整我重点做了三类针对性增强模拟雨雾用高斯模糊叠加噪声模拟雾天能见度下降的画面让模型不因为图像模糊就丢失目标。光照突变随机调整亮度、对比度模拟云层遮挡和阳光直射交替出现的场景。运动模糊对局部区域做运动模糊处理模拟落石高速滚落时画面拖影的效果。这里要特别提醒一个实践细节数据增强的比例不能盲目加大。增强后的样本太“假”反而会让模型学到不真实的特征。我最终把增强后样本控制在总样本量的30%以内并且每次训练前都人工抽几批增强后的样本检查视觉效果确认没有畸变到离谱再开始训练。2.5 合成数据用小规模仿真补足极端场景针对夜间的落石检测真实数据极其稀缺因为夜间光线差、落石不易发现监控画面上往往只有一个模糊的小黑影。我试了一个非常有用的办法用游戏引擎做合成数据。在Unity里搭建了一个模拟边坡场景设置不同大小、不同颜色的石块从不同高度滚落同时改变光照和相机距离渲染出几千张合成图像再自动生成标注框。合成数据和真实数据混合训练后夜间场景的检测准确率提升非常明显漏检率下降了三成以上。当然合成数据和真实数据之间一定有域差异所以我把合成数据的占比控制在20%以内并且混合训练时给合成样本设置更低的loss权重让模型在学习时优先拟合真实分布再用合成数据补充边界情况。3. 模型选型与训练调优全过程3.1 版本选择YOLOv8还是更新版本项目启动时团队里对YOLO版本分歧很大。新成员倾向于直接用最新版本理由是“新版本肯定更好”老成员建议用YOLOv5理由是“资料多、踩坑少”。最终我选了YOLOv8原因比较实际YOLOv8的C2f模块和Anchor-Free检测头在小目标任务上表现稳定Ultralytics生态提供的训练、导出、部署流程非常顺滑而且和TensorRT的兼容性做得比较好踩坑成本可控。后面我也测过更新的YOLO11和YOLO-WorldYOLO-World的开放词汇检测能力很惊艳但需要额外维护文本提示词体系对灾害监测这种固定目标场景反而增加复杂度YOLO11性能有提升但当时在边缘设备的推理引擎还有兼容性问题。最终维持了YOLOv8s作为主力模型YOLOv8n作为备用轻量模型。这里我个人的教训是版本选择不要跟风要看你的部署环境和工具链成熟度。生产环境追求的是稳定可控而不是“最新”。3.2 关键训练参数imgsz、batch、epoch的调优实录落石检测最关键的训练参数我认为第一是imgsz输入图像分辨率第二才是batch和epoch。我用640×640起跑训练完发现小目标漏检严重把imgsz提到1280×1280后小目标的召回率显著提升但训练时间和显存占用大幅上升在单张RTX 3090上训练速度慢了一倍多。后来采用了一种折中方案前100个epoch用640分辨率训练让模型先收敛到稳定状态后50个epoch用1280分辨率微调。这相当于让模型先学会基本特征再细化小目标的分辨能力实测效果比全程1280训练差不了太多时间却省了40%。batch大小方面我踩过一个坑显存不够时把batch从16减到4结果训练一直在震荡mAP死活上不去。后来才意识到batch太小导致BN层统计量不稳定。解决方案是开启Ultralytics的梯度累积参数模拟更大的batch同时配合Warmup让训练过程稳定下来。epoch设置建议不要死守默认。我习惯用早停机制监控验证集mAP和loss连续30个epoch没有提升就提前停止。最终模型一般训练到230至260个epoch就收敛了比硬跑300个epoch节省不少时间。3.3 小目标专项优化P2层、SAHI切片推理与分辨率选择YOLOv8默认的检测头包括P3、P4、P5三层分别对应8倍、16倍、32倍下采样。小目标小于32×32像素主要靠P3层检测但在落石场景里很多目标比这个还小P3层也经常无能为力。针对小目标我试过两种方案都值得分享。第一种是给YOLOv8加P2检测头。P2层对应4倍下采样特征图比P3大4倍保留的空间细节更多。改动方法是在Ultralytics的yaml配置文件中新增一个从骨干网络第2层引出的检测分支。这个改动对代码量要求不高但训练显存占用上升明显推理速度也会下降20%左右。实测中P2层让小于16×16像素的目标召回率提升了约15%但带来一定的误报上升需要后续阈值调整来平衡。第二种是SAHISlicing Aided Hyper Inference切片推理。原理很简单把大图切成多个小块分别送进模型检测再把结果拼接回去。这种方法对显存占用几乎无影响但推理时间会随切片数量成倍增长。对于1080P监控画面我切成2×2切片把模型推理时间从30ms增加到90ms但小目标召回率提升了20%以上还是可以接受的。分辨率选择上最终我选择在部署阶段使用960×960输入分辨率而不是原生的1080P。原因有两点一是TensorRT对960这个尺寸的优化较好二是将1080P缩放后送入模型相比在1280分辨率下推理速度更快且目标尺度损失在可接受范围内。4. 从“能检出”到“能用”误报抑制与预警策略4.1 置信度阈值与NMS参数别用默认值这是整个项目里收益最明显的调整之一。用YOLOv8默认的置信度阈值0.25去跑落石场景结果惨不忍睹——晴天上午每帧能检出上百个box绝大多数是岩石纹理和光影变化。逐一调整后发现落石检测的精确率和召回率对置信度阈值极其敏感阈值设太高比如0.5会把大量模糊的小目标滤掉模型变成“睁眼瞎”阈值设太低又会被背景干扰淹没。我的最终方案是分双阈值检测阶段用0.15的较低置信度阈值保证召回预警阶段再要求目标在连续多帧中置信度超过0.35才触发报警。这样既避免了单帧误报又不至于因为阈值过高漏掉真实落石。NMS的IoU阈值也要调。默认的0.45过于激进会把密集分布的落石碎块合并成一个大框导致后续跟踪丢失目标。我把IoU阈值降到0.3让相互靠近的目标能保留为多个独立检测框跟踪效果明显改善。4.2 用跟踪器做时间连续性过滤单帧检测的固有缺陷单帧检测有一个天然缺陷它把每一帧当作独立图像处理无法利用时间维度的信息。一个静止的岩石纹理可能因为光照角度变化在某几帧被误检成落石一个真正的落石虽然每帧都被检出但如果只看单帧结果也无法和误检区分。我引入了ByteTrack跟踪器来解决这个问题。核心逻辑是真正的落石一定有一个时间上连续的运动轨迹而误检往往在空间上随机出现、难以形成连贯轨迹。具体实现分三步对每个检测框分配唯一ID并记录它的轨迹。设定一个“观察期”一个目标连续出现在至少3帧且位置变化符合“向下滚动”的运动规律才判定为有效落石。跟踪器还能平滑相邻帧的检测结果弥补单帧漏检的问题——如果某一帧因为模糊没有检出目标但前后帧都检出了且轨迹连续可以插值补上。实测下来ByteTrack引入后误报数从每小时十几次降低到每小时一到两次效果非常明显。代价是增加了约10ms的CPU开销在整体延迟预算内完全可以接受。4.3 区域规则与相机标定把AI从“哪都能检”变成“只在危险区检”模型和跟踪器只能回答“画面里是否有落石”但在实际预警中位置信息比有无信息更重要一块在坡顶缓慢滑动的石头危险性远低于一块已经滚到路基上的石头。我的做法是在监控画面中标定多个感兴趣区域ROI一级警戒区公路路面和路基边缘任何落石出现在这里立即触发最高级别报警。二级警戒区边坡中段落石在此区域出现时预警并持续跟踪。三级观察区坡顶和远处山体检测到后仅记录日志不触发外部报警。ROI标定需要做透视校正和坐标映射。我给每路视频用棋盘格标定相机内参和外参再根据标定结果计算出监控画面中每个像素对应的真实世界坐标。这样当检测到落石时系统输出的就不是“画面坐标x, y”而是“离公路边缘距离约12米、位置在桩号K23500附近”这个输出对公路养护部门来说才是真正可行动的。需要注意的是单目相机标定只能做平面映射无法准确估计目标的z轴高度。如果要做更精确的空间定位需要双目相机或者激光雷达配合。当前项目里我们用单目加区域判定的方案已经能满足预警需求。4.4 误报的最后一层拦截逻辑规则与人工复核工单即使有了置信度阈值、跟踪器和ROI规则实际运行中还会冒出一些意想不到的误报。比如夜间昆虫飞过镜头前在红外补光下就是一个快速移动的亮点跟踪器甚至能形成一条看似合理的运动轨迹。我学了弹道导弹防御系统里的一个思路加入了多层拦截的“拦截树”每一层只负责过滤特定类型的假目标如果不能确认是落石就继续往下传如果在某一层被判定为“不可能是落石”直接丢弃。具体规则包括运动方向判定落石整体应向下滚落向上飞行或原地悬浮的目标直接过滤。速度范围约束速度过快超过自由落体上限或过慢连续多帧几乎静止的目标重点排查。目标尺寸约束过大超过一个成年人或过小不到几个像素的目标单独处理。这层规则过滤后整个系统的可用性才算真正达到预警级别。我还设计了一个“人工复核工单”机制系统每天把触发过报警但未达到预警标准的事件截图和短片段保存下来生成工单推送给值班人员由人做最后确认。这些确认结果再回流到数据集里作为新样本持续优化下一版模型。这个闭环非常重要它让系统每跑一段时间就会变得更好而不是原地踏步。5. 部署与工程化把模型真正塞进监控系统5.1 推理引擎选型ONNXRuntime还是TensorRT训练好的模型要部署到边缘设备推理引擎选型绕不开ONNXRuntime和TensorRT两个选择。我第一次部署用的是ONNXRuntime优势是通用性好、跨平台、集成简单。在Jetson Orin Nano上跑YOLOv8sFP32精度大约能到15FPS。这个速度在1080P下勉强够用但没有余量去跑多路视频和后续的跟踪逻辑。后来换成TensorRT FP16推理速度提升非常明显单路视频达到了35FPS以上几乎是ONNXRuntime的两倍。TensorRT的代价是模型转换流程相对繁琐先把PyTorch模型导出为ONNX格式再用trtexec工具做解析和优化中间还要处理动态shape、插件兼容性等一系列问题。但考虑到边缘设备算力有限这层折腾是值得的。一个经验教训TensorRT转换时固定输入尺寸会让优化效果更好。我最终把模型输入固化为960×960放弃了对动态尺寸的支持。这样TensorRT能针对这个尺寸做更多硬件层优化推理速度又能再提升10%左右。代价是不同分辨率的输入都需要先缩放但实际影响可控。5.2 视频流接入解码环节成了隐藏瓶颈模型在TensorRT下速度快了新的瓶颈出现了——视频流解码。起初我用OpenCV的VideoCapture直接读RTSP流CPU占用率直接飙到80%以上解码速度跟不上推理速度整条链路还是卡在十几FPS。一番排查后问题出在OpenCV默认调用FFmpeg软解在Jetson设备上没有利用硬件解码单元。换成GStreamer管线调用NVMM硬件解码后CPU占用率骤降到15%视频解析和模型推理的瓶颈才算真正解决。这里分享一个具体的GStreamer管线配置用于在Jetson上拉取RTSP流并硬解码gst-launch-1.0 rtspsrc locationrtsp://your_camera_ip:554/stream1 \ ! rtph264depay ! h264parse ! nvv4l2decoder \ ! nvvidconv ! video/x-raw,formatBGRx \ ! videoconvert ! video/x-raw,formatBGR \ ! appsink drop1解释一下几个关键参数drop1表示当处理速度跟不上时丢弃旧帧保证系统始终处理最新的画面而不是堆积延迟nvvidconv把NVMM内存中的数据转到普通内存供模型使用。如果不用这个转换TensorRT直接读取NVMM内存推理还能更快但工程复杂度会明显上升我这里选择了稳妥方案。5.3 系统工程化看门狗、断线重连、日志与遥测AI模型部署到生产环境后真正的挑战不是模型本身而是系统能不能稳定地跑下去。我在这套系统里设计了三个工程化保障机制第一是进程健康看门狗。边缘设备部署了一个独立的看门狗进程每隔30秒检查一次主推理进程的心跳。如果心跳超时自动重启推理进程并记录重启原因。没有这个机制模型进程一旦因为内存泄漏或异常输入崩溃整个预警系统就静默失效了那比不装系统还危险。第二是视频流断线重连。监控摄像头RTSP流经常会因为网络抖动断开。这里的关键是不要用长时间阻塞的读帧方式而是设置超时机制检测到播流异常后自动重新握手。每次重连之间要有退避策略防止摄像头被频繁请求打爆。我的实现里设置了1秒、2秒、4秒、8秒的指数退避重试最多重试5次仍然失败就报警给运维人员。第三是运行日志和遥测。除了常规的日志文件我还把每个检测结果、每帧的推理耗时、CPU和GPU占用率、温度等指标上报到本地的时序数据库InfluxDB里配合Grafana做可视化监控。这样做最大的价值不是“看板好看”而是当模型在某些时段出现异常时可以回溯分析当时环境因素和系统状态快速定位是天气变化、光线问题还是设备降频导致的。5.4 实测性能数据与端到端延迟分析最终系统在Jetson Orin Nano 8GB上跑了两路1080P视频流整体性能数据如下组件性能指标备注视频解码2路1080P25fps依赖NVMM硬解YOLOv8s推理TensorRT FP16单路约35ms/帧960×960输入ByteTrack跟踪约8ms/帧CPU运行ROI规则与预警逻辑约2ms/帧几乎无开销端到端延迟约300-500ms从画面出现落石到发出预警端到端延迟300到500毫秒看起来不高但要注意这个指标是“单帧处理时间乘以累计帧数”因为连续帧跟踪需要积累观察期。确实存在跟踪器要等3帧确认后才给出置信判断所以一个速度极快的落石可能在0.5秒后才被系统确认。对于落石预警这种场景这个延迟是可以接受的但如果未来要接入主动拦截系统这个延迟还需要进一步压缩。实测里还有一个有趣的发现Orin Nano的电源模式对性能影响巨大。默认模式功率只有7W推理速度会下降到20FPS以下切到15W性能模式后速度立刻恢复到35FPS。但同时设备温度也从60°C升到75°C需要确保设备散热条件良好否则长时间高温运行会导致GPU降频。6. 回看这个项目哪些决定做得对哪些坑其实可以避开6.1 做得对的三个决定第一个对的决定是数据优先策略。项目早期我把大量精力花在数据采集、筛选和标注规范上而不是急着调模型。事实证明投入产出比最高的永远是数据。后面训练时几乎没有在模型结构上做过大改动效果却很稳定根子就在数据质量上。第二个对的决定是引入跟踪器。很多YOLO项目止步于“模型检测结果直接可视化”看起来效果不错但一接实际场景就崩溃。跟踪器把检测问题从单帧提升到时序维度彻底改变了整个系统的可用性。这套思路不只适用落石检测任何固定场景下的连续监控类项目都可以借鉴。第三个对的决定是构建了负样本库。最初让我做负样本标注的是个老工程师他说“你要告诉模型什么不是它才知道什么是”。事实证明这个思路极其正确负样本混合训练后系统误报率下降了近一半。6.2 如果再做一次我会怎么改进如果这个项目重来一遍有些地方我会做得不一样。数据集层面我会更早引入合成数据并且一开始就建立从真实场景到Unity参数之间的映射流程而不是后期才补。这样合成样本的尺度、光照、纹理一致性会好得多训练效果还能再上一个台阶。模型层面我会从第一天就考虑多模型协作的可行性而不是只用一个模型应对所有天气和时段。理想的方案是白天和夜间分别训练两个专用模型白天模型优先保证精度夜间模型使用红外增强的输入并更依赖于运动特征。多模型按时间或环境亮度自动切换效果会比单模型强不少。这个方案当时因为排期原因没有上线是我比较遗憾的点。部署层面我会从一开始就做监控面板和告警链路的半自动化测试而不是等系统上线后再补。预警系统的价值不仅在于“检测到了”更在于“检测到之后能在一分钟内让该知道的人知道”。这个链路如果不在项目早期就反复验证关键时刻极容易出问题。最后说一个最深刻的体会做落地项目尤其是灾害监测这种“可能一年不报警一报警就是大事”的系统考验你的不是某一个模型的精度而是整个系统在极端条件下是否还能稳定、可靠地工作。YOLO只是这套系统里的一个组件真正让系统“能用”的是数据、跟踪、规则、工程化这些看起来不那么炫酷的东西。也希望正在做类似项目的朋友能少走一点我走过的弯路。本文还有配套的精品资源点击获取