
最近我把一套基于Jetson Nano和YOLOv5s的无人机道路抛洒物实时检测系统完整地跑通了从数据集整理、模型训练到机载部署整个链路踩了不少坑也沉淀了不少经验。这套系统解决的实际问题是无人机在空中巡查时能够实时发现路面上的轮胎皮、纸箱、木板、散落砂石等障碍物并能把告警信息和位置回传到地面站。它适合做毕业设计、边缘计算项目、无人机视觉方向研究的同学参考也适合已经在做道路巡检工程应用的朋友拿来借鉴方案。这篇博文我不打算只贴代码而是把为什么选这套方案、数据集怎么搞、模型怎么调、部署怎么加速、实测会遇到什么问题一条线讲清楚。1. 整体方案设计与技术选型思路1.1 为什么是 Jetson Nano YOLOv5s 这个组合先说结论这个组合是“低成本边缘实时检测”里性价比最稳的一套。Jetson Nano在二手市场几百块就能拿下算力虽然只有472 GFLOPS但配合TensorRT加速后跑YOLOv5s这种轻量级模型FP16精度下能做到20到30 FPS完全够无人机巡查这种场景使用。YOLOv5s属于YOLOv5系列里最小的一版参数量只有7.2M左右模型文件大概14MB部署到嵌入式设备上非常友好。很多人会问既然Jetson Orin Nano性能更强为什么还要选Jetson Nano我的观点是先看需求再选硬件。道路抛洒物检测对帧率的要求并没有高速场景那么苛刻10到15 FPS其实就能覆盖绝大多数巡检需求而且Jetson Nano功耗只有5W到10W对无人机续航的影响相对可控。Orin Nano固然性能翻倍但价格也翻了几倍飞丢或者炸机的损失成本完全不同。等模型验证成熟、确需更高分辨率输入时再升级硬件整个代码框架可以无缝迁移。另外一个关键点是生态。YOLOv5的官方仓库对Jetson系列支持非常完善导出ONNX、转换为TensorRT引擎的社区资料一抓一大把踩坑成本低。相比之下如果用YOLOv7、YOLOv8虽然精度更高但在老旧的JetPack 4.6环境里某些算子和自定义模块的兼容性反而需要额外处理对新手不太友好。1.2 系统整体架构与数据流整套系统的数据流可以用三条链路概括图像采集链路、检测推理链路、告警回传链路。图像采集链路无人机挂载的摄像头可以是CSI接口摄像头也可以是USB摄像头或图传摄像头输出视频流通过GStreamer或OpenCV接入Jetson Nano。实际项目中我推荐使用RTSP流方式把摄像头画面通过图传模块发送到机载接收端再由Jetson Nano解码这样摄像头和计算板可以分离安装方便减震布局。检测推理链路Jetson Nano上运行TensorRT加速后的YOLOv5s引擎对每一帧图像做目标检测。检测结果包括目标类别、置信度、边界框坐标。这里要注意不要直接拿原始视频流一帧一帧跑最好做一个抽帧策略比如每2帧推理一次或者按时间间隔0.1秒推理一次既能降低功耗也能减少误报。告警回传链路检测到抛洒物后系统会做“多帧确认”只有连续若干帧都检测到同一区域的同一类别目标才触发告警。告警信息通过串口或WiFi回传到地面站同时在本地保存截图和GPS坐标。GPS坐标的获取可以接一个串口GPS模块或者通过无人机飞控的MAVLink协议读取。整体来看Jetson Nano承担的职责就是“边缘计算节点”所有推理都在本地完成不需要把视频流大量回传地面站这对图传带宽有限的实际场景来说非常重要。1.3 不同方案的取舍对比我身边有人用纯云端方案也有人用大模型方案还有用传统图像处理的我都简单对比过方案优势劣势是否适合本场景云端AI检测视频传回服务器可用大模型、精度高依赖网络、延迟高500ms以上、流量费贵不适合大模型目标检测如YOLOv8x、RT-DETR精度高嵌入式设备跑不动或帧率极低不适合传统视觉边缘检测、阈值分割零成本、延迟低泛化能力差、光照一变就废仅适合特定辅助Jetson Nano YOLOv5s平衡精度、速度、成本需要一定的部署经验适合尤其要说明的是道路抛洒物检测最怕的就是“漏报”。传统视觉方案在固定角度、固定光照的监控摄像头下还能用但无人机视角变化快、光照条件复杂一个阴影或者路面标线就会让阈值分割失效。深度学习模型虽然需要准备数据但泛化能力远好于传统方法这也是我坚持用YOLOv5s而不是OpenCV轮廓检测的原因。2. 数据集构建从零搞定道路抛洒物标注2.1 数据来源怎么找做检测项目数据集永远是第一道坎。道路抛洒物这个方向比较垂直网上没有现成的完整公开数据集直接下载需要自己收集和整理。我整理过的数据来源主要有三类第一自采数据。用无人机在真实道路上空拍摄视频高度控制在30到80米视角尽量模拟实际巡查场景。拍摄时间覆盖白天不同时段包含晴天、阴天、逆光等光照条件。采集到的视频用OpenCV按每5秒抽一帧的方式切成图片筛选出含抛洒物的帧。这个过程比较耗时间但数据质量最高。第二公开数据集的二次标注。BDD100K、Cityscapes这类自动驾驶数据集里包含大量路面目标可以把其中属于“抛洒物”类别的目标比如纸箱、轮胎、锥桶抽取出来重新整理标注格式。虽然它们的视角大多是车载平视和无人机俯拍有些差距但可以作为预训练辅助数据。第三合成数据。用3D建模或者图像合成的方式把抛洒物贴到路面上再通过背景融合生成训练样本。这种方法适合补充稀有类别但做出来的图片和真实感有差距需要控制比例我只用来补“轮胎皮”这种难以采集的类别。需要强调的是数据集的版权和隐私问题一定要重视。自采数据避免拍到清晰人脸和车牌公开数据集要注意授权协议商用之前要逐条核对。2.2 类别定义与标注规范道路抛洒物种类繁多一开始不要太贪心建议先定义6到8个高频类别就够用了。我项目中用的是这8类轮胎皮、纸箱、木板、锥桶、散落砂石堆、塑料布、铁质障碍物、其他抛洒物。“其他”这个类别很重要它能兜住那些标注时说不清、但确实是异物的目标避免模型在推理时把未知物体硬分到某个已知类里。标注工具我用的是LabelImg虽然界面老一点但胜在轻量和稳定。标注格式有两种主流选择PASCAL VOC格式XML文件和YOLO格式txt文件。LabelImg默认导出VOC格式YOLOv5训练需要YOLO格式转换用脚本统一处理。标注规范上有几条实操经验边界框不宜过大尽量贴合目标轮廓但也不能切到目标内部因为YOLO训练的损失函数对框的大小很敏感。被遮挡的目标如果可见面积超过20%仍然标注低于20%就跳过避免引入大量模糊学习信号。夜间或反光严重的图片建议单独建文件夹不要混在正常数据里方便后面做针对性增强。一个小技巧标注时统一使用“从左上角拖到右下角”的习惯虽然LabelImg支持任意方向但统一方向可以避免后期脚本处理坐标时出现负数。2.3 数据增强与小目标处理策略无人机俯拍视角下抛洒物在整张图中的占比通常很小很多目标可能只有三四十个像素宽。这种小目标检测是YOLO系列一直以来的弱项必须从数据处理阶段就做准备。我在离线增强阶段做了四类操作水平翻转、随机旋转±15度、亮度对比度调整、高斯噪声。这部分我建议用脚本批量处理而不是在线增强因为离线增强能看到实际效果避免生成过于畸形的样本。在线增强用的是YOLOv5自带的Mosaic、MixUp、HSV扰动训练时随机应用。针对小目标还有一个更关键的策略——切图。我实验过把原始4096x2160的俯拍大图先切成四块1024x1080的子图再分别送入模型训练小目标的像素尺寸相当于放大了一倍mAP能提高约8个百分点。虽然标注时要多花点时间但效果立竿见影。数据增强的过程中一定要检查增强后的图片是否合理。我出现过旋转90度后“纸箱”被旋转成了“竖条”视觉上已经不像纸箱的情况这种样本混进去只会增加学习难度后来我在脚本里限制了最大旋转角度并在输出前人工抽样检查。2.4 数据划分与YOLO格式转换数据准备好之后要按训练集、验证集、测试集7:2:1的比例划分。注意划分时要以“视频片段”为单位而不是以“图片”为单位。如果同一个视频片段里的连续帧被同时分到训练集和验证集会因为场景高度相似导致验证指标虚高部署到新场景后掉点明显。YOLO格式的txt文件内容很简单每行代表一个目标格式为“类别ID 中心点x 中心点y 宽度w 高度h”所有坐标都归一化到0到1之间。转换时要注意类别ID和类别名称一一对应别在脚本里写死。我项目的完整数据目录结构是这样的dataset/ ├── images/ │ ├── train/ # 训练集图片 │ ├── val/ # 验证集图片 │ └── test/ # 测试集图片 ├── labels/ │ ├── train/ # 对应的YOLO标注txt │ ├── val/ │ └── test/ ├── data.yaml # 数据集配置文件 └── classes.txt # 类别列表写转换脚本的时候有一个坑值得注意读取XML坐标时像素坐标要除以图片的原始宽高而不是重新缩放后的宽高。我一度为了方便先把图片压缩到640x640再读取标注导致框的位置全部偏移训练出来的模型预测框基本都在图片左上角排查了半天才发现是归一化基准错了。3. YOLOv5s 模型训练关键细节3.1 训练环境准备训练阶段不一定要在Jetson Nano上跑我是在一台有NVIDIA显卡的PC上训练的模型训练完毕后再部署到Jetson上。PyTorch版本建议用1.10到2.0之间的稳定版本CUDA版本对应显卡驱动选择没有太苛刻的要求。YOLOv5仓库的克隆和依赖安装按照官方README来就行。特别提醒一句训练前要确认torch和torchvision版本是匹配的很多人直接pip install torch就完事结果导入torchvision的时候报错来回折腾半天。git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt如果显卡显存不足比如只有4GB显存就把训练批次调小、输入分辨率调低而不是直接换更大的batch。YOLOv5对batch size的敏感度没有想象中高小batch配稍多的epoch也能收敛到不错的精度。3.2 配置文件和训练命令训练前需要改两个文件数据集配置文件data.yaml和模型配置文件yolov5s.yaml。data.yaml内容如下train: /path/to/dataset/images/train val: /path/to/dataset/images/val test: /path/to/dataset/images/test nc: 8 # 类别数量 names: [tire, carton, wood, cone, gravel, plastic, metal, other]yolov5s.yaml里主要改nc为8其他参数保持默认。训练命令python train.py \ --data data.yaml \ --cfg models/yolov5s.yaml \ --weights yolov5s.pt \ --batch-size 16 \ --epochs 200 \ --img 640 \ --device 0每条命令我都解释一下weights指定预训练权重YOLOv5s.pt是在COCO上预训练过的迁移学习能让模型快速收敛img 640是训练输入分辨率我在实验中发现640比416在抛洒物检测上的mAP高约5个点虽然显存占用增加但值得epochs 200是因为小数据集加迁移学习150到200轮左右基本收敛再多了容易过拟合。3.3 关键训练参数与调优心得训练参数里最值得花心思的是这三个学习率、锚框、损失权重。YOLOv5默认使用cosine学习率调度初始学习率0.01。我之前贪快调成0.02训练直接发散loss曲线飞上天。后来实验下来0.006到0.01是安全区间新手不要轻易动。锚框anchor是很多教程忽略的点。YOLOv5在训练时会自动调用k-means重新计算anchor但如果你用的是预训练权重它默认的anchor是基于COCO数据集的80类目标统计出来的。对于抛洒物这种细长、扁平的物体建议在训练前手动跑一次anchor计算python train.py --data data.yaml --cfg models/yolov5s.yaml --weights yolov5s.pt --noautoanchor False让YOLOv5在训练开始前先执行自适应anchor计算。我实测不调整anchor时细长木板这类目标召回率只有50%左右调整后提升到76%。损失权重方面YOLOv5的box_loss和cls_loss默认权重是0.05和0.5。如果项目更关注“别漏检”可以把box_loss权重适当调低、cls_loss略微调高让模型更积极地把目标分到正确类别如果更关注“别误报”则反过来。我用默认配置效果已经不错建议默认跑通后再微调。另外推荐开启--cache属性把小图片缓存到内存中训练速度能提升好几倍python train.py ... --cache3.4 模型评估指标怎么看训练结束后看results.png和混淆矩阵。YOLOv5训练完成会自动生成results.png里面有训练loss、验证loss、mAP等曲线。我判断过拟合的方法是验证集mAP在某个epoch后开始下降或者波动增大但训练集mAP还在涨说明模型开始“背题”而不是“学规律”了这时取下降前的权重即可。混淆矩阵最重要。道路抛洒物场景里我重点关注的是“轮胎皮”和“纸箱”这两个类之间是否互相误判。它们在外观上确实有点像都是深色块状物如果混淆矩阵显示两者互相误判比例超过15%我就回过去检查数据标注看是不是有些纸箱被打上了轮胎皮标签。TensorBoard也可能用到YOLOv5支持用--project和--name参数指定输出目录训练结束后用tensorboard --logdir目录即可可视化。不过对我来说results.png基本够用TensorBoard更像锦上添花。4. Jetson Nano 端侧部署与加速4.1 部署环境与依赖安装Jetson Nano的推荐系统是JetPack 4.6它自带了CUDA 10.2、cuDNN 8.2、TensorRT 8.2这些组件的版本匹配关系很重要。我见过有人直接刷最新JetPack 5.x然后发现TensorRT版本和YOLOv5导出的ONNX算子不兼容反而多花时间。刷机步骤简单说三步用SD卡烧录JetPack镜像、开机完成初始配置、开启MaxN性能模式。# 开启MaxN模式 sudo nvpmodel -m 0 sudo jetson_clocksJetson Nano默认运行在5W低功耗模式CPU主频被限制实测跑YOLOv5s只有5到7 FPS。切换MaxN模式后功耗上限10W性能直接翻倍实测能到15到20 FPS。如果无人机供电充足一定要开MaxN。PyTorch安装是很多人的噩梦。Jetson Nano是aarch64架构不能直接pip install torch需要到NVIDIA官方论坛下载预编译的wheel包或者用NVIDIA提供的PyTorch容器镜像。我建议直接在Jetson上创建Python 3.6的虚拟环境然后安装对应用户版本的torch 1.11.0和torchvision 0.12.0。pip3 install torch-1.11.0-cp36-cp36m-linux_aarch64.whl pip3 install torchvision-0.12.0-cp36-cp36m-linux_aarch64.whl注意Jetson上安装torch主要是为了导出ONNX和做精度对比实际推理走TensorRT不依赖PyTorch所以版本选型以兼容性为先。4.2 ONNX导出与TensorRT加速YOLOv5自带的export.py可以直接导出ONNXpython export.py --weights best.pt --include onnx --img 640 --opset 11 --simplify导出后可以用Netron工具打开ONNX文件检查一下网络结构确认输入输出的维度符合预期。这一步能提前发现很多问题比如输入节点名不对、输出层被优化掉了这些问题到TensorRT阶段再排查会非常痛苦。转换成TensorRT引擎有两种方式一种是直接使用trtexec工具命令行转换另一种是写Python脚本用TensorRT API加载ONNX后构建engine。trtexec方式更简单适合快速验证。/usr/src/tensorrt/bin/trtexec \ --onnxbest.onnx \ --saveEnginebest_fp16.engine \ --fp16FP16是Jetson Nano上的最佳平衡点INT8虽然更快但需要校准数据集而且校准不好精度损失明显。我在项目里没有用INT8因为FP16已经能跑满30 FPS了且mAP掉了不到1个点INT8虽然能到40 FPS但部分小目标开始漏检性价比不高。TensorRT引擎构建时间在Jetson Nano上比较久慢的话要等两分钟这是正常的。建议在PC上构建好engine文件然后拷贝到Jetson上运行但要注意构建机器的TensorRT版本必须和Jetson一致否则序列化出来的engine无法加载。4.3 视频流接入与实时推理流程Jetson Nano上常见的视频接入方式有CSI摄像头、USB摄像头、RTSP网络流。无人机场景里RTSP流更常见通过图传接收模块把画面转发到本地端口Jetson用OpenCV读取。# 用OpenCV读取RTSP流 cap cv2.VideoCapture(rtsp://192.168.1.100:8554/stream)用GStreamer可以降低延迟尤其是在USB摄像头上直接cap.read()的CPU开销比较大。推荐在OpenCV后端设置GStreamer属性cap cv2.VideoCapture(rtspsrc locationrtsp://192.168.1.100:8554/stream latency0 ! decodebin ! videoconvert ! appsink, cv2.CAP_GSTREAMER)latency0是降低画面延迟的关键不设置的话默认缓冲会让画面延迟几百毫秒实飞时操作手感会很差。推理主循环的核心代码结构import cv2 import numpy as np import tensorrt as trt import pycuda.autoinit # 加载TensorRT engine并分配缓冲区的代码省略... # 这里只展示推理和显示的骨架 while True: ret, frame cap.read() if not ret: break # 抽帧策略每2帧处理1次 frame_id 1 if frame_id % 2 ! 0: continue # 预处理letterbox 归一化 input_blob, ratio, (dw, dh) letterbox(frame, new_shape(640, 640)) # 推理 outputs engine(input_blob) # 后处理坐标还原NMS boxes, scores, class_ids postprocess(outputs, frame.shape, ratio, (dw, dh)) # 绘制结果 draw_boxes(frame, boxes, scores, class_ids)这里的letterbox和postprocess代码可以直接复用YOLOv5仓库utils里的函数我建议单独写一个推理模块把TensorRT引擎、预处理、后处理封装成类方便在多个场景里调用。4.4 性能实测数据与功耗控制我实测的几种配置对比如下配置输入分辨率帧率FPS单帧耗时ms备注PyTorch直接推理640x6401.8555不可用TensorRT FP16640x6406.5154可用但偏低TensorRT FP16416x4161283高抛洒物场景可用TensorRT FP16 抽帧640x6402245实际体验流畅Jetson Nano的显存是4GBFP16推理时显存占用大约1.2GB剩余显存足够跑多个应用。但要注意开启MaxN模式后核心温度上升很快如果没有主动散热几分钟就会触发降频帧率从12掉到5。我的解决方案是给Jetson Nano加了个5V风扇温度控制在60度以内帧率稳定很多。功耗方面整个Jetson Nano加摄像头模组在MaxN模式下实测约8到12W。如果是大疆这类载重较大的无人机挂载这个问题不大如果是自制小四轴就需要权衡电池容量和飞行时间。我在实验机上用的是5200mAh 3S电池单独给Jetson供电能和飞控电源隔离避免电机启动瞬间拉低电压导致系统重启。5. 无人机集成与实时告警实现5.1 机载设备安装与减震Jetson Nano在无人机上的安装核心问题不是固定而是减震。无人机飞行时的高频震动会让Jetson的存储卡读写异常严重点直接死机。我的做法是用减震球把Jetson Nano固定在一个碳纤维底板上底板再通过四个减震柱连接到机架。摄像头单独用一个小支架固定尽量让镜头朝下同时避开螺旋桨视场。很多教程会忽略电磁干扰的问题。Jetson Nano的WiFi模块、GPS模块、图传模块最好分开一定距离避免相互干扰。我第一版把所有模块堆在一起结果GPS搜星很慢图传画面还经常花屏拆开重排之后问题消失。5.2 图传链路与检测结果回传检测画面回传地面站有两种做法一种是把OpenCV处理后的画面直接编码成H.264流推到地面站另一种是只回传检测结果数据和截图。我推荐第二种因为图传带宽有限推视频流会占用大量带宽且影响控制信号。我实测用WiFi的UDP协议传JPEG压缩后的检测结果图1080p的图片压缩到100KB左右每秒传1张毫无压力。检测结果如果要叠加显示在无人机遥控器的屏幕上可以用MAVLink的自定义消息通道不过这个接入比较麻烦。更简单的方式是地面站程序通过TCP接收JSON格式的检测结果自己叠加到地图或画面上。5.3 告警与防抖逻辑实现实时检测系统最容易被忽视的问题是“抖动误报”。无人机在飞行时画面不断变化同一个抛洒物可能这一帧被检测到、下一帧又没了。如果每一帧都触发告警地面站会收到几十条重复信息。我的防抖方案是“连续多帧确认空间去重”from collections import deque # 用一个队列记录最近N帧的检测结果 detection_history deque(maxlen10) def detect_filtered(new_detections, frame_id): detection_history.append(new_detections) stable_alerts [] # 对当前帧每个候选框 for detect in new_detections: # 查找在最近N帧中是否有同一位置的检测 count 0 for hist in detection_history: if any(similar_box(detect, h) for h in hist): count 1 # 连续出现至少3次才触发告警 if count 3: stable_alerts.append(detect) return stable_alerts坐标还原到GPS也比较关键我采用的做法是先获取无人机当前GPS坐标和相机正下方的中心点坐标再根据目标在画面中相对中心点的像素偏移和当前飞行高度用近似的比例关系估算目标GPS坐标。精度在没有云台姿态数据时大概在5到10米范围内但作为道路抛洒物的位置提示已经够用地面人员到场后可以沿路搜索。5.4 实飞测试中的工程取舍实飞和实验室差异最大的不是模型精度而是现场环境的不可控性。我遇到过几次坑一是飞行高度变化导致目标像素尺寸剧烈变化在80米高度标注训练的数据飞到120米时很多目标太小根本检不到。解决方法是训练时加入不同模拟高度的增强或者干脆设定固定的巡查高度。二是在强光下摄像头自动曝光导致路面过曝抛洒物被亮光吞掉。解决方法是切换摄像头为手动曝光模式并把曝光时间压低。抽帧策略在实飞时也要动态调整。巡航速度快时抽帧间隔要短悬停检查时可以降低采样频率来省电。这个逻辑也不复杂读一下飞控的速度信息设置几个档位即可。6. 常见问题与排查经验速查6.1 性能与稳定性问题**检测帧率低怎么办**先看是不是没有开MaxN模式和jetson_clocks这一步能翻一倍性能。再看CPU占用如果是OpenCV解码消耗过高切换到GStreamer管线。最后再考虑降低输入分辨率或增大抽帧间隔。**运行过程中突然卡死或自动重启是什么原因**大概率是供电不足或温度过高。用万用表测一下Jetson输入电压电机的瞬时压降会导致电压跌到4.8V以下。解决办法是加入防反接二极管和足够容量的电容或者采用独立供电。**系统存储空间不足怎么处理**JetPack系统本身占空间比较大训练过程中生成的日志和权重文件也会积累。建议把swap文件设大一点4GB左右同时定期清理未使用的容器和日志文件。6.2 检测精度问题**模型在自采视频上漏检小目标但在测试集上表现不错为什么**这种情况通常是训练数据里小目标太少测试集恰好避开了这些困难样本。解决办法是回到数据集增加小目标样本数量并使用切图策略让模型看到更多的“放大的小目标”。**误报特别多尤其是路边的树木护栏被识别成抛洒物怎么处理**我在项目里也遇到类似情况原因是护栏在俯拍视角下和木板很像。一个有效的做法是增加负样本把没有抛洒物但包含护栏、路标、树影的图片作为背景图片加入训练并设置背景类。YOLOv5训练时可以用--train background图片路径来加入负样本。**置信度阈值调多高合适**这个需要根据告警系统的容忍度来定。我巡航模式下把置信度阈值设置为0.35悬停复核时提升到0.6。追求低漏检率时阈值调低追求低误报率时阈值调高两者只能取一个平衡没有一步到位的阈值。6.3 部署环境问题速查表现象可能原因解决方式trtexec转换报“Network has dynamic or shape input”ONNX未固定输入尺寸导出时指定固定输入尺寸或设置--explicitBatchTensorRT engine加载报错引擎在别的机器上构建在目标设备上重新构建engineCV2无法打开RTSP流GStreamer插件缺失sudo apt install gstreamer1.0-plugins-good内存不足导致进程被杀swap过小或显存溢出增加swap并关闭无关进程letterbox后检测框错位后处理没有还原坐标检查坐标还原公式中的比例和填充值图像颜色异常GStreamer的像素格式不匹配在appsink前加videoconvert指定BGR输出部署环境的坑往往是“环境匹配”问题CPU架构、TensorRT版本、PyTorch版本、CUDA版本任何一个不匹配都会出现奇奇怪怪的报错。我的经验是先跑通一个最简单的分类模型比如torchvision里的resnet18确认整条推理链路没有问题再上YOLOv5的ONNX导入这样能快速定位是哪一层的问题而不是几件事混在一起排查。写到最后我想起一个实际测试里印象很深的细节第一次带着整套系统去室外跑的时候模型在电脑上测试精度很高一到无人机上就频繁漏检。排查了很久发现是无人机飞行时视角倾斜导致抛洒物形状变化剧烈而我训练数据里绝大多数是近乎垂直的俯拍照。后来我在训练集里加入了倾斜拍摄角度让模型看到更“躺”的抛洒物问题才真正解决。这件事给我最大的教训是流程跑通只是第一步真正要花时间的是让数据和真实部署环境尽可能一致。数据质量上投入的功夫会在最终效果中数倍地体现出来。这套思路也可以继续扩展比如在无人机上同时跑车道线检测、裂缝检测等只要遵循“数据贴近场景、模型轻量可靠、边缘端做好加速”这三条原则很多巡检类问题都能用类似的方案落地。