☰
RM雷达站数据集与开源源码:从目标检测到YOLO部署的实战指南
2026/10/6 4:48:42 网站建设 项目流程

简介:面向RoboMaster参赛队伍、机器人感知研究者与无人车/无人机开发者,这是一份RM雷达站数据集与高校开源项目的快速索引包,可将散落的训练数据和算法源码集中收录,便于按需查阅。包体精简,共3个文件:核心HTML页面负责总览与跳转,inscode文件用于在线运行或环境配置,gitignore辅助版本管理,整体仅6KB,适合作资料清单长期收藏。目前已有226人学习下载,虽热度不高,但内容指向明确,适合已确定方向后直接使用。价值在于省去逐篇检索的时间:读者可对照大疆官方数据集、Damon2019/RM-DATASET、华农数据集等不同来源,避开视角单一、噪声过多等问题;也能从上海交大、沈阳航空航天大学、中国石油大学(华东)、华中科技大学等开源的雷达站程序中,获取YOLOv5剪枝、高性能推理加速等工程手段。2024赛季厦门理工与辽宁科技的新方案还展示了如何利用规则压缩成本、提升效果,对自研雷达站具有现实参考意义。

1. RM雷达站数据集与开源源码:先想清楚你要的是哪一层“开源”

“雷达站”这词在 RoboMaster 圈子里指的不是作战单位,而是那张“全场视野”的视觉入口:摄像头架在高处,逐帧抓取对方装甲板的位置,把角度和距离喂给控制器,让哨兵或者前哨塔知道“往哪打”。真正动手过的都知道,这套系统只有两样硬通货:一个贴现场采集、清洗、标注出来的数据集,一份能跑通“图像→装甲板坐标→角度/距离”的开源源码。数据集决定精度上限,源码决定你从调参到上车的路径长短。这篇笔记不堆概念,直接把数据集怎么做、开源代码怎么选、哪些参数必须调,以及几个我踩过的具体坑一次讲清楚,适合刚接手雷达站视觉、准备跟队打比赛或复现开源方案的嵌入式/视觉方向同学。

2. 雷达站到底检测什么:任务拆解与数据集的真实构成

在动手做数据集之前,先把雷达站的检测目标拆清楚。不拆清楚就去标框,后面九成要重来。

2.1 灯条/装甲板检测与通用目标检测的差异

雷达站挂在固定支点上,视野里出现的是敌方战车的装甲板,而装甲板在画面里通常是一个带自发光灯条的矩形,可能带数字。这和 COCO 那种通用目标检测至少有四个差异。

第一,目标小。装甲板在长焦或大视场下可能只有几十个像素宽,在 YOLO 默认的 640 输入分辨率下,小目标占比很高,需要你用高分辨率输入或者切 ROI 再放大。第二,自发光。灯条是 LED 发光体,自动曝光会把它糊成一团白光,灯条边界和装甲板表面会失去全部细节。第三,多姿态且带旋转。车辆在赛场上不会老老实实正对着你,装甲板经常呈现明显的旋转矩形,轴对齐框会框进大量背景。第四,目标类别内部强相似。如果要做数字识别,类别是 1~5,低分辨率下互相混淆是常态。

这说明一个现实:雷达站数据集不应该套用“框住目标”的思路。正确做法是同时考虑灯条边界、装甲板四角、数字区域、曝光和模糊程度。常见开源方案里有两种路线:只标装甲板单类,或者加上灯条和数字多类。我后面会画出一条相对稳妥的落地路线。

2.2 数据集的最小构成:图像、标签、相机参数

数据集的最小实体是“图像 + 标签 + 相机信息”。三者缺一个,后期都要回头补,甚至重新训练。

图像:来自训练场或比赛现场的原始帧,应当覆盖不同距离、不同角度、不同光照条件。这里不要只捡清晰漂亮的帧,越是曝光过曝、灯条成团、有强反光的帧,越是有价值。因为推理时刻遇见的就是这些坏帧。

标签:至少包含装甲板类别;如果需要数字辅助,另外加数字类别。我建议在数据集里同时保留两个独立类别:armor 和 light。理由在避坑章节会展开——用灯条中心做精定位,比用装甲板框中心稳定得多。

相机信息:内参矩阵、畸变系数、实际分辨率。这一组数据常被忽略,但它是用 PnP 测算角度/距离的前提。没有标定过的内参,后面所有角度换算都会带上系统性偏差,跑得再好也只是“看着准”。

同时,要把负样本及时录入数据集:没有装甲板的空场、带高强度反光的围挡、车体上的非装甲板灯条、远距离下烧糊的灯带。负样本数量不必非常多,但它能让模型在“没有目标时别乱输出”,对雷达站这种要求低误报的场景极其重要。

注意:采集时多录几场比赛,不要只录一段。雷达站位置固定,但对方车辆在场地里的出现角度、速度、灯光状态都不一样。多场视频混合抽帧,比单场抽帧拥有更好的场景覆盖。

2.3 数据量级与类别平衡:几千张起步,按场景分层而不是硬堆

常见做法是先从现场视频抽帧标出几千张正样本,再逐步增加负样本和难例,直到验证集上对真实对抗环境的误检率显著下降。具体数字看你们队伍能跑多少场真车对抗,我的经验是可以按三层分配:

  • 基础正样本:覆盖 3~15 米距离、0~45 度偏航角,装甲板清晰可见,约 70%。
  • 难例正样本:灯光直射、运动模糊、半遮挡、装甲板部分露出,约 20%。
  • 负样本:背景、假灯条、车牌、反光体、远处模糊目标,约 10%。

注意类别平衡不要只看数量,而看单个类别在实际推理中的“先验概率”。雷达站场景里没有树,但有 LED 屏幕、护栏反光,所以负样本要按照实际场地特征去采。

当数据集垒到一定程度后,判断质量的标准不是不断上涨的 mAP,而是验证集上的混淆矩阵。打开混淆矩阵,看“装甲板”与“背景”之间的假阳率,如果假阳率偏高,就要刻意加大负样本的比例。盲增数量对 mAP 的提升,远不如按困难场景补漏,这是我拿真金白银的比赛时间换来的效率差。

2.4 雷达站的“负样本经济”:误检率优先级高于检出率

雷达站不是离线识别系统,它输出的角度会直接驱动云台。一次误检可能导致一发打击浪费,甚至提前暴露位置。所以数据集的优化目标应该写成“在保持低误检率的前提下提升可检出距离”,而不是传统意义上的追求最高 recall。

做法上,我给负样本设计更多的随机亮度、随机模糊和时间噪声,模拟灯条在不同曝光下的样子;同时把正样本分出一个专门的 hard 集,让模型在这些困难样本上反复对抗。这样训练出来的模型在场上会表现得“保守但可靠”。很多新手队伍只看召回率,结果模型在训练集里什么都找得到,到真实对抗里乱七八糟的误检把控制器都喂饱了,方向从一开始就反了。

3. 自建 RM 雷达站数据集:从原始帧到 YOLO/MMRotate 可训练格式

把目标拆清楚之后,这一步要解决三个问题:帧怎么抽、框怎么标、格式怎么转。

3.1 采集与抽帧:用 ffmpeg 按时间抽帧,再用拉普拉斯方差过滤模糊

雷达站一般不直接录 rosbag,而是录原始画面或者 RTSP 视频流。抽帧有个常见误区:每秒都抽,导致相邻帧高度相关,训练集真实的多样性极低。我一般用 fps=5 左右抽帧,也就是每秒保留 5 张,后面再人工筛一遍清晰度。

ffmpeg -i radar_record.mp4 -vf "fps=5" -q:v 2 frames/%04d.jpg

这段命令把 radar_record.mp4 按每秒 5 帧抽出 JPEG。-q:v 2 是 JPEG 质量,值越低质量越高,2 表示高质量。文件大一点没关系,别默认用 5 或 6,否则标注时边缘细节不够,装甲板边界会发虚。

抽帧之后,运动模糊严重的帧要过滤。我一般用 OpenCV 的拉普拉斯方差做批量过滤:

import cv2 import glob def is_sharp(img_path, threshold=80): head = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) lap_var = cv2.Laplacian(head, cv2.CV_64F).var() return lap_var > threshold, lap_var blur_paths = [] for path in sorted(glob.glob("frames/*.jpg")): ok, var = is_sharp(path, threshold=80) if not ok: blur_paths.append(path) print(f"blur: {path} var={var:.1f}") # 把模糊帧移到一个单独目录,先保留做二次筛选,别直接删除

threshold=80 是经验值,实际看画面纹理丰富程度:如果画面大部分是平坦地面,阈值要适当下调;如果围挡、网格多,阈值可以上调。更稳的做法是先对一批帧人工分成“清晰/模糊”,再找一个能让分类大多数正确的阈值。这个脚本的价值在于把几千张帧快速压到几百张,省下的时间远比写脚本的时间多。

3.2 标注规范:灯条、装甲板、数字拆开标,还是合并标?

这里有两个流派。

流派一只标“装甲板”一个类别,用轴对齐框或旋转框框住整个装甲板。优点是标注快、训练简单;缺点是框的中心容易被背景带偏,尤其装甲板斜着出现在画面时。

流派二标“灯条”和“装甲板”两类,装甲板框框住两块灯条加中间区域,灯条框额外标出灯条本身。这类在开源资料里更常见,因为有了灯条的两个中心点,后面做 PnP 匹配时直接拿“两根灯条中心”作为关键点,比拿整个装甲板框的中心稳定得多。

我建议:至少标出“装甲板”和“灯条”两类;如果做数字识别,再单独加“数字”类。用 LabelImg 标注轴对齐框即可。遇到明显旋转的装甲板时,先按旋转矩形的大致包围盒标,推理阶段再做旋转后处理。如果你想把旋转检测彻底做扎实,可以转成旋转框训练,参考 MMRotate 路线,数据格式会偏向 DOTA 那样的旋转框标注,MMRotate 训练 DOTA 数据集的流程在开源社区里已经足够成熟,把类别和路径替换成自己的装甲板数据就能用。

3.3 格式转换:VOC XML 转 YOLO TXT,再产出 train/val 划分

LabelImg 默认输出 VOC 格式 XML,而 YOLOv8 需要 txt 标签。下面这段脚本能一次处理一个标注目录:

import os import xml.etree.ElementTree as ET CLASSES = ["armor", "light", "digit"] # 顺序必须与训练时的 yaml 一致 def voc_to_yolo(xml_file, out_dir): tree = ET.parse(xml_file) root = tree.getroot() w = int(root.find("size/width").text) h = int(root.find("size/height").text) lines = [] for obj in root.iter("object"): cls_name = obj.find("name").text if cls_name not in CLASSES: continue cls_id = CLASSES.index(cls_name) bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # 越界裁剪,避免标注框超出图像边界 xmin, xmax = max(0, xmin), min(w, xmax) ymin, ymax = max(0, ymin), min(h, ymax) x_center = (xmin + xmax) / 2 / w y_center = (ymin + ymax) / 2 / h box_w = (xmax - xmin) / w box_h = (ymax - ymin) / h lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}") out_path = os.path.join(out_dir, os.path.basename(xml_file).replace(".xml", ".txt")) with open(out_path, "w") as f: f.write("\n".join(lines) + "\n") os.makedirs("labels_yolo", exist_ok=True) for file in os.listdir("annotations"): if file.endswith(".xml"): voc_to_yolo(os.path.join("annotations", file), "labels_yolo")

注释里已经写了重点:越界裁剪。LabelImg 画框允许框超出图像边界,如果不去裁剪,中心点公式会落在图像外,YOLO 损失计算会出问题。转换完成后,再按文件数做 8:2 的 train/val 划分。建议把同一段视频的连续帧分到同一侧,避免视频相邻帧泄漏进验证集,否则你的验证集分数虚高,真实水平被严重高估,上场才现原形。

3.4 数据增强:YOLOv8 自带增强和需要避开的翻转坑

YOLOv8 训练时默认做 Mosaic、HSV 扰动、随机平移缩放,对雷达站场景基本够用。但有一个坑:如果你把“数字”作为类别,就不要随意开水平翻转。数字“2”翻转后外观接近一张无效样本,“5”翻转后接近“2”也分不清,翻转会破坏数字语义。

更安全的做法是只做轻微旋转、HSV 亮度扰动、随机裁剪,也不翻转。用 Albumentations 离线生成增强副本时注意控制副本数量:

import albumentations as A transform = A.Compose([ A.Rotate(limit=5), A.HueSaturationValue(hue_shift_limit=8, sat_shift_limit=15, val_shift_limit=20), A.RandomBrightnessContrast(brightness_limit=0.2, contrast_limit=0.1), A.HorizontalFlip(p=0.0), ])

这套增强和“按场景分层”的采集配合很好:轻微旋转模拟车辆斜向逼近,亮度扰动模拟赛场投光灯的强弱变化。但离线增强会让训练集膨胀,建议每张原始图最多生成 2~4 个副本,训练时间翻倍的代价换来的收益过了这个量就明显下降。

增强做完,画图验证一步不能省。按随机的样张把标签框渲染回原图,如果放大装甲板区域,边缘有一半背景或者灯条没框住,看起来歪,就要回头调标注框,而不是靠模型硬扛。标注质量和增强质量一样重要,这一眼检查能省掉后期大量排错时间。

4. 开源源码怎么选:传统 OpenCV 方案与 YOLO 方案的落地对比

4.1 传统视觉与深度学习方案各自的边界

在开源社区里,雷达站视觉源码大体分两类。

一类是 OpenCV 的颜色阈值方案:先把图像转到 HSV,用颜色阈值提取灯条的红色或蓝色区域,再用轮廓筛选、灯条对匹配,最后用 PnP 计算角度。优势是对嵌入式环境非常友好,通常 CPU 上能跑到 50fps 以上,不依赖标注样本;劣势是照明条件一变就露馅,尤其是对方灯条直射相机时,阈值很难覆盖,极易闪断。

另一类是 YOLO 深度学习方案:把装甲板、灯条甚至数字交给模型检测,然后叠加后处理。优势是抗光照能力强、泛化窗口大;劣势是需要数据集、需要训练,还要考虑嵌入式推理优化。雷达站常见的嵌入式平台,例如 Jetson 系列或者 RK3588,YOLO 都要做转换优化才能跑到可用的帧率。

很多开源实现其实是混合的:用 YOLO 先做粗检测,缩小搜索区域,再用传统方法做角点精定位。这样模型和传统方案都只在擅长的范围工作,稳定性通常最好,但代码复杂度也更高。选型标准我建议这样判断:如果队伍刚起步、离比赛还有三个月以上,直接上 YOLO 路线并走 TensorRT 优化;如果只有两周准备时间,纯 OpenCV 方案更容易在现有代码上改出效果,至少先保证“有数据可打”。

4.2 最小推理管线:YOLO 检测装甲板,像素坐标转云台角,串口发出

我在工程里常用的是 YOLO 检测 + OpenCV 坐标换算 + 串口输出的三段式。核心代码就一个循环,目标是快速验证整条链路,别把优化留到跑通之前。

import cv2 import numpy as np import serial import struct import math from ultralytics import YOLO K = np.array([[800.0, 0.0, 640.0], [0.0, 800.0, 360.0], [0.0, 0.0, 1.0]]) # 用实际标定的相机内参替换 model = YOLO("rm_armor.pt") cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 0.25) # 手动曝光 cap.set(cv2.CAP_PROP_EXPOSURE, 200) # 具体值按现场照度调 uart = serial.Serial("/dev/ttyUSB0", 115200, timeout=0.05) def pixel_to_angle(cx, cy): fx, fy = K[0, 0], K[1, 1] cx0, cy0 = K[0, 2], K[1, 2] yaw = math.degrees(math.atan2(cx - cx0, fx)) pitch = math.degrees(math.atan2(cy - cy0, fy)) return yaw, pitch while True: ret, frame = cap.read() if not ret: continue results = model(frame, imgsz=640, conf=0.35, verbose=False) for box in results[0].boxes: x1, y1, x2, y2 = box.xyxy[0].tolist() score = float(box.conf[0]) cls = int(box.cls[0]) if cls != 0: # 0 代表装甲板 continue cx = int((x1 + x2) / 2) cy = int((y1 + y2) / 2) yaw, pitch = pixel_to_angle(cx, cy) # 帧头 + 类别 + 偏航角 + 俯仰角 pkt = struct.pack('>BBff', 0xAA, 0x55, float(yaw), float(pitch)) uart.write(pkt)

这里最值得看的不是检测调用,而是 pixel_to_angle:atan2(cx - cx0, fx) 把像素偏移量换算成相对相机光轴的偏航角,属于单目最简单的测角方式。要让它准确,K 矩阵必须是这张相机在 1280x720 分辨率下的真实内参,不能直接抄示例里的 800 和 640。建议先用棋盘格标出 fx、fy、cx、cy 再替换。

串口协议我写成了 1 字节帧头 + 1 字节类别 + 两个 4 字节 float,这是调试阶段好用的做法。正式比赛往往会压缩成定点数并加 CRC,因为在低波特率下 float 传输会拖长单次传输时间,影响整条链路的延迟。

4.3 必调参数:曝光、置信度、NMS 与相机标定

这一段直接给新手参照。相机参数和推理参数有五个最需要动:

参数推荐起点值说明
自动曝光关闭开启后画面会随灯光变化剧烈波动,检测会跟着闪
曝光值100~300(V4L 范围)以灯条不过曝为准,具体看现场照度
conf 阈值0.35太低误检多,太高近处小目标又被丢掉
NMS IoU 阈值0.6高于默认值,避免同车多块装甲板被错误合并
imgsz640追求帧率时可压到 416,近距离场景的 mAP 损失可控

相机标定建议用棋盘格在 1280x720 下拍 15~20 张不同角度,用 OpenCV 的 calibrateCamera 算内参和畸变系数。注意标定和推理要用同一分辨率,否则焦距和主点全部对应不上,角度换算就会带上固定的偏置。

一个容易忽视的细节是:画面中出现两辆车同框、多块装甲板候选时,到底把哪个目标发给控制器,要显式决定。不能只按模型的 conf 降序选。我一般会先按“距离画面中心最近”或者“距离上一次锁定目标最近”来选,后者在追踪场景里更稳,且不会在车辆交叉时频繁切换目标。

很多开源仓库只做了检测,没做目标跟踪和多目标分配,你需要自己补这一小段逻辑。这也是“开源源码”落到自家车上的第一道坎:检测结果怎么变成控制指令,代码里往往写得最简略。

5. 避坑 / 常见问题:雷达站数据集与开源部署的 5 条踩坑记录

5.1 现象:训练 mAP 很高,一到现场暗光环境就翻车

主要原因不是模型结构,而是训练集分布跟场地真实分布不一致。室内采集的帧通常在均匀光照下,装甲板灯光和背景分离得很干净;到了比赛场,头顶大灯、敌方补光、地面上杂乱反光全混在一起,模型见过的差异少了,自然崩溃。

解决办法:把现场视频按前面的流程抽帧、清洗、补标回数据集,重新训练后再验证。不要先急着调曝光和阈值,那是用后处理解决分布问题,治标不治本。补数据时尽量多录几天不同时段的光照,哪怕隔几天再录一段,也比连续录一下午有用。

5.2 现象:模型能检测,但在 CPU 平台只有 8fps,根本供不上雷达站控制

原因是把输入分辨率设得过高,或者选了 YOLOv8x/l 级别的大模型,又没做任何端侧优化。雷达站的帧率优先级通常高于 mAP,因为它一旦滞后,发给下位机的角度就是过时的,控制闭环会振荡。

解决办法:用 YOLOv8n 或更小的模型,同时把 imgsz 压到 640 甚至 416;在 Jetson 上走 TensorRT 转 FP16,这是嵌入式开源项目里最常规的加速路线。转换后必须用同一段回放视频对比转换前后的输出框,防止某些层优化后数值漂移。这个对比我每次都会做,省得上了场才发现新模型是“看起来很快,实际不准”。

5.3 现象:开源源码的串口协议跟自家控制板对不上,云台乱转

原因很常见:开源项目使用的帧头、字节序、校验方式跟你的下位机完全不一样。雷达站和下位机之间的协议是最容易“代码能编译但连不上”的部分,而且很难在仿真里发现。

解决办法:第一步把协议文档里波特率、帧头、数据类型、校验算法四项列成表格,跟代码逐一核对;第二步先接串口调试助手回环测试,别接电机控制板;第三步写一段仿真脚本,模拟云台随机角度,检测时序和粘包问题。很多“雷达站代码跑起来会抖”的反馈,最终都指向协议字段没对齐,而不是算法本身的问题。

5.4 现象:旋转装甲板框得不准,训练出来的中心点偏一边

原因是把旋转目标当轴对齐框去框。装甲板斜着出现在画面时,轴对齐框的中心点不等于装甲板几何中心,用来算角度就有系统性偏差。

解决思路有两类。一是训练阶段直接用旋转框标注和训练,走 MMRotate 或 YOLO-OBB 路线,参考 DOTA/HRSC2016 这类旋转框公开数据集的格式和增强策略;二是保持轴对齐框做粗检,再用两块灯条的中心点做精定位,最后用四点坐标做 PnP。我推荐第二种作为第一步,它绕开旋转框训练的前期复杂度,只靠一个灯条检测模型就能获得稳定得多的中心点,后期再决定要不要升级到旋转框方案。

5.5 现象:标注文件里出现宽高为负的框,训练直接崩

原因常见于标注画框时从右下往左上拖,或者手工改数据时漏改坐标。这类异常在几百张时还能靠运气撑过去,一旦到几千张,代码会集中爆出来。

解决办法是在格式转换脚本里加一个 max(0, xmax - xmin) 的检查,发现异常框直接打日志跳过,而不是让训练在中间崩掉。更进一步的做法是做可视化检查:把标签框渲染回原图,快速目检一遍。我每个批次抽查 10%,专看第一次转出的帧,这比任何 schema 校验都直接。标注数据的脏,往往就脏在这些看不见的角落。

6. 进阶:用视频回放验证整条链路,再压出最后 10ms

6.1 离线回放验证:先不接设备,把“图像→角度→协议”整链跑通

雷达站最麻烦的环节不在模型,而在“检测到目标之后发送的角度”。把主循环封装成函数,输入换成视频文件,输出写到 CSV,再和手工标出的真值角度对比,看误差是否在可接受范围。

我实际做法是录一段带云台角度的真车视频,每帧跑推理,将预测 yaw/pitch 和时间戳写进文件,再用同一段视频逐帧核对。就这么简单,能提前发现 90% 的协议、角度方向、坐标系正负号问题。注意检查角度的正负号和零点偏移:很多开源代码假定相机光轴与云台机械轴完全重合,实际安装时总有几度偏差,这个偏差要在发送之前用固定目标实测补偿掉。

6.2 压帧率:TensorRT 转 FP16,imgsz 从 640 压到 416

耗时大户基本是前处理 resize、模型推理、后处理三个位置。我常用的顺序是:先把 OpenCV resize 改成原生缩放,减少不必要的插值,能省一点点;再把模型输入从 640 降到 416,通常能换回 1.5 倍左右的帧率,mAP 损失在近距离场景不明显——雷达站本来就是近场为主。最后用 TensorRT 转 FP16。

转换时记住三个关键点:explicit batch 设为 1,dynamic shape 如果嫌麻烦先固定成 1x3x416x416,转换后用同一段视频对比原始 PyTorch 输出的检测框,差异在 1% 以内才算安全。INT8 虽然能再快一些,但校准数据集没覆盖到位时,小目标容易突然消失,得不偿失。

6.3 最后一点经验:先保障“角度稳定”再谈“距离精确”

我最早做雷达站时把精力全放在测距上,想通过单目直接精确算出对方距离,结果角度抖动都没解决,距离值毫无意义。后来把重心放回像素坐标转角度、加小视场平滑,系统立刻稳定下来。对雷达站这个位置来说,控制器真正高频依赖的是角度而不是绝对距离,距离可以作为低速辅助量。这条经验来自一次很狼狈的实战排练,也让我后来跟队里同学说得最多的一句变成了:先把角度链路做到闭环,再想“打得准”的事。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询