简介:面向无人机频射信号检测任务的数据集,适合目标检测、射频信号识别方向的开发者与研究人员使用。压缩包内共729个文件,包括364张jpg原始图片、364个txt格式的YOLOv5标注文件,以及1个yaml配置文件,图片与标注一一对应,压缩包大小约136.82MB,数据组织清晰。标注文件已对齐YOLOv5训练要求,yaml可快速配置类别与路径,大幅减少数据预处理环节;txt标注包含目标位置与类别信息,可直接用于模型训练与数据集划分。素材来自多个采集片段和不同飞行器型号,图片命名保留原始样本标识,便于追溯与筛选;基于该数据集的平均正确识别率约94.3%,可作为无人机频射信号检测模型训练、验证与效果对比的参考基准,内容预览中的文件名也体现样本来源标识,方便按场景筛选数据。目前已有451人学习下载,适合需要构建无人机频射信号检测模型的入门与进阶用户。
1. 无人机频射信号检测:为什么用一组RF图就能训练出94.3%的识别率
识别一架无人机,除了用摄像头看它、用雷达扫它、用麦克风听它,还有一条被低估的路线是抓它发射的射频信号。无人机与遥控器或图传模块之间的通信链路是它无法摆脱的物理特征,哪怕机身做了隐身涂装,只要链路还在工作,射频信号就会泄露出去。更关键的是,射频检测不需要可见光,夜间、大雾、强逆光都照常工作,设备成本也比雷达低一个量级。本标题提到的“无人机频射信号检测数据集”就是围绕这个思路准备的样本资源,364张原始图片配上平均94.3%的正确识别率,说明在这个样本量级上,基于图像的射频检测已经具备工程可用性。
这个数据集的特殊之处在于,它把一段射频信号变成了一张图片,然后用YOLOv5去完成目标检测。很多做物联网、安防、无线电监测的工程师看到“364张图”会下意识觉得数据太少,但射频信号图和自然图像的本质差异让这个数字变得合理。本文就沿着这条线拆开讲:射频信号是怎么变成YOLOv5能吃的图片的,标注格式怎么组织,训练时有哪些参数必须调,以及94.3%这个数字在实际验证里应该怎么核对。
2. 从频射信号到YOLOv5训练图:时频图的生成与数据集组织
2.1 为什么射频信号要转成图片而不是直接喂网络
YOLOv5的输入约定是普通图像张量,它的Backbone本身不具备处理一维I/Q波形的能力。常见做法是把一段窄带或宽带接收机采到的中频信号,通过短时傅里叶变换(STFT)或Choi-Williams分布转成时频图。横轴是时间,纵轴是频率,像素灰度或色阶代表该时刻该频率上的能量强度。这样一架无人机的遥控信号、图传信号、数传信号就会在图上形成一条或多条明显的亮带,形状像断续的“条纹”或“扫把”,和自然图像里的“目标”概念完全对得上。
这也是为什么YOLOv5能用在射频检测上:它检测的目标不再是“物体轮廓”,而是“时频图上的能量纹理”。一套20MHz采样率、每帧采100ms的数据,STFT后大约产生一张640乘640的灰度图,频率分辨率大概能到几十kHz量级,足够区分常见的2.4G跳频遥控和图传信号。
import numpy as np from scipy.signal import stft import cv2 # iq_data: 复数数组,长度对应一帧的采样点数 # fs: 采样率,例如 20e6 def iq_to_spectrogram(iq_data, fs=20e6, nperseg=256): f, t, Zxx = stft(iq_data, fs=fs, nperseg=nperseg, noverlap=nperseg//2) # 取幅值谱,再转成 0-255 灰度图 mag = np.abs(Zxx).astype(np.float32) mag_db = 20 * np.log10(mag + 1e-12) mag_norm = cv2.normalize(mag_db, None, 0, 255, cv2.NORM_MINMAX) img = mag_norm.astype(np.uint8) return cv2.resize(img, (640, 640))这段代码将复数I/Q数据变换成640乘640的单通道灰度图。nperseg=256是STFT窗口长度,直接影响时间分辨率和频率分辨率的平衡;窗口越小频率分辨率越差,但能捕捉瞬间跳频。对帧长较短的样本,我会先补零再变换,避免边缘效应把目标条纹截断。代码里的cv2.normalize用的是全局归一化,实际训练时建议替换成按per-frame的百分位归一化,否则个别强干扰信号会把整张图的对比度压掉。
2.2 364张原始图片的层次结构到底怎么摆
拿到这份数据集时,最值得先确认的其实是目录规范。YOLOv5对数据集的路径没做硬编码,但约定俗成的组织方式是images和labels两个并列目录,各自拆成train和val两个子集,如果你愿意也可以按8:1:1拆成train、val、test。364张原始图片很小,手挪就行,但不要用手工复制去分,写个脚本最稳。
datasets/DroneRF/ ├── images/ │ ├── train/ # 约255张 │ └── val/ # 约73张 ├── labels/ │ ├── train/ # 每张图对应同名txt │ └── val/ ├── drone.yaml └── classes.txt划分时先打乱再按比例切,固定随机种子,保证可分复现。常见写法是random.seed(42)之后做shuffle,再切片。注意labels里的文件必须和images里的图片同名,只是后缀从.jpg换成.txt,YOLOv5在训练期是靠同名匹配来找标签的,丢失一个就相当于这张图没了监督信号。
2.3 YOLOv5格式标注的转换和文件内容
很多人拿到RF数据集时,原始标注可能来自MATLAB或通用CSV,格式类似“起始时间、结束时间、起始频率、结束频率、类别”,跟YOLOv5的归一化中心点格式完全不是一回事。转换的原理是把时频图上的边界框映射到640乘640坐标系,再除以宽高做归一化。比如一条图传信号从t=20ms到t=80ms,对应到640像素宽就是横坐标从128到512。
# 原始标注: img_width, img_height, x1, y1, x2, y2, class_id # 转换为 YOLOv5 格式: class_id x_center y_center width height def convert_to_yolo(x1, y1, x2, y2, img_w, img_h): x_center = (x1 + x2) / 2 / img_w y_center = (y1 + y2) / 2 / img_h width = (x2 - x1) / img_w height = (y2 - y1) / img_h return f"{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}"转换后每行就是“类别编号 归一化x中心 归一化y中心 归一化宽 归一化高”。这里有一个高频踩坑点:原始CSV里的频率是MHz,图片纵轴是像素,转换前必须确认频率范围和图片像素的线性映射关系,很多raw格式是log频率刻度,直接线性转换出来的框会错位到离谱。转完后来回抽检几张图,把txt描到图上人工核对边界。
3. 无人机频射信号数据集的标注规范与校验手段
3.1 labelimg和CVAT在RF时频图上的取舍
标注工具选哪个,取决于你的使用场景是单人操作还是团队协作。labelimg的优点是轻量,单机装完就能标,切换正方形框不生效的问题在最新版已经修过,但处理大图序列时效率偏低。CVAT是网页版,支持半自动标注,团队多人并发标注时状态同步、冲突检测都做在服务端,适合需要把364张图快速扩成上千张的项目。
无人机频射信号数据集的标注有其特殊性:时频图上亮带边界往往不是清晰的四边框,而是由跳频点组成的断续条纹,按最小外接矩形标会把大量背景包进框里。我一般会在工具里先把时频图拉伸到高对比度再标,但核心依然是标“链路信号暴露的最紧包络”,让图传、遥控、数传各占一个框,而不是把整片频带全部框成一个大目标。
3.2 标签缺失、越界和空文件怎么批量校验
364张图的人工标注很难做到零失误,最稳妥的做法是在训练前写一个校验脚本,把三种典型垃圾直接筛出来:一是labels目录下存在无对应图片的txt;二是框的坐标越界,归一化值小于0或大于1;三是零字节的空txt,它会让YOLOv5认为这个训练样本没有目标,直接作为背景样本加入训练,如果背景样本比例失控,模型会偏向不预测任何目标。
import os def check_labels(img_dir, lbl_dir): img_names = set(os.path.splitext(f)[0] for f in os.listdir(img_dir)) for f in os.listdir(lbl_dir): stem = os.path.splitext(f)[0] if stem not in img_names: print(f"orphan label: {f}") continue path = os.path.join(lbl_dir, f) if os.path.getsize(path) == 0: print(f"empty label: {f}") continue for line in open(path): parts = line.strip().split() if len(parts) != 5: print(f"bad line: {f}: {line.strip()}")这段脚本在每次新增或修改标注后跑一遍,比肉眼抽查要省心得多。跑完之后用YOLOv5自带的可视化脚本把标注画回图上,按10%的比例随机抽查,重点看跳频信号的那几类框是否贴着能量条纹。
3.3 标注类别定义与不均衡问题
364张原始图片里,各类无人机的信号出现次数通常是不均衡的,遥控信号多、图传信号少,这在标注阶段就要有意识地记录类别频次,而不是等训练阶段才去发现。标注类别的粒度也值得提前想清楚:是把“大疆所有型号”归为一类“dji”,还是按遥控、图传、数传拆成三类,这直接影响后续的混淆矩阵怎么解读。按我处理这类RF数据集的经验,先按链路类型拆三类起步,训练到loss平缓后再决定要不要细分型号。
4. yolov5训练自己的数据集:配置文件、超参和训练命令
4.1 drone.yaml配置文件怎么写才不出错
YOLOv5读取数据集信息依赖一个yaml文件,里面定义的是训练集路径、验证集路径和类别名,这三项缺一就会在启动时直接报错。路径可以填绝对路径,也可以填相对于训练启动目录的路径,但最稳的是写绝对路径,项目目录被移动后不容易出问题。
# drone.yaml train: /data/datasets/DroneRF/images/train val: /data/datasets/DroneRF/images/val nc: 3 names: ['remote_control', 'video_transmission', 'data_link']nc必须和names的长度严格一致,多一个少一个都会在构建datasets时抛索引错误。类别顺序一旦定了,就不要在中途改,否则已生成的标签文件全部作废。这个yaml文件同时被train.py和val.py读取,评估模型时用错配置文件是初学者最常见的错误。
4.2 训练命令和yolov5超参数的选择
在364张图这种小数据集上,训练命令本身不需要太花哨,但要控制好几个参数。常见做法是先用yolov5s的预训练权重起步,imgsz设成640,batch设成16或32。数据量小的场景不要盲目把epochs拉高到300以上,配合早停机制反而更有效。
python train.py \ --data drone.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 120 \ --patience 20 \ --hyp hyp.scratch-low.yaml--weights选用yolov5s而不是yolov5m或yolov5l,是因为364张图不足以支撑大模型的参数量,模型越大过拟合来得越快。--patience 20的作用是当验证集mAP连续20轮没有提升时自动终止训练,省掉盲目等待的时间。--hyp指向低增强超参文件,小数据集上用默认的hyp.scratch-low.yaml比hyp.scratch-high.yaml稳定得多,强增强在小样本上经常把原本清晰的特征破坏成噪声。
4.3 必调超参数:mosaic、mixup和上下采样
hyp.scratch-low.yaml里的几个关键参数在RF小数据集上必须改,否则训练曲线会异常。mosaic的默认值是1.0,也就是百分百启用四图拼接,这对小目标检测很友好,但对时频图亮带目标,大幅裁剪拼接会切断信号条纹的连续性,建议降到0.3到0.5。mixup同理,0.1到0.2足够,太高会让时频能量背景混叠。
# hyp.scratch-low.yaml 中需要调整的关键项 mosaic: 0.4 mixup: 0.15 fliplr: 0.5 scale: 0.3fliplr在时频图上的含义是时间反转,对遥控和数传信号来说有一定合理性,但图传帧结构通常有时间先后依赖,水平翻转等于制造语义错误样本,我通常把它降到0.2甚至0。scale控制的是随机缩放范围,默认0.5在自然图像上没问题,但时频图上频率轴的压缩会让跳频特征糊掉,调到0.3以下再配合--img 640反复验证。
4.4 训练时的loss曲线和验证集指标对照
训练结束之后,runs/train/exp/目录下会生成results.png和混淆矩阵、F1曲线等图表。看曲线有几个门道:train/box_loss和val/box_loss的差距如果持续拉大,就是典型的过拟合信号,此时回看超参是否开大了mosaic或mixup;metrics/mAP_0.5在训练后期还在缓慢上升但precision和recall在摇摆,说明验证集本身标注噪声不小,优先去修标注而不是继续加epochs。
在验证集上达到94.3%的平均正确识别率通常指的是mAP@0.5,运行下面的命令可以看到逐类的P、R和mAP:
python val.py \ --data drone.yaml \ --weights runs/train/exp/weights/best.pt \ --img 640 \ --conf 0.25 \ --iou 0.45--conf是置信度阈值,默认0.25,线上推理时为了降低误报可以提到0.4。--iou是NMS的IoU阈值,时频图上两个目标紧挨着时,--iou降到0.35能避免相邻框被吞掉。
5. 换一种验证思路:从364张图片复现94.3%的可靠性检验
拿到数据集和YOLOv5训练环境的人,最该做的一步不是急着复现数字,而是先弄清楚94.3%这个“平均正确识别率”的口径。常见的评估指标有mAP@0.5、mAP@0.5:0.95、精确率、召回率四种,同一个模型算出来的数字差距可以达到10个百分点以上
。如果原始发布方没有注明指标口径,你训练出来的模型mAP@0.5是93%,而对方说94.3%,其实已经算复现成功了;但如果对方指的是单帧分类准确率,而你这边按目标检测来算,那两者很可能差5个百分点以上,所以一上来先对齐口径,别跟数字较劲。
在验证阶段有一个值得做的技巧叫“拆分窗口复测”:把一张时频图横向切分成左右两半,分别送进模型推理,再把两边的预测框合并回原图坐标。这个操作等价于用更高的时间分辨率去检测短时出现的跳频信号,可以减少目标在图边缘被截断导致的漏检。实现方式是先用cv2.imread读取原图,再对左右半区分别做推理。
img = cv2.imread("sample.jpg") h, w = img.shape[:2] left = img[:, :w//2] right = img[:, w//2:] # 两半分别经过 model.predict() 后 # 右侧框的 x_center 需要加上 w//2 偏移 def offset_boxes(boxes, offset_x): boxes[:, 0] += offset_x return boxes这个技巧对跳频明显、目标持续时间短的遥控信号样本很有效,代价是推理时间翻倍。生产环境如果对实时性要求不高,可以用它作为置信度校准手段,观察左右半图的预测框和整图推理结果是否一致,如果不一致,说明该样本本身存在标注模糊或信号混叠。
最后再提一个容易被忽略的细节:验证时别忘了检查数据集的类别顺序。网络热词里常提到“kitti标注转yolo”“mmrotate训练dota数据集”,这些转换过程都以类别编号为纽带,一旦顺序错位,检测结果会整体错位还不报错。用一套不依赖训练环境的小脚本,把原始标注的类别名和预测结果的类别名做一次全量对照,基本能把这个隐患摁死。94.3%可以在nice的测试条件下复现,但只有把指标口径、类别顺序、时频图预处理参数三者都钉住,这个数字才对你有真正的参考价值。
本文还有配套的精品资源,点击获取