简介:针对4D毫米波雷达在实际应用中静态杂波干扰动态目标检测的痛点,这份算法资源面向雷达感知、自动驾驶、交通监控等方向的算法工程师与研究者,聚焦静态目标与动态目标的区分、识别与滤除,可用于改善多目标跟踪和检测精度。整包仅348KB,共2个文件:1个MATLAB脚本(.m)实现静态杂波滤波核心流程,1个.mat数据文件存放用于验证算法的角度/回波测试数据,可快速加载运行并观察处理前后效果。目前已有2019人学习下载,属于轻量而典型的算法参考资料。读者既能从脚本中理解静态目标去除的完整信号处理链路与参数设置,也能借助附带数据复现实验,对比不同场景下的滤波结果;在此基础上,还可进一步尝试用CFAR、高斯混合模型或深度学习网络替换或融合原有方法,开展算法改进与性能调优。 在实验室调77GHz毫米波雷达的时候,我遇到的第一道坎就是静态目标去除。走廊里空无一人,点云图上却是密密麻麻的一片——墙、消防栓、门框、天花板和金属支架全被雷达当成"目标"亮了出来。等真人走进来,反而被这堆静态杂波淹得找不着北。这是所有做毫米波雷达信号处理的人迟早要面对的问题:雷达天生不挑食,凡是能反射电磁波的东西它都想报到点云里,而静止物体占了绝大部分。这篇内容就围绕着毫米波雷达静态目标去除算法展开,把原理、实现和工程里的坑一次性讲清楚。适合正在调雷达的算法工程师、做车载或安防感知的开发者,以及刚入门信号处理方向的学生。
1. 静态目标为什么是毫米波雷达的"头号干扰"
1.1 回波模型:所有反射物都在给雷达"发消息"
FMCW体制的毫米波雷达发射chirp信号,接收回波与发射信号混频后得到中频信号。每一个距离单元上的回波幅度和相位,记录的是该距离上所有反射体的矢量和。静止目标(墙、地、桌椅、金属立柱)的回波在连续多个chirp之间几乎不变,相位保持恒定;运动目标的距离在变化,回波相位会按多普勒频率持续旋转。所以把同一个距离bin上的慢时间序列拉出来看,静止目标表现为一个基本不动的直流分量,运动目标则是一个不断旋转的复数向量。
静态目标去除要做的事,就是这个"常量"从慢时间序列里滤掉,同时尽量保留运动目标的旋转分量。听起来不复杂,但雷达信号处理的所有麻烦都藏在"尽量"这两个字里。
1.2 静态目标在距离-多普勒图上的样子
距离FFT做完之后、多普勒FFT尚未进行时,静止目标在每个chirp上的复数值几乎完全一样。跨chirp做多普勒FFT,能量就集中在零速度bin(多普勒索引0)附近。多个不同距离上的静止目标叠加在一起,会在距离-多普勒谱的零速度位置形成一条横贯整个距离维的亮线。
如果多普勒FFT之前不做任何处理,这条亮线会一直压在CFAR检测门限之上。就算目标检测算法勉强绕开了它,窗函数旁瓣泄漏也会把零速度附近的底噪整体抬高,让附近低速目标的信噪比直往下掉。
1.3 不去除会引发什么连锁反应
静态杂波不处理,后面每一级都在给前面填坑。
第一,强静态杂波会抬高CFAR参考窗的均值,检测门限被拉高,弱目标直接被淹没。第二,静态点云持续出现在同一位置,跟踪器要反复处理这些固定虚假航迹,计算量上升,数据关联开始出错。第三,测角阶段如果静态反射体和目标处于同一距离-多普勒单元,相位不平衡会引入角度估计偏差。第四,在多目标场景,静止目标和运动目标回波耦合,目标分离和聚类算法都会失效。
所以,静态目标去除不是"锦上添花"的前处理,而是整个感知链条的地基。
2. 三条去除路线:MTI对消、多普勒置零与背景相消
2.1 三条路线的工作原理
处理静态目标,工程上有三类主流思路。
第一类是MTI(动目标指示)对消。在慢时间维对相邻chirp做差分,两脉冲形式是 y[n] = x[n] - x[n-1]。静止目标前后chirp相同,直接减没了;运动目标因为相位旋转,差分后还有残留。从滤波角度看,这相当于一个高通滤波器,传递函数 H(z) = 1 - z^{-1},在直流处有一个零点。
第二类是多普勒置零法。先把2D-FFT做完,在距离-多普勒谱上把零速度bin附近的若干行直接清零或压低权重。这个方法直观粗暴,适合快速验证,但问题不少,后文会专门讲。
第三类是背景相消法。维护一个随时间缓慢更新的背景模型,当前帧减去背景帧得到前景。工程上常用指数加权平均:B[n] = α·B[n-1] + (1-α)·X[n],再用X[n] - B[n]作为输出。α取0.9~0.99之间,控制背景更新速度。
2.2 三种方法的核心差异与选型
| 方法 | 运算量 | 静态抑制效果 | 低速目标影响 | 典型场景 |
|---|---|---|---|---|
| MTI对消 | 极低 | 高 | 有凹口,低速明显衰减 | 车载、运动目标速度较高的场景 |
| 多普勒置零 | 低 | 中 | 取决于清零bin范围 | 快速原型验证 |
| 背景相消 | 中 | 高 | 瞬时运动目标几乎不受影响 | 环境缓慢变化的场景 |
选型时最容易犯的错误是拿着一种方法套所有场景。MTI对消对低速目标不友好,背景相消对长时间静止目标"吸收",多普勒置零则受限于频谱泄漏。实际产品里往往不是只用一种,而是组合用:MTI先打掉固定强反射,背景相消再处理缓慢漂移的杂波残留。
2.3 我为什么优先推荐MTI
如果只让我选一种实现,我会选MTI。原因有三:实时性好,每个chirp只做一次复数减法和一次复数存储,完全可以在DSP或MCU上实时跑;参数少,主要就是PRT和阶数;效果可预期,幅度响应曲线一画,哪里衰减多少都算得出来。
背景相消需要额外维护背景帧,内存和定时逻辑都更复杂,在安防场景里还经常出现"人坐着不动被判无人"的bug。多普勒置零虽然简单,但速度分辨力不够时,低速目标和静态目标会挤在同一个bin里,一置零就把活目标也杀了。所以多数情况下MTI是那个"不会错太多"的答案。
3. MTI对消器从原理到代码:距离FFT后逐chirp相消
3.1 两脉冲对消的幅度响应:凹口到底有多宽
两脉冲对消的传递函数 H(z) = 1 - z^{-1},在单位圆上的幅度响应是 |H(f)| = 2|sin(π·f·PRT)|,其中f是多普勒频率,PRT是chirp重复周期。
f = 0时幅度为0,静止目标被彻底抑制;f增大后增益上升,直到 f·PRT = 1/2 时达到最大值2。这个响应曲线意味着MTI会在零速度附近形成一个凹口,凹口的宽度直接由PRT决定。
我举个例子。77GHz雷达波长约3.9mm,PRT = 100us。一个速度0.25m/s目标的多普勒频率 fd = 2v/λ ≈ 512.8v,v=0.25时约为128Hz。此时归一化频率 fd·PRT = 0.0128,两脉冲对消的增益 |H| = 2|sin(π×0.0128)| ≈ 0.08,也就是幅度只剩8%,大约-22dB。
一个0.25m/s的慢速目标在这种参数下基本被削没了。这说明MTI不能随便用在室内低速场景,上参数前必须先把幅度响应曲线画出来,确认你要关心的最小目标速度落在可接受的区间内。
3.2 三脉冲对消:边界更陡,但噪声也放大更多
为了加深静态抑制,工程上常用三脉冲对消:y[n] = x[n] - 2x[n-1] + x[n-2],传递函数 H(z) = (1 - z^{-1})^2。在直流处变成了二阶零点,零速附近的抑制比两脉冲更陡。
对同一个0.25m/s目标,三脉冲增益 |H| = 4sin^2(π×0.0128) ≈ 0.0065,约-44dB。所以三脉冲对极低速目标几乎是赶尽杀绝。
代价也明显:白噪声通过两脉冲对消后方差变为2倍,通过三脉冲后变为6倍。阶数越高,杂波压得越狠,噪声也抬得越高。三脉冲适合强地杂波且目标速度较高的车载场景;室内弱杂波场景反而两脉冲更稳妥。
3.3 处理链位置与C代码实现
MTI放在距离FFT之后、多普勒FFT之前。原因有两个。距离FFT之后每个bin对应一个距离单元,跨chirp做差分针对的是某个距离上的静态反射,物理意义清晰;如果提前到ADC域做差分,不同距离的回波混叠在一起,根本减不干净。放到多普勒FFT之后再做,那就成了频域置零,失去了对幅度响应曲线的控制。
下面是两脉冲MTI的C语言实现。数据在距离FFT之后是复数,按"chirp优先、距离bin次之"的方式排布。
// 两脉冲MTI对消 // data_in[chirp * num_range_bins + range_bin] 为复数 // 输出写入 data_out,chirp从0开始 for (int range_bin = 0; range_bin < num_range_bins; range_bin++) { for (int chirp = 1; chirp < num_chirps; chirp++) { int idx_cur = chirp * num_range_bins + range_bin; int idx_prev = (chirp - 1) * num_range_bins + range_bin; data_out[idx_cur].re = data_in[idx_cur].re - data_in[idx_prev].re; data_out[idx_cur].im = data_in[idx_cur].im - data_in[idx_prev].im; } // 第一个chirp没有prev,直接置零 int idx_first = range_bin; data_out[idx_first].re = 0.0f; data_out[idx_first].im = 0.0f; }三脉冲对消的代码几乎一样,只是差分系数变成1、-2、1:
// 三脉冲MTI对消 for (int range_bin = 0; range_bin < num_range_bins; range_bin++) { for (int chirp = 2; chirp < num_chirps; chirp++) { int idx_cur = chirp * num_range_bins + range_bin; int idx_prev1 = (chirp - 1) * num_range_bins + range_bin; int idx_prev2 = (chirp - 2) * num_range_bins + range_bin; data_out[idx_cur].re = data_in[idx_cur].re - 2.0f * data_in[idx_prev1].re + data_in[idx_prev2].re; data_out[idx_cur].im = data_in[idx_cur].im - 2.0f * data_in[idx_prev1].im + data_in[idx_prev2].im; } // 前两个chirp没有足够的参考,置零 data_out[range_bin].re = 0.0f; data_out[range_bin].im = 0.0f; data_out[num_range_bins + range_bin].re = 0.0f; data_out[num_range_bins + range_bin].im = 0.0f; }第一、二个chirp的输出置零会损失少量积累,但chirp数达到128时损失不到1.6%,对多普勒FFT输出几乎无影响。
4. 实测躲不开的四个坑:泄漏、误伤、吞人、折叠
4.1 坑一:多普勒置零为什么不彻底——频谱泄漏
如果跳过MTI,直接在2D-FFT后的距离-多普勒谱上把零速度bin清零,会发现静态目标在邻近几个bin还有明显残留。这是窗函数旁瓣泄漏造成的。
矩形窗第一旁瓣只有-13dB,静态目标强度足够高时,旁瓣照样压过检测门限。解决方法是加窗,汉明窗或汉宁窗可以把旁瓣压到-40dB左右,残留范围显著缩小。但加窗会降低多普勒分辨率,本来能分开的两个低速目标可能就糊在一起。所以多普勒置零适合快速验证,不太适合对低速分辨有要求的工程实现。
4.2 坑二:低速目标被凹口误伤——PRT像天平两端
上文已经算过,PRT=100us、波长3.9mm时,2.24m/s以下的目标在两脉冲MTI后增益都低于-3dB,0.25m/s目标被压掉22dB。如果做室内人体活动检测,这个结果会让检测率惨不忍睹。
要保留更低速度的目标,可以增大PRT,但代价是最大无模糊速度 Vmax = λ/(4·PRT) 下降。PRT=1ms时,凹口-3dB点降到约0.22m/s,但Vmax只有0.975m/s,目标再快一点就会出现多普勒模糊。所以选PRT是一场天平游戏:一头是低速保留能力,另一头是最大可测速度。产品需求里必须明确最小可检测速度,再反推PRT和MTI阶数,不能拍脑袋定。
4.3 坑三:背景相消把"静止的人"吞掉——存在性检测要另想办法
这是我在人体存在雷达项目里踩得最深的坑。背景相消对瞬时运动目标几乎没有杀伤力,效果很好,但人一旦在工位上坐几分钟不动,就会被指数加权背景模型逐步吸收,从前景里消失。
对"判断房间有没有人"这种需求,静态目标去除的"静态"定义需要重新画:墙和家具是真正要去的静态目标,而坐姿或睡姿的人虽然宏观静止,但呼吸带来的胸腔起伏会产生毫米级的微动,回波相位以呼吸频率(0.2~0.5Hz)周期变化。处理的关键不是把零速度整个挖掉,而是区分"完全不变的分量"和"带微动的准静止分量"。做法上,可以对慢时间序列做带通滤波,保留下呼吸频率附近的微弱调制,而不是简单的高通差分。把人当墙滤掉,正是智能存在检测误判"无人"的典型原因。
4.4 坑四:相位噪声与速度折叠
静态目标回波在理想情况下恒定不变,但实际雷达存在相位噪声,近距离强反射体的相位噪声会把能量扩散到周围多普勒bin,MTI也只能压掉一部分,零速度附近仍然有残余底噪。处理办法是优化锁相环配置降低相位噪声,或者在MTI之后再做一次频域杂波图抑制。
另一个容易被忽略的问题是速度折叠。高速目标的多普勒频率超过PRF/2时,会折叠到低速区,正好落进MTI凹口里被误杀。解决思路是先解速度模糊,再做静态抑制;或者在雷达参数设计阶段保证目标最大速度在无模糊范围之内。工程上见过不少系统用了MTI之后突然丢目标,查到最后都是速度折叠搞的鬼。
5. 去除静态目标之后的下游算法与效果验证
5.1 CFAR与测距测速的变化
MTI之后,距离-多普勒谱的零速度带不再是一条亮线,CFAR参考窗的均值恢复正常,检测门限随之降下来,弱目标更容易被发现。测速方面,速度本身由多普勒峰的位置决定,MTI对目标幅度的衰减不会改变峰位,但由于信噪比下降,峰值估计的抖动会变大。测距不依赖多普勒维,基本不受影响。
需要留意的是MTI对噪声统计特性的改变。两脉冲对消让白噪声功率翻倍,三脉冲变成六倍。噪声底抬高了,CFAR的恒虚警率设计要按处理后的噪声方差重新标定,否则虚警率会比理论值高不少。
5.2 点云、跟踪与深度学习检测的衔接
4D毫米波雷达输出点云时,很多方案会在点云级再做一次速度门限,把速度绝对值接近零且位置长时间不变的点剔除,作为信号级MTI的兜底。如果前面已经做了MTI,这个门限可以设得很小,只清理漏网的杂点。跟踪器侧,静态点去除后航迹起始率会明显改善,匈牙利数据关联的歧义也少了。
给深度学习检测模型喂点云时,静态点大量存在会让标注语义混乱:同一点云在A帧是背景,在B帧又出现在目标框里,训练出来的特征会互相打架。所以模型输入的预处理链路里,静态目标去除是必须前置的步骤。
5.3 可复现的定量评估方法
静态目标去除到底有没有生效,我习惯用三组指标来验证。
第一,静态点数量占比。固定机位采集一段场景,处理前90%以上的点云来自静止物体,处理后应降到个位数。第二,目标对比度。取目标所在的距离-多普勒bin,算目标幅度与周围噪声底之比。两脉冲MTI处理前后,这个对比度在目标速度远离凹口时通常能提升几dB到十几dB。第三,虚警率。跑一段无人的固定场景,统计CFAR输出点数量,MTI之后应大幅下降。
这三组数据建议固化到自动化测试里。每次改参数、换场景都跑一遍,能快速暴露口碑问题,比人工盯点云图像靠谱得多。
我现在的习惯是在代码库里固定一个"静态目标去除"独立模块,默认两脉冲MTI,参数表里放PRT、最小目标速度、噪声底估计。每次换场景,先画出幅度响应曲线,确认低速目标增益在可接受范围,再决定是继续用两脉冲、换成三脉冲,还是改用均值相消加背景更新的组合方案。代码本身只有几十行,真正花时间的永远是参数和场景的匹配。最后再提醒一句:静态目标去除不是越强越好,它能去掉墙,也能去掉一个坐了十分钟的活人。先搞清楚你的应用到底要检测什么,再决定对"静止"下多重的手。
本文还有配套的精品资源,点击获取