
简介目标检测是计算机视觉中的核心技术其任务是在图像或视频中定位并识别多个物体。YOLO系列模型凭借单阶段检测的架构优势在保证精度的同时实现了实时推理其中YOLOv8通过C2f模块、Anchor-Free检测头和解耦头等设计进一步提升了小目标密集场景下的检测性能。在无人机交通监控这类应用中高空视角带来目标小、密度高、方向多变等挑战需要兼顾精度与速度的检测方案。同时将模型部署到边缘设备如RK3588利用NPU加速实现低延迟推理是满足实时监控需求的关键。基于YOLOv8构建的无人机交通监控系统覆盖数据处理、模型训练、ONNX/RKNN转换、边缘部署及地面站可视化展示可服务于车流统计、路口监控、违章记录等场景为智能交通提供了高性能、可落地的技术参考。 做无人机交通监控这个项目前后折腾了大概两个多月。从一开始用Faster R-CNN跑一帧图要几百毫秒到后来换成YOLOv8在嵌入式设备上也能跑到实时中间的坑踩了不少但也确实把整套系统的设计思路、数据流程、训练调参和部署方案完整打通了。这个项目包里的内容就是一个基于YOLOv8的无人机交通监控系统的完整实现包含数据集处理脚本、训练配置、模型权重、推理代码和地面站展示模块。如果你正准备做类似的航拍车辆检测、车流统计或者路口监控我的建议是先别急着改模型结构把整套数据链路和部署流程跑通再考虑优化精度。这篇文章把我整个项目的设计和实操过程详细写出来从数据标注到模型训练再到嵌入式部署都能直接参考。1. 系统整体设计与技术选型思路1.1 为什么是YOLOv8而不是其他目标检测框架交通监控这个场景有个很实际的需求无人机在高空视角下车辆目标小、密集、朝向各异而且一张图上可能有几百个目标。如果用两阶段检测器精度虽然高但推理速度完全跟不上。我以前用Faster R-CNN做类似项目在GTX 1660 Ti上推理一张1080P的航拍图单帧耗时基本在300ms以上只能做离线分析根本谈不上监控。后来换了YOLOv8之后同样的硬件条件下640x640输入推理速度能跑到30-40ms一帧加上DSADeepStream或者自研的追踪模块之后勉强能到实时。YOLOv8相比之前的YOLOv5核心改动集中在三个地方C2f模块替换了原来的C3模块通过梯度流的优化让特征提取更充分Anchor-Free的检测头设计省掉了大量候选框超参Decoupled Head把分类和回归分支拆开收敛速度和精度都提升了不少。这些改动对交通监控这种小目标密集场景是实打实的收益。当然YOLOv8也不是没有缺点。它对小目标的支持依然算不上完美输入分辨率调到1280之后精度会明显好一些但推理速度也会跟着下掉。我在项目里是这么取舍的无人机高空广角画面用1280输入做检测低空巡查用640输入保证实时。这个策略在后面会详细讲。1.2 系统架构从无人机采集到地面站显示整个交通监控系统不是单跑一个模型就完事它是一个完整的链路。我搭的结构大概分成四层数据采集层、边缘推理层、传输层和地面站应用层。数据采集层是无人机本身我用的是一款支持RTSP推流的行业机搭载的摄像头是1/1.8英寸CMOS视频输出1080P30。采集到的画面通过无人机机载的算力单元这里用的是RK3588开发板直接做YOLOv8推理检测结果叠加到视频帧上。这里我刻意没有把原始视频全部传回地面因为带宽有限1080P的H.264码流大约需要4-8Mbps而实测图传链路经常波动到2Mbps以下。所以边缘端先做检测再把带检测框的低码率视频推流到地面站这样即使网络差也能保证监控不中断。地面站应用层跑着一个基于PySide6写的小工具主要功能是接收RTSP流、解码显示、画目标框同时统计车流量和拥堵指数。这里用到的追踪算法是ByteTrack处理遮挡和漏检的情况比单纯用IoU匹配要稳得多。1.3 开发环境与硬件选型把开发环境列出来供大家参考宿主机Windows 11 RTX 4070用于训练训练框架PyTorch 2.0 CUDA 11.8 cuDNN 8.9推理测试机GTX 1660 Ti用于验证大体量模型能否跑得动边缘端RK3588开发板NPU算力6TOPS用于最终部署标注工具LabelImg和Roboflow前者本地免费后者界面更友好且自带增强功能这里有个容易踩的坑PyTorch版本不是越新越好。YOLOv8官方代码在PyTorch 1.8到2.1都能跑但如果你要导ONNX或者TensorRT2.0以上版本兼容性更好。我之前试过用PyTorch 2.1.2配YOLOv8导出的ONNX在RK3588的RKNN-Toolkit2里转换时提示算子不支持后来换回2.0.1才顺利通过。2. 数据准备公开数据集与自建数据集结合2.1 公开交通数据集怎么选、怎么下载训练交通监控模型最忌讳的是只用自己拍的那几百张图泛化能力会非常差。我的做法是先把公开数据集跑通再补自己的数据微调。几个比较常用的公开数据集我列在下面数据集名称目标类别特点适用场景VisDrone车辆、行人、自行车等无人机视角目标小、密集最适合做航拍交通监控UA-DETRAC车辆轿/公交/货车等地面视角场景多样补充地面视角样本CCPD2020车牌安徽合肥真实场景采集用于车牌识别辅助功能BDD100K车辆、行人、交通标志等公开道路场景规模大丰富负样本类别VisDrone是首选它完全就是无人机视角包含2880张图片和约26万个标注框。不过要注意VisDrone的标注格式是它的自定义格式需要转换成YOLO格式才能用。我在项目包里放了一个转换脚本输入VisDrone的标注文件输出txt格式的YOLO标签核心逻辑就是把坐标从(x1, y1, x2, y2)归一化到(center_x, center_y, width, height)并除以图像的宽高。下载方式很简单直接去VisDrone官方页面下载即可训练集和验证集分开下载一共约1.5GB。如果你的网速不理想可以分批下载但一定确保解压之后目录结构和标注文件一一对应。2.2 数据标注实操YOLO格式与工具自己标注数据是逃不掉的一步尤其是要识别特殊场景比如本地的公交车样式、三轮车等。YOLO格式的标注文件是一个txt每行代表一个目标格式是“类别id 中心点x 中心点y 框宽 框高”注意这四个坐标值都是归一化到0-1之间的。我用LabelImg标注的时候有个很实用的习惯先设置好类别文件classes.txt再打开自动保存模式Auto Save mode这样每标完一张图就自动保存不用担心忘记保存导致重复劳动。标注速度上一个人标1000张图大约需要6-8小时可以先标500张跑通训练流程后续再迭代补充。Roboflow也是一个值得推荐的方案它除了标注界面好用还自带数据集管理和预处理功能。不过Roboflow的免费版有图片数量限制而且云端上传下载需要稳定的网络不像本地工具那么稳定。我最终采用的是本地LabelImg标注Roboflow只用来做在线增强和格式转换。2.3 数据增强与类别平衡航拍数据有个明显问题正午和傍晚的光线差异巨大车辆阴影长短完全不同不同高度下车辆尺寸差异也大。针对这些情况我在训练配置里打开了几项关键增强Mosaic把4张图拼成一张训练增强模型对不同尺度目标的适应能力MixUp两张图按比例混合让模型更鲁棒HSV扰动hue、saturation、value分别偏移模拟不同光线环境随机翻转和旋转对航拍来说随机90度旋转特别有效因为车在画面里是各个方向都有类别平衡方面VisDrone数据里“car”类占据了绝大多数而“truck”“bus”相对稀少。如果不做任何处理模型对少数类的召回率会很低。我用了一个简单有效的策略对少数类样本做重复采样也就是在训练时让每一轮每个类别的样本数量尽量接近。YOLOv8官方代码里没有直接支持这个功能我是在自定义数据加载器里做了一个权重采样效果比直接调loss权重更直观。3. 模型训练从环境搭建到收敛调优3.1 GTX 1660 Ti上的环境配置实测很多人跑来问GTX 1660 Ti能不能跑YOLOv8我的答案是能但显存容量决定了你只能用较小的batch size和分辨率。1660 Ti是6GB显存在默认的640x640分辨率下YOLOv8s模型的batch size设为8勉强能跑但训练速度感人大概需要300ms/iteration。为了在它上面能完成训练我做了几项关键设置开启混合精度训练amp显存占用降低约30%调整workers0避免Windows下Dataloader多进程导致的显存碎片使用YOLOv8s而不是YOLOv8m或更大的模型实测下来GTX 1660 Ti上YOLOv8s训练VisDrone子集约8000张图需要大约18小时每个epoch总共训练100个epoch差不多需要两三天。这个速度可以接受但如果你有更好的显卡还是建议用RTX 3060以上体验会好很多。3.2 训练参数解析与最小数据集实验YOLOv8的命令行训练格式是yolo detect train datavisdrone.yaml modelyolov8s.pt epochs100 imgsz640 batch8 device0核心参数我逐个拆开说datadata yaml文件里面写train/val路径和类别信息注意路径建议写绝对路径写相对路径容易被CWD坑到model预训练权重的路径这里用的yolov8s.pt是COCO预训练的迁移学习能显著加快收敛epochs训练轮数我最终用的是150因为mAP在100轮之后还在缓慢爬升imgsz输入分辨率640是速度和精度的平衡点1280能提升小目标检测但显存需求翻倍batch根据显存调整6GB显存建议不超过8device指定GPU编号用CPU训练直接写cpu如果你从来没有训练过自己的数据集我的强烈建议是先用几十张图跑一个最小数据集验证整个流程通了没有。我之前遇到过数据yaml里类别写错导致训练出来的模型输出类别和实际完全不匹配的情况这种错误通过最小数据集很快就能发现。训练过程中还可以加一个参数yolo detect train ... projectrun nameexp1这样每次训练的结果都会存在run/exp1目录下包含weights、日志和混淆矩阵方便后续对比实验。3.3 损失函数曲线与训练状态判断YOLOv8训练过程中会在终端输出loss信息同时训练结束后在run/exp1目录下生成results.csv文件里面记录了每个epoch的box_loss、cls_loss、dfl_loss以及精度指标。判断模型是否收敛我一般看三个指标box_loss回归框损失应该在训练初期快速下降然后趋于平缓cls_loss分类损失如果这个值出现震荡说明学习率可能偏大mAP50和mAP50-95最终判断标准mAP50代表简单场景下的精度mAP50-95则更严格两者差距过大的话说明模型过拟合比较严重画损失函数曲线可以用Ultralytics自带的plot方法也可以直接把results.csv读出来用matplotlib画。我习惯后者因为方便自定义。一个简单的脚本import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(run/exp1/results.csv) plt.plot(df[epoch], df[train/box_loss], labelbox_loss) plt.plot(df[epoch], df[val/box_loss], labelval_box_loss) plt.legend() plt.show()这里有个经验如果val_loss在训练后期持续升高而train_loss还在下降那一定是过拟合了。解决方法是降低epoch数、增加数据增强强度、或者引入早停机制。YOLOv8本身就支持patience参数设成30意味着30轮内mAP没有提升就提前停止训练。3.4 网络结构与注意力机制改进尝试跑通基线模型之后我又围绕YOLOv8的网络结构做了几轮改进实验。网上讨论最多的几个改进点我都试过在C2f模块里融合ECA注意力机制通过对通道维度的全局平均池化和一维卷积生成注意力权重计算开销很小但对小目标的提升有约1-2%的mAP收益把主干网络尾部的下采样方式从普通的Conv改成ADown减少信息丢失适合低分辨率输入场景直接在数据层面做“切片拼接”模拟YOLOv7的SPPCSPC结构对多尺度目标有作用这些改进在学术上也许有争议但在实际项目中我建议你做实验前先建立一个基线然后一次只改一个点用相同的参数跑两轮对比。我之前就是同时改了三个结构结果模型不收敛排查了很久才发现是某个改进模块的初始化和原网络冲突。从网络结构上看YOLOv8的架构我简单拆解成三块BackboneCSPDarknet、NeckPAN-FPN和HeadDecoupled Head。改进点通常落在Backbone的C2f模块或者Neck的融合方式上。ECA注意力嵌入C2f后的结构可以理解为在残差分支中加入了一个轻量通道注意力计算量增加不到5%但对小目标的特征表达确实有帮助。4. 落地部署从PyTorch到嵌入式设备4.1 模型导出ONNX、TensorRT与量化训练好的模型不能直接拿到嵌入式设备上跑需要经历一个模型转换的流程。我最常用的路线是PyTorch权重导出ONNXONNX转换为RKNN格式针对RK3588在NPU上推理导出ONNX的命令是yolo export modelruns/detect/train/weights/best.pt formatonnx opset12 simplifyTrue这里有几个注意点opset参数建议12或13老版本RKNN-Toolkit对更高版本的opset支持不完整simplifyTrue会通过onnx-simplifier移除冗余算子减少转换时的报错概率导出前确认输入尺寸是固定的动态尺寸在RKNN转换时会麻烦很多导出后可以用onnxruntime本地验证一下推理结果确保导出的模型没有精度变化import onnxruntime as ort session ort.InferenceSession(best.onnx) # 推理验证...4.2 RK3588部署NPU推理实践RK3588是瑞芯微的旗舰SoC自带的NPU有6TOPS算力跑YOLOv8s量化模型能到30-40FPS完全够用。部署流程大致是# 安装RKNN-Toolkit2 pip install rknn-toolkit2 # 编写Python脚本转换模型 from rknn.api import RKNN rknn RKNN() rknn.config(mean_values[[0,0,0]], std_values[[255,255,255]], target_platformrk3588) rknn.load_onnx(modelbest.onnx) rknn.build(do_quantizationTrue, datasetdataset.txt) rknn.export_rknn(best.rknn)量化这一步特别关键。RKNN默认使用int8量化如果直接转换精度可能会掉3-5%甚至更多。解决方法是准备一个具有代表性的量化校准数据集也就是dataset.txt里指定的图片列表图片要尽量覆盖各种场景。我之前偷懒只放了10张图片结果模型在夜间场景的检测效果一塌糊涂后来换成500张白天夜晚混合的图像精度才恢复正常。在RK3588上推理我写了一个简单的C接口通过RKNN的C API加载模型并执行推理效果比Python接口稳定很多。这里踩过一个大坑rknn.config里的mean_values和std_values必须和训练时保持一致。YOLOv8训练时默认用0-1归一化所以推理时mean_values设0std_values设255。这个搞错了检测结果会完全乱掉。4.3 无人机端推理性能与策略把模型部署到无人机载机之后我发现推理速度虽然达标了但整体系统的吞吐量还有问题。原因是NPU推理和视频解码是串行的一帧解码、推理、编码、推流全链路耗时超过了100ms达不到流畅度要求。解决办法是把解码和推理放到两个线程里用队列做缓冲。解码线程不断读取RTSP流并放入队列推理线程从队列取帧执行检测。这样即使偶发推理超时也不会阻塞视频采集。实测优化后全链路延迟稳定在80ms左右1080P画面下能达到20FPS以上的流畅度。另外针对不同飞行高度我做了两套推理策略高度在50米以上用640x640分辨率推理优先保证实时性高度在30米以下用1280x1280分辨率推理因为此时车辆在画面中占的像素较多检测精度更重要这里的切换逻辑很简单飞控通过MAVLink协议把高度信息传给机载电脑机载电脑根据高度动态设置推理分辨率。实测在50米高度用1280分辨率时每帧推理时间从30ms暴增到110ms但检测效果明显好于640权衡后还是保留了这个策略。4.4 地面站监控与告警模块地面站软件的核心需求是实时显示车辆位置信息和统计车流。我的方案是地面站接收机载推流RTSP用OpenCV的VideoCapture解码YOLOv8的检测结果通过JSON格式随视频流一同通过WebSocket传到地面站地面站解析JSON把目标框画在视频帧上同时维护每条车道的车流量计数器车流量统计功能我用的是“虚拟线圈”方案在视频画面里画几条水平线作为虚拟检测线当车辆检测框的中心点跨过这条线时对应车道的计数加一。这个方法简单可靠不像追踪算法那样需要维护大量ID状态对交通流量统计来说足够了。拥堵指数则是基于单位时间内通过检测线的车辆数来计算的。设定一个阈值比如5秒内通过车辆数低于3辆就认为是拥堵。这个判断逻辑很朴素但在实际运行中效果非常直观。你可以在项目包的ground_station/代码里看到完整实现稍微改改阈值就能适配不同路段的场景。5. 常见问题与排查技巧实录5.1 训练报错和路径问题速查表我在整个项目过程中整理了可能遇到的最常见的几类问题下面这个表可以直接对照排查现象可能原因解决办法FileNotFoundError: data.yamldata yaml路径写的是相对路径改为绝对路径CUDA out of memorybatch size或imgsz过大调低batch到4-8或降低imgsz到480AssertionError: class numbers not match数据标注的类别数和yaml中配置不一致检查classes.txt和data.yaml中nc参数训练loss一直不下降学习率过高或者数据标注有大量错误降低学习率到0.001检查标注样本导出onnx时算子不支持PyTorch版本过高换用PyTorch 2.0.1RKNN转换后检测结果全乱mean/std设置错误恢复为0-255归一化检测框框偏小或偏大标注时框选不准确重新检查并修正标注5.2 显存不足与OOM的应对措施GTX 1660 Ti上训练时最容易遇到的就是显存溢出。除了降低batch size还有一个技巧是使用梯度累积。YOLOv8命令行没有直接暴露梯度累积参数但你可以修改train.py源码将optimizer的zero_grad频率降低。我之前用accumulate4配合batch4等效于batch16的训练效果在6GB显存上跑通了YOLOv8s的1280分辨率训练。如果你的显卡更弱比如4GB显存建议放弃YOLOv8s直接使用YOLOv8n或者用官方模型做蒸馏用大模型指导小模型训练效果比直接训练小模型要好很多。5.3 模型不收敛的几个隐性原因排除掉数据问题后模型不收敛通常和这几个因素有关学习率设置过高YOLOv8默认学习率是0.01配合余弦退火但小数据集建议降到0.001类别不平衡极端背景占了绝大部分面积这时需要降低背景loss权重或者增加正样本惩罚数据增强太强Mosaic和MixUp在数据量少的场景下容易让模型“学不进去”预训练权重载入不完整换了backbone后不要加载原版的pretrained权重而是用COCO训练好的backbone权重我排查不收敛问题的方法是先关掉所有数据增强用一小部分数据训练10个epoch如果loss正常下降说明问题出在增强策略上如果loss仍然不降再检查学习率和模型结构。5.4 嵌入式部署经常被忽略的细节RK3588部署时还有几个小问题容易被忽略内存带宽NPU推理时DDR带宽会被占满和视频编码模块共享带宽可能导致系统卡顿需要在系统层配置好ISP和NPU的优先级温度降频机载设备在户外暴晒后SoC温度升高会触发降频推理速度直线下降。我加了一个简单的散热风扇和温度监控脚本超过70度就自动降低分辨率日志文件无限增长长时间运行时日志文件会把存储空间占满导致进程崩溃务必配置logrotate轮转日志还有一个比较隐形的坑是模型文件命名RKNN模型文件后缀如果包含中文字符部分版本的RKNN C API可能会加载失败建议全部用英文小写命名。5.5 数据标注质量对模型的直接影响标注质量这个问题值得单独拿出来说。我做了一套标注质量校验脚本原理很简单统计每个标注框的宽度、高度和长宽比如果长宽比超过某个阈值比如10:1大概率是标注时误操作拉出了长条框需要人工复查。另外我在标注规范里明确规定遮挡超过70%的车辆可以不标遮挡在30%-70%之间的要标但需要在备注里标记“遮挡”后期训练时可以针对性增加遮挡增强。这样既能保证训练数据的标注一致性又能在后续调优时知道模型在遮挡场景下的弱项在哪。如果你有精力建议对训练集和验证集做一次分布检查确保验证集中各个类别的占比和训练集大致一致。我之前因为验证集里bus类特别少导致训练出来的模型评估时bus的mAP虚高换了一版分布均衡的验证集后结果直接掉了6个点这才是真实水平。6. 项目目录结构与扩展思路6.1 压缩包里的目录一览项目包根目录结构大致如下├── data/ │ ├── visdrone2yolo.py # VisDrone格式转YOLO格式脚本 │ ├── visdrone.yaml # 数据集配置文件 │ ├── enhance.py # 数据增强脚本 │ └── datasets/ # 原始数据集存放目录 ├── models/ │ ├── yolov8s.pt # 初始预训练权重 │ └── best.pt # 训练好的最优权重 ├── scripts/ │ ├── train.sh # 训练启动脚本 │ ├── export_onnx.sh # 导出ONNX脚本 │ └── export_rknn.py # RKNN转换脚本 ├── deploy/ │ ├── rk3588/ # RK3588部署代码C │ └── ground_station/ # 地面站PySide6程序 ├── docs/ │ └── 系统说明文档.pdf └── README.md整个项目包的核心是模型权重、数据转换脚本和部署代码三部分。拿到包之后第一件事是跑通data/visdrone2yolo.py把VisDrone数据集转成YOLO格式然后按README里的步骤依次训练和部署。如果只想先看看效果直接用models/best.pt加一张航拍图跑一下推理yolo detect predict modelmodels/best.pt sourcetest.jpg6.2 后续还能怎么扩展这个系统目前做了车辆检测和车流统计往外扩展的方向其实很多。比如加一个车牌识别模块在检测到车辆后通过裁剪车牌区域送入LPRNet识别就能实现违章车辆的自动记录再加一个车辆颜色和车型分类就能做更细颗粒度的交通流分析。另外一个很实用的扩展是融合多架无人机的数据。目前的系统是单机单路视频如果能让无人机之间通过MAVLink互相共享位置和检测结果地面站就能拼接出更大范围的交通态势图。这个方向技术上可行难点主要是多路视频的时间同步和重叠区域的目标去重。我个人的建议是先花时间把单机系统的稳定性和检测精度打磨好再去扩展多机协同。因为多机场景下网络抖动、时间同步这些问题会指数级放大如果基础系统不够稳扩展后排查问题的难度会很痛苦。这个项目做到最后我最大的体会是目标检测模型本身上下限都重要但真正决定系统好不好用的其实是数据质量和工程落地的细节。YOLOv8的能力足够强把数据准备和部署链路做扎实比花大量时间改网络结构带来的收益更大。这也是我在项目包里尽量把数据脚本、训练配置和部署代码都写清楚的原因。后续如果你在复现过程中遇到问题可以重点检查数据集格式转换和RKNN量化这两个环节——这两处是我实际跑下来最容易出问题的位置也是影响最终效果最明显的两块。本文还有配套的精品资源点击获取