ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

毫米波雷达无线感知:DETR动作识别毕设源码详解

毫米波雷达无线感知:DETR动作识别毕设源码详解 简介基于毫米波雷达的无线感知智能化算法设计完整毕业设计源码与论文文件打包为zip。面向智能家居应用场景方案提出将特征队列提取与任务输出需求解耦的系统架构利用人体骨架关键点、锚定框等通用中间特征统一不同任务相比端到端网络具备更好的鲁棒性与可移植性同时针对Detection Transformer模型进行毫米波领域创新性修改并完成仿真数据生成、真实数据采集与解析。压缩包共91个文件以Python源码py/pyc、Jupyter Notebook、训练过程及数据分布图jpg、论文PDF和说明文档为主整体约13.16MB目录涵盖code、datasets、models、util等模块含数据加载器、模型构建、训练评估与可视化脚本便于直接运行或二次开发且结构清晰。已有100人学习下载适合需要完整参考实验流程与论文写作框架的读者。1. 毫米波雷达无线感知这套毕设源码把动作识别做成了两段流水线智能家居里的毫米波雷达无线感知最常翻车的点不是模型不够深而是换个房间、换个人准确率就掉一截。直接拿点云做端到端动作分类模型记住的是训练集里的站位和速度分布一到新场景就露怯。这套毕设源码没堆更强的分类网络而是把“人在哪、在做什么”拆成两段先用 DETR 输出通用锚定框BBOX再基于这个中间特征做动作判别。项目核心是把 Detection Transformer 针对毫米波雷达做了适配包含完整可训练的 DETR 实现、真实雷达数据加载器、训练引擎、IOU 评估脚本和毕业论文。要解决的是点云稀疏、噪声大的条件下动作识别精度不够高的问题。源码目录里模型、数据集、评估、画图脚本分得清清楚楚README 也标了依赖跑起来不需要东拼西凑。你在做毫米波雷达、无线感知或 DETR 相关课题的话这套资源值得对照论文读一遍。下面从技术路线、数据流、训练参数和踩坑记录逐层拆。2. 为什么端到端分类会撞墙解耦架构与 DETR 适配方案2.1 端到端网络的瓶颈任务耦合是精度上不去的根源雷达无线感知里最常见的 baseline 是把点云体素化喂给 CNN输出动作类别。这个流程在单一固定场景下很漂亮但它有一个结构性问题——特征提取和任务输出是耦合的。backbone 是被动作分类标签直接监督的学到的是“对这个数据集、这套站位、这个速度区间有效”的特征而不是“人形目标在雷达坐标系里的通用表征”。我拆过不少类似项目这类模型的典型症状是A 房间测试准确率 90% 以上换到 B 房间雷达安装高度差了 20 厘米准确率直接跌到 70%。原因不是网络容量不够而是模型把“人站在哪、往哪个方向走”当成了分类依据。另一个问题是扩展性今天要分走路、小跑、静止三个动作明天要加跌倒检测端到端的做法是重新标注、重新训练整个网络动作类别一多标注成本跟着爆炸。所以项目论文里反复强调一个词解耦。把“感知人形目标”和“判断具体动作”分成两个层次来建模。这个思路和计算机视觉里“检测 分类”的两阶段范式一脉相承但落到了雷达点云上。2.2 特征队列与输出解耦锚定框如何统一多个动作任务论文的核心贡献是提出了“特征队列”与“任务输出需求”解耦的架构。特征队列可以理解为一组通用中间特征——人体骨架关键点、锚定框、或者目标中心的 3D 坐标。下游任务需要的不是原始点云而是这组中间特征。以这套源码为例选的是锚定框BBOX先用模型预测出人形目标在雷达坐标系里的 3D 包围盒再用这个包围盒去聚合点云特征做动作判别。这样做的好处有三个。第一BBOX 预测是任务无关的不管后面接的是动作分类、跌倒检测还是轨迹跟踪前面这层不用动。第二动作分类的输入从“整帧稀疏点云”变成“目标框内的结构化特征”类别判断受位置偏移的干扰小得多。第三可移植性强——同一个检测头换一个环境只要重新微调不用从头训练。端到端分类和解耦架构的差别可以看这张对比表维度端到端分类网络特征解耦 BBOX 中间层换场景迁移基本要重训微调检测层即可新增动作类别重新标注全部数据只加分类头和数据特征可解释性黑匣子无法定位失败原因先看 BBOX 是否准再查分类对点云稀疏度的敏感度高整帧稀疏直接掉点低目标局部特征相对稳定这套架构里新加一个动作理论上只动最后的分类层检测层和特征队列都不需要改。这就是论文里说的“更鲁棒、更适用、更可移植”。2.3 DETR 在雷达场景的适配集合预测、匈牙利匹配与位置编码选 DETR 而不是 Faster R-CNN 或 YOLO 做 BBOX 预测是这套源码里值得琢磨的一个决定。雷达点云没有 RGB 纹理anchor-based 检测器对 anchor 的尺寸预设非常敏感——人走近雷达时点云占的栅格数会变大走远时变小固定 anchor 很难覆盖这种尺度变化。DETR 是集合预测范式不需要预设 anchor直接用一组可学习的 object query 在解码器里迭代出目标框天然避开 anchor 设计。模型结构上源码把 DETR 的四个关键件都拆出来了backbone 负责把 BEV 栅格特征图提成高维特征position_encoding 把雷达坐标系下的位置信息编码进特征transformer 的 encoder 做全局上下文建模decoder 用 object query 迭代出预测new_matcher 用匈牙利算法在预测框和真实框之间做最优匹配匹配成本由分类损失、L1 框距离和 GIoU 三部分加权组成。DETR 在雷达场景必须做适配的点是位置编码。图像 DETR 的位置编码作用在像素坐标上而雷达 BEV 栅格的横纵轴对应真实物理空间的距离和横向位置单位是米。源码的 position_encoding.py 仍然用正弦余弦编码但编码的坐标轴含义已经换成“物理坐标栅格”这直接影响模型对目标相对雷达远近的感知能力。另一个适配点是 query 数量图像目标检测一张图可能有几十个目标而智能家居单雷达场景通常只有一到两个人源码里 query 数量设得很小避免大量空 query 占用匹配和不必要的计算。3. 把训练跑起来从雷达点云到 BBOX 的数据流与关键参数3.1 真实数据长什么样点云、JSON 标注与 BEV 栅格化这套项目采集的是真实毫米波雷达数据不是仿真数据。雷达输出的每个点通常包含四到五个维度相对雷达的 x、y、z 坐标、多普勒速度、信噪比。标注文件是 JSON 格式labeled_json_list.txt 里按行记录了所有标注文件的路径。每个 JSON 里至少包含三样东西point 数组、bbox_3d 包围盒、action 动作标签。我一般拿到一套雷达数据第一步是去看 BBOX 标注的坐标系定义。radar 坐标系的 x 轴多是正前方距离y 轴是横向z 轴是高度单位都是米。bbox_3d 可以用多种格式存——中心点 长宽高、或者两个对角点。训练前必须确认 DETR 输出的是归一化的中心点格式还是反归一化回物理坐标后再算损失。这两者差一步转换loss 的表现会完全不同。雷达点云没法直接喂给 transformer需要先栅格化成 BEV 特征图也就是把三维空间投影到地面俯视图的网格上。源码里用的是三维体素信息而不是丢掉高度的纯 BEV这样动作判别能用到高度维度的变化——人蹲下和站立在 z 轴上的分布差异很大对跌倒类动作尤其重要。3.2 数据加载器 Radar_Real_dataloader_new.py 阅读笔记数据加载器的核心是把原始点云变成多通道 BEV 栅格。下面是我按源码结构还原的核心逻辑去掉了无关分支方便讲参数含义# datasets/Radar_Real_dataloader_new.py 的核心逻辑示意 import json import numpy as np import torch from torch.utils.data import Dataset class RadarRealDataset(Dataset): def __init__(self, json_list_path, grid_range(-4, 4, -5, 5), grid_res0.1, max_pts512): # grid_range: BEV 栅格在 x/y 方向覆盖的范围单位米 # grid_res: 栅格边长0.1 表示 10 厘米一格 # max_pts: 单帧最多取多少点超出截断防止极端帧撑爆显存 with open(json_list_path, r) as f: self.label_files [line.strip() for line in f if line.strip()] self.grid_range grid_range self.grid_res grid_res self.max_pts max_pts def _points_to_bev(self, points): # points 形状 (N, 4)依次是 x(m)、y(m)、z(m)、多普勒速度(m/s) xmin, xmax, ymin, ymax self.grid_range H int((ymax - ymin) / self.grid_res) W int((xmax - xmin) / self.grid_res) # 三通道占用密度、高度最大值、多普勒均值 occ np.zeros((H, W), dtypenp.float32) hmax np.zeros((H, W), dtypenp.float32) dopp np.zeros((H, W), dtypenp.float32) for px, py, pz, v in points[:self.max_pts]: ix int((px - xmin) / self.grid_res) iy int((py - ymin) / self.grid_res) if 0 ix W and 0 iy H: occ[iy, ix] 1.0 hmax[iy, ix] max(hmax[iy, ix], pz) dopp[iy, ix] v dopp np.divide(dopp, occ, outnp.zeros_like(dopp), whereocc 0) return np.stack([occ, hmax, dopp], axis0) # (3, H, W) def __getitem__(self, idx): with open(self.label_files[idx], r) as f: anno json.load(f) points np.asarray(anno[points], dtypenp.float32) bbox np.asarray(anno[bbox_3d], dtypenp.float32) action anno[action] # walk / jog / static bev self._points_to_bev(points) return torch.from_numpy(bev), torch.from_numpy(bbox), action三个通道的设计是有讲究的占用密度通道区分“有没有人”高度最大值通道区分“站姿和蹲姿”多普勒均值通道区分“走动和小跑”——小跑的多普勒速度明显比走路高。如果之后要扩展跌倒检测通常还会加一个 z 轴速度方差通道。grid_res 是这里最值得调的参数。0.1 米一格在 8×10 米的范围内生成 80×100 的栅格分辨率够用且计算量可控。如果调成 0.05空间分辨率提升但单帧计算量翻四倍而且点云稀疏时很多格子是空的收益有限。我一般先用 0.1 跑通流程最后调优阶段再试 0.05比较 IOU 的增量值不值这个计算成本。3.3 模型装配backbone、transformer、matcher 怎么协同模型文件分散在 models/ 目录下detr.py 是组装入口backbone、transformer、position_encoding、new_matcher 各自独立。我习惯先从 detr.py 的 forward 看数据流因为它定义了各个模块之间的接口约定# models/detr.py 的结构压缩版 import torch.nn as nn from .backbone import build_backbone from .transformer import build_transformer from .position_encoding import build_position_encoding class Detr(nn.Module): def __init__(self, num_classes4, num_queries5): super().__init__() # backbone 输出通道固定成 256之后 transformer 的 d_model 也是 256 self.backbone build_backbone(out_channels256) self.position_encoding build_position_encoding() self.transformer build_transformer( d_model256, nhead8, num_encoder_layers6, num_decoder_layers6 ) # 类别数含一个无目标类实际动作类是走路、小跑、静止三类 self.class_head nn.Linear(256, num_classes) self.bbox_head nn.Linear(256, 4) # 输出归一化 [cx, cy, w, h] def forward(self, x): features self.backbone(x) # 得到特征图形状 (B, 256, H, W) hs self.transformer(features, self.position_encoding) # hs 形状 (num_queries, B, 256) cls self.class_head(hs) # (num_queries, B, num_classes) box self.bbox_head(hs).sigmoid() # bbox 必须压到 0-1 return {pred_logits: cls, pred_boxes: box}这里有两个关键接口约束。第一backbone 输出通道必须和 transformer 的 d_model 一致否则拼接位置编码时维度对不上。第二bbox 输出必须过 sigmoid 压到 0 到 1因为后面匈牙利匹配里的 L1 距离是在归一化坐标下算的。很多自己改 DETR 的人会在这一步漏掉 sigmoid导致 box 预测值跑到范围外IOU 怎么都上不去。new_matcher.py 实现的是匈牙利匹配匹配成本是三部分的加权和# models/new_matcher.py 的匹配成本逻辑 # cost cost_class * cls_weight # cost_bbox_l1 * bbox_weight # cost_giou * giou_weight匹配的作用是把 N 个 query 的预测和 N_gt 个真实框一一对应起来训练时每个真实框只分配给一个 query剩下的 query 要学会输出“无目标”。这套机制替代了传统检测里的 NMS 和 anchor 匹配是 DETR 的核心。3.4 训练与评估loss 之外必须盯住 IOU 分布训练入口在 main.py训练循环在 engine.py。engine.py 里我重点看的是损失加权和反向传播逻辑# engine.py 的训练循环核心片段 def train_one_epoch(model, criterion, data_loader, optimizer, device): model.train() for samples, targets in data_loader: samples samples.to(device) targets [{k: v.to(device) for k, v in t.items()} for t in targets] outputs model(samples) # outputs[pred_logits]: (B, num_queries, num_classes) # outputs[pred_boxes]: (B, num_queries, 4) loss_dict criterion(outputs, targets) weight_dict {loss_ce: 1.0, loss_bbox: 5.0, loss_giou: 2.0} losses sum(loss_dict[k] * weight_dict[k] for k in loss_dict.keys()) optimizer.zero_grad() losses.backward() optimizer.step()weight_dict 是最值得动手调的地方。原始的 DETR cosine 方案里 bbox 权重是 5、giou 是 2小目标多的时候 giou 权重可以提到 3 或 4。注意这里 loss_bbox 用的是 L1它对大目标和小目标的误差一视同仁但雷达点云下距离远的目标天然框小L1 误差被归一化后相对偏大所以代码里通常要和 giou 配合——giou 对尺度不敏感能平衡 L1 的尺度偏差。评估脚本是 radar_eval_bbox.py它做的事是把预测框和真实框的 IOU 全部算出来再按动作分组统计分布。IOU 的计算本身很简单但要注意坐标系的约定必须和训练时一致否则评估出来的数字没有参考意义。项目里那几张“不同动作预测 IOU 分布图”就是用这个脚本的结果画的比单独看 loss 曲线能说明问题得多——loss 是所有样本的平均IOU 分布能看出哪个动作类别在做贡献、哪个类别在拖后腿。4. 毫米波雷达训练避坑指南五类高频翻车现场与排查记录4.1 静止样本挤压运动样本预测框整体偏移**现象**训练几千步后走路和小跑的预测框还能大致贴合目标但样本量更大的静止类别把所有 query 都往中心区域拉可视化预测框时发现静止类别的框普遍偏大运动类别的框则偏小整套 BBOX 预测出现系统性偏移。**原因**数据集里静止场景的采集成本低样本量可能是运动类别的两到三倍。匈牙利匹配按全局最优配对分类损失占大头时模型倾向于把 query 都匹配到容易分类的静止目标上运动类别的 query 被挤占。这是 DETR 在类别不平衡数据上的通病不是雷达特有的但雷达点云稀疏让这个问题更明显。**解决**在损失里对动作类别做加权静止类别权重降到 0.5 左右运动类别提到 1.5 以上更直接的办法是在加载器里对三个动作做等比例采样每个 batch 保证三类样本数量接近。我一般两种都做等比例采样影响训练稳定性损失加权影响最终精度配合起来最省事。4.2 loss 一路下降但 IOU 不涨匹配成本失衡**现象**训练曲线很漂亮loss 稳步下降但 radar_eval_bbox.py 算出来的 IOU 中位数卡在 0.4 左右不动。预测框似乎找到了目标的大致位置但边界始终对不准。**原因**loss 下降被分类损失主导了。分类任务相对好学模型很快就学会把每个 query 分到正确的类别剩下的 bbox 回归任务在总损失里占比太小优化器没有足够的梯度去修正框的边界。典型的权重失衡——weight_dict 里 loss_bbox 权重太低或者匹配成本里 bbox 部分占比太小导致匹配阶段就把框位置误差放过了。**解决**先把 weight_dict 里 loss_bbox 从 5 提到 10loss_giou 从 2 提到 3观察 IOU 是否开始跟着 loss 走。如果还不行去查 new_matcher 的匹配成本权重确保匹配阶段对框位置有足够约束。判断标准是匹配成本里 bbox 相关部分应该至少和分类部分同量级否则匈牙利匹配只按照分类配对框回归信号从一开始就是脏的。4.3 训练曲线震荡不收敛学习率与 query 数量耦合**现象**训练 loss 在几百步内快速下降后开始剧烈震荡偶尔出现尖峰换一个随机种子结果差异很大同一个配置跑两次最终 IOU 能差五个百分点。**原因**transformer 部分对学习率非常敏感。雷达数据量小通常只有几千帧backbone 用大学习率很容易把预训练特征冲掉而 DETR 的 decoder 在训练早期会频繁调整 query 的目标分配学习率太大时匹配结果不稳定loss 自然震荡。另外 num_queries 设得比场景里真实目标数多很多时大量空 query 会让分类头不断在“无目标”和“有目标”之间切换也会放大震荡。**解决**把优化器换成 AdamWtransformer 部分学习率用 1e-4backbone 学习率单独设成 1e-5给 backbone 加一层 0.1 倍缩放。训练轮次不要少于 150 epochDETR 类模型是出了名的后期才收敛。num_queries 从 5 开始调——单雷达单人场景 5 个 query 够用且留了余量调成 10 不会带来精度提升只会增加空 query 占比和训练时间。4.4 仿真转真实掉点坐标对齐是最隐蔽的坑**现象**先用仿真数据训练模型在仿真测试集上 IOU 超过 0.8换到真实采集数据直接评估IOU 掉到 0.4 以下而且预测框整体往某个方向偏移。**原因**仿真数据标注的坐标系和真实数据标注的坐标系不一致。常见的情况是仿真里雷达坐标系原点在雷达本体x 轴正方向是雷达朝向真实采集时标注工具用了图像视角的坐标转换或者 z 轴方向没有对齐。这个偏移很小人工检查单帧根本看不出来但匈牙利匹配对全局偏移极其敏感所有框同时偏一个方向IOU 会系统性下降。**解决**加载数据后先做一个坐标一致性检查——随机抽 50 帧打印点云坐标的 min/max和标注框的坐标范围对比确认 x/y/z 的语义一致。再算一下真实数据里所有 bbox 中心点的均值和 std和仿真数据的对应统计量对比。两个分布差异超过两倍优先怀疑坐标变换而不是模型。我当时是卡了两天最后发现仿真数据的 y 轴方向反了翻转过来 IOU 直接涨了二十个点。4.5 稀疏点云导致检测框抖动时序平滑收尾**现象**模型在单帧上预测的 IOU 不低但连续帧的可视化里预测框来回跳位置抖动明显。走路这类连续动作上一秒框在前方下一秒跳到侧面动作分类倒是没受影响但框的稳定性没法用。**原因**雷达点云本身是稀疏的单帧目标可能只有几十个点帧与帧之间点云分布变化大。DETR 对每一帧独立做集合预测没有时序约束帧间抖动是正常现象。这不是模型 bug是单帧检测的通病。**解决**在 post-processing 阶段对连续帧的 bbox 做指数滑动平均alpha 取 0.3 到 0.4比纯平均更能跟上动作变化。动作分类也建议用窗口投票——取最近 5 帧的分类结果做多数表决能明显抑制单帧误判。这类后处理的代码不复杂但效果立竿见影是工程落地时性价比最高的一步。5. 画图脚本怎么用BBOX 分布、loss-IOU 与 UMAP 的排查链路5.1 先画 BBOX 数据分布图标注质量决定模型上限项目 Fig 目录里那几张 BBOX 数据分布图不是摆设它们是整个项目的“体检报告”。画BBOX分布图.py 做的事很简单把每帧标注框的中心点按动作类别画成散点。我在跑任何训练之前都会先执行这一步# plotting/画BBOX分布图.py 的核心流程 import json import numpy as np import matplotlib.pyplot as plt with open(labeled_json_list.txt) as f: files [line.strip() for line in f if line.strip()] centers {walk: [], jog: [], static: []} for path in files: anno json.load(open(path)) b anno[bbox_3d] centers[anno[action]].append([b[0], b[1], b[2]]) fig plt.figure(figsize(8, 6)) ax fig.add_subplot(111, projection3d) for action, color in [(walk, C0), (jog, C1), (static, C2)]: data np.array(centers[action]) ax.scatter(data[:, 0], data[:, 1], data[:, 2], s2, alpha0.5, labelaction) ax.set_xlabel(x (m)) ax.set_ylabel(y (m)) ax.set_zlabel(z (m)) ax.legend() plt.savefig(bbox_dist.png, dpi150)看这张图有三个重点一是三类动作的空间覆盖范围是否重叠如果走路全在左边、小跑全在右边模型学到的可能不是动作差异而是位置差异二是 z 轴分布有没有分层静止和运动的 z 分布如果完全重合说明动作判别只能靠多普勒通道三是各类别点数有没有明显差距这是后面类别不平衡处理的前置判断。5.2 loss 与 IOU 关系图判断训练是否真收敛loss 曲线人人都会看但 loss 下降不等于检测精度上升这个项目专门画了 loss 与 IOU 关系图两个指标叠在同一条时间轴上。核心判断逻辑是loss 下降的同时 IOU 必须同步上升如果出现“loss 平缓但 IOU 持续下降”说明模型在过拟合分类任务检测能力在退化。训练过程的正常形态是前 50 个 epoch loss 快速下降IOU 从 0.2 涨到 0.650 到 100 个 epoch 两者增速放缓100 个 epoch 以后 loss 进入平台期IOU 可能还能缓慢涨几个点。IOU 平台期比 loss 平台期来得晚是 DETR 的正常现象不要在 loss 刚平稳时就停训练多等二十个 epoch 往往有惊喜。这张图还有一个作用判断要不要加载 checkpoint 继续训。如果 loss 还在降、IOU 也在涨直接续训不需要动超参数如果 loss 降但 IOU 不动回到第 4.2 节的权重排查流程而不是盲目加训练轮数。5.3 UMAP 特征可视化检查特征队列是否可分umap.jpg 是这套项目里最有意思的一张图它把模型提取的特征用 UMAP 降维到二维平面看三类动作在特征空间里的分布。我一般从 transformer 的 encoder 输出或者分类头之前的特征层取值拉平成向量后交给 umap 库# plotting 里 UMAP 可视化的流程 # 1. 加载训练好的模型提取中间层特征 # 2. 每帧得到一个特征向量形状 (num_queries, 256)拉平成 1D # 3. 用 umap 降到 2D 后按动作类别着色 import umap reducer umap.UMAP(n_neighbors15, min_dist0.1, metriceuclidean) # feature_matrix 形状 (N_frames, feature_dim)每行是一帧的特征 embed reducer.fit_transform(feature_matrix)UMAP 图能直接回答“特征队列是否解耦成功”这个问题。理想状态下三类动作在 2D 空间里呈现三个清晰但不完全分离的簇——完全分离说明特征学到的是动作差异重叠严重说明特征队列还是被位置或速度主导。如果三类动作在 UMAP 里按空间位置排布、而不是按动作类型聚集那就要回到数据层面重新审视标注质量。5.4 不同动作预测 IOU 分布图定位薄弱动作项目里“不同动作预测 IOU 分布图”用的是雷达评估脚本的输出对每个动作类别分别统计预测框和真实框的 IOU 分布。这张图的用处是定位薄弱动作而不是看平均分。平均 IOU 0.7 可能掩盖着“走路 0.85、静止 0.8、小跑 0.45”的极端情况。我拿到这张图后的处理流程固定是先看哪个类别的 IOU 中位数最低再看这个类别的 BBOX 数据分布——是样本量太少还是空间覆盖有盲区或是多普勒特征本身就弱。确定原因后针对性地补数据或者调损失权重而不是整体调参。这个“数据 → 模型 → 评估 → 归因”的闭环比只看 test accuracy 调参靠谱得多也是这套画图脚本真正的价值所在。6. 进阶迁移把 DETR 检测器扩到更多动作与多人场景如果你用这套源码复现完走路、小跑、静止三个动作下一步通常是把检测器往更多场景迁移。两种常见的扩展方向增加动作类别以及多目标场景。增加动作类别是成本最低的迁移。把 detr.py 里 num_classes 从 4 改成 5多一个“跌倒”类把数据集标注补上对应类别加载器里动作字符串映射表加一项然后直接复用已有权重微调。我在做这类扩展时建议只微调分类头和 transformer 最后两层backbone 冻结学习率设成训练时的十分之一这样既能保留已经学好的 BBOX 感知能力又能快速适应新动作。要注意新类别的样本量不能太少否则匈牙利匹配阶段新类别抢不到 query训练完新动作识别率会很难看。多人场景是另一个方向改动集中在 query 数量和数据标注。单雷达场景下两人追跑需要把 num_queries 从 5 调到 10 或 15并且数据里要同时存在单人和双人帧否则匹配阶段模型没见过“两个真实框同时出现”的情况。多人场景的类别不平衡更严重建议把 bbox 损失的权重再往上提因为两个人的框互相靠近时分类损失区分度下降只能靠框回归约束匹配。如果用的是 4D 毫米波雷达点云里多出速度维信息BEV 通道里还可以再加一个垂直向速度分量对多人交错场景的区分效果比单纯调权重好得多。迁移时我最常检查的一组参数如下表每个参数改动后都先跑 30 个 epoch 看趋势再决定是否继续参数位置单人/三动作基线多人/多动作建议num_queriesdetr.py510~15num_classesdetr.py4类别数 1loss_bbox 权重engine.py weight_dict5.07.0~10.0学习率main.py1e-4微调时 1e-5栅格分辨率dataloader grid_res0.1m0.05~0.1m后处理平滑 alpha评估/推理逻辑0.3多人时 0.2验证方法也建议换一套思路不要只测离线 IOU。雷达感知最终要跑在嵌入式平台上我在迁移完总是做一次推理耗时记录——单帧前向加后处理目标是在 Jetson 级别的设备上达到 10 帧以上达不到就降栅格分辨率、减 encoder 层数优先保帧率再保精度。这套源码给我最大的启发是 DETR 在雷达这类无纹理传感器上比想象中适用只要把位置编码和特征通道按物理坐标重新设计集合预测范式对稀疏点云反而比 anchor-based 检测器更稳。现在我每次做雷达感知训练拿到新数据的第一件事就是先跑一遍 BBOX 分布图和 UMAP确认数据和特征的底子没问题再开始碰模型——这个习惯救过我太多次翻车现场。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进