简介:一份面向毫米波雷达信号处理与微多普勒目标识别跟踪的完整工程资料,基于Matlab与Python实现,覆盖从原始回波数据读取、距离-多普勒谱与角度谱生成、恒虚警检测、点云聚类,到微多普勒时频分析、特征提取、分类器训练验证,以及目标跟踪参考实现的端到端流程,适合雷达算法工程师、高校研究生和自动驾驶感知开发者学习或二次开发。压缩包共54个文件,包含22个Matlab源码脚本、5个Python分类器文件、3个雷达实测数据文件、2个交互式笔记,以及配置文件、结果图片和说明文档,整体约50.42MB,目录结构清晰,模块间解耦良好。借助内置数据,读者可以快速复现微多普勒分类与目标检测跟踪实验,深入理解目标测距、测速、测角联合估计的工程化方法,并能够在此基础上方便地扩展自己的识别或跟踪算法。资源上线以来已有2018人浏览学习,是一份从基础理论到工程落地的综合性毫米波雷达感知学习资料。 做毫米波雷达项目的工程师,大概都有过这种被问住的时刻:别人问你在做什么,你说“目标识别和目标跟踪”,对方第一反应是摄像头方案,第二反应是激光雷达方案,很少有人第一时间想到毫米波雷达。但恰恰是这个频段,最近几年在智能座舱、智慧办公、安防监控、机器人感知里冒头得特别快。
这篇文章不是教科书式的原理堆砌,而是把我实际做的一套 77GHz FMCW 毫米波雷达项目,从原始 ADC 数据一路做到目标识别和跟踪的完整链路整理出来。核心正好覆盖五个关键词:毫米波雷达、信号处理、微多普勒、目标识别、目标跟踪。适合刚入门的同学理解整条链路长什么样,也适合已经在写代码的工程师对照查漏,看看自己在哪个环节还缺一块。
1. 这活儿到底是什么:从天线前的电磁波到屏幕上的目标轨迹
1.1 你得先知道毫米波雷达是怎么看世界的
毫米波雷达用的是频率在 24GHz、60GHz、77GHz 附近的电磁波。77GHz 的波长只有 3.9 毫米左右,波长短带来两个直接好处:天线尺寸可以做得很小,所以单颗芯片能集成 4 发 4 收甚至更多的收发通道;频带资源丰富,毫米波雷达通常有 GHz 级别的扫频带宽,距离分辨率能做得很高。
工程上最常用的是 FMCW(调频连续波)体制。雷达发射一个频率线性扫过的 chirp,信号碰到目标后反射回来,接收机把回波和本振信号混频,得到一个差频信号,这个差频的频率就直接对应目标的距离。整个过程本质上就是把“电磁波飞行的微小时延”变成了“一个容易测量的频率”。
这里有个容易被新手忽略的点:FMCW 雷达测的是差频,而不是直接测飞行时间。所以距离分辨率取决于扫频带宽,而不是时间测量精度。带宽 1GHz 时,距离分辨率大概是 15cm,这个能力放到人体存在感知、手势识别、车内儿童存在检测这些场景里,已经非常够用。
1.2 整条链路的四个环节,缺一块都跑不通
一个完整的毫米波雷达目标识别与跟踪项目,绝对不是“算法跑通”那么简单。从天线收到电磁波到屏幕上输出“目标 A 是人、正在以 1.2m/s 速度移动”,中间要经过四层处理:
- 信号处理:原始 IQ 数据 -> 距离-多普勒图 -> CFAR 检测 -> 点云输出。
- 特征提取:从点云里提微多普勒特征、RCS 特征、尺寸和速度特征。
- 目标识别:分类器判断目标是行人、车辆、无人机还是静态物体。
- 目标跟踪:把识别结果和点迹关联起来,用滤波算法形成稳定的轨迹。
这四个环节里,信号处理决定你“能不能看见”,特征提取和识别决定你“能不能认出来”,跟踪决定你“能不能持续盯住”。很多项目死在第一步——点云质量太差,后面算法再强也白搭。我见过有人花大力气调一个深度学习识别模型,最后发现问题出在前端 CFAR 门限设错了,目标点云碎成一地。所以下面我把每一步的关键操作和取舍都讲清楚。
2. 信号处理:把原始 IQ 数据变成点云的那几步
2.1 距离维 FFT 和速度维 FFT:先把“在哪”和“多快”算出来
信号处理的起点是一帧 chirp 的原始 ADC 数据。假设一帧内有 128 个 chirp,每个 chirp 采样 256 个点,那原始数据就是一个 128×256 的复数矩阵。这个矩阵的行叫快时间维,列叫慢时间维。
第一步对快时间维做 FFT,叫做距离维 FFT。因为差频频率和目标距离成正比,所以 FFT 之后每个 bin 就对应一个距离单元。一个典型参数计算过程是这样的:扫频带宽 B=1GHz,距离分辨率 d_res = c/(2B) ≈ 0.15m;ADC 采样率 fs=10MHz,chirp 时长大约 50us,最大可测距离由中频带宽决定,大约能覆盖几十米范围。实测下来,室内场景这个配置能稳定看到 15~20 米的行人。
第二步对慢时间维做 FFT,叫速度维 FFT。多普勒效应会让不同速度目标的相位在 chirp 之间发生变化,所以对同一距离 bin 沿着慢时间做 FFT,就能算出速度。速度分辨率取决于一帧的总时长,最大不模糊速度取决于 chirp 周期。比如 chirp 周期 Tc = 80us,波长 λ ≈ 3.9mm,那么最大不模糊速度 v_max = λ/(4×Tc) ≈ 12.2m/s,这个量级覆盖行人绰绰有余,但要测高速公路上的对向来车就会模糊。
做完两步 FFT,得到的数据叫距离-多普勒图(Range-Doppler Map,RDM)。这张图是后面所有处理的“母体”,也是我花最多时间调试的地方。一个常见经验是:雷达参数配置不能只看单指标,距离分辨率和最大速度是互相牵制的,改带宽、改 chirp 周期必须一起重新推一遍。
2.2 DOA 角度估计:决定目标空间位置的最后一公里
距离和多普勒知道了,目标在极坐标下的半径和径向速度有了,但还差一个角度。角度估计靠的是多根接收天线之间的相位差。天线间距做成半个波长时,不同方向来的回波会在不同天线上产生线性变化的相位差,对这个相位差再做一次 FFT,就是常用的角度维 FFT(Ang-FFT)。
Ang-FFT 的好处是计算量小,适合嵌入式实时处理。但有个硬伤:角度分辨率受限于天线数量,4 根天线在 ±60° 范围内大概只能分出几十度的角度分辨能力。想更好,就得用超分辨算法,比如 MUSIC 或 ESPRIT。
我在项目里试过用 MUSIC 做人体的角度估计,精度确实比 Ang-FFT 高一截,尤其是两个目标距离很近的时候。但这玩意儿对通道幅相一致性特别敏感,阵列校准稍微差一点,谱峰就歪了。所以工程上要是天线数量够,优先用 Ang-FFT;只有在多目标分辨确实不够时再上超分辨,而且一定要做好幅度和相位校准。
距离维 FFT、速度维 FFT、角度维 FFT 都做完后,数据就是一个三维的 Range-Angle-Doppler 立方体。但这时候还不能直接用,因为里面全是噪声和杂波。
2.3 CFAR 检测:别让噪声把真实目标给埋了
我见过不少新手拿到 RDM 图,一眼看到亮斑就说“目标在这里”,但真要在实时系统里做,必须用一个自动门限把目标从噪声里捞出来。这个门限不能用固定值,因为不同距离、不同角度网格的噪声底不一样,远距离噪声大,近距离地面杂波强,固定门限总会在某个区域误报或者漏检。
工程上最常用的是 CA-CFAR(单元平均恒虚警检测)。原理很简单:在待检测单元周围开一个保护窗和一个参考窗,用参考窗里的平均功率估计当前噪声底,再乘以一个门限系数,超过就判为目标。保护窗是为了防止目标自身能量泄漏到参考窗里,把噪声底抬高了。
有两个参数直接影响检测质量:参考窗长度和门限系数。参考窗太小,噪声估计不稳;参考窗太大,会把邻近目标也算进噪声底。门限系数设高了漏检,设低了虚警多。我的经验是先拿一段干净场景的底噪数据,统计噪声分布,然后反推虚警率,再定门限系数。这比拍脑袋调要靠谱得多。
CFAR 之后得到的是一个个“点”,包含距离、角度、多普勒速度、RCS 等信息。到这一步,才算真正从电磁波变成了点云数据。
3. 微多普勒:识别目标身份的关键指纹
3.1 微多普勒效应到底是什么
目标整体移动会产生一个多普勒频移,这是大家都知道的。但如果目标内部还有旋转、摆动、振动,比如人走路时手臂和腿的摆动、无人机旋翼的旋转、车轮的转动,这些部件会在这个主多普勒频移周围产生额外的频率调制,这就是微多普勒效应。
用人来举例:躯干贡献一个相对稳定的多普勒频移,速度多少就偏多少;腿和手臂周期性摆动,相当于在躯干速度上叠加了一个周期变化的微动速度,反映在频谱上就是主峰旁边出现周期性的旁瓣和展宽。也就是说,人走路的微多普勒谱图是带着“呼吸感”的,像一条花边,而一个匀速直线运动的刚体目标,频谱就是干干净净一条线。
这个差异非常有用。摄像头在黑暗里认不出人,激光雷达在烟雾中看不清轮廓,但微多普勒特征不依赖光学图像,只要目标在动,它就在那里。通过提取这些特征,我们可以做到:区分人和动物、区分行人和车辆、识别无人机和飞鸟。这就是微多普勒在目标识别里的核心价值。
3.2 怎么提取微多普勒特征:STFT 和时频谱图
微多普勒的频移非常小,而且随时间变化,只看一帧的 RDM 很难看清。正确做法是把目标所在距离单元的多普勒谱按时间连续堆叠,形成一个“时间-多普勒”二维图,再对这个序列做短时傅里叶变换(STFT),得到时频图。
我项目里的做法是:先通过 CFAR 锁定目标所在距离单元,然后连续取 256 帧这个单元的多普勒复数值,组成一个慢时间序列,再做 STFT。窗函数用汉明窗,窗口长度 64,重叠率 50%。这样得到的分辨率既能看清腿摆动的节律,又不会因为窗口太长把瞬态信息抹平。
import numpy as np from scipy.signal import stft # complex_doppler_series: 目标距离单元上连续256帧的复数多普勒值 # frame_time: 单帧时间,比如 50ms,对应帧率 20Hz f, t, Zxx = stft( complex_doppler_series, fs=1 / frame_time, window='hann', nperseg=64, noverlap=32, return_onesided=True ) # Zxx 的幅度谱就是微多普勒时频图 spectrogram = np.abs(Zxx)注意这里有个细节:只取 CFAR 检测出的目标距离单元,而不是整张 RDM 都做 STFT。因为微多普勒信号本身很弱,整张图做 STFT 计算量巨大,而且目标距离单元之外的噪声会把特征淹没。
这里还有个真机上特别容易踩的坑:人的微多普勒频移量级很小,大约几赫兹到几十赫兹。如果雷达的速度分辨率不够(比如一帧总时长太短),这些特征直接被抹掉了。我最初把一帧设成 12.8ms,测试时发现微多普勒谱图几乎看不出人的摆动特征,后来把一帧的 chirp 数提高到 256,帧时长拉到 30ms 以上,特征才清晰可见。
3.3 一个能落地的识别流程实例
拿到时频图后,下一步是特征提取和分类。我的做法不是直接上深度学习,而是先提取一组可解释的统计特征:
| 特征 | 对应物理含义 | 典型判断逻辑 |
|---|---|---|
| 躯干多普勒频率 | 目标整体径向速度 | 行人通常 < 3m/s,车辆可以 > 10m/s |
| 微动频率 | 肢体摆动或旋翼旋转频率 | 人约 1~2Hz,无人机旋翼可达 100Hz 以上 |
| 多普勒展宽带宽 | 微动部件速度变化范围 | 行人展宽大且有周期性,刚体目标展宽小 |
| 谱图熵 | 时频图复杂度 | 人体摆动熵值高,匀速刚体熵值低 |
特征提取后我用随机森林做分类,训练数据来自实验室采集的各类目标微多普勒谱图。实测在嵌入式平台上,随机森林比轻量 CNN 更稳:一是训练不需要大量数据,几千条样本就能收敛;二是推理快,单帧特征只有几十维;三是特征可解释,调试时能直观看到是哪个特征把人和无人机分开的。
当然,如果你有丰富的 GPU 资源,用 CNN 直接对时频图分类效果会更好。但要注意,模型再强也弥补不了微多普勒特征的物理缺陷——如果目标径向速度为零(比如人正对着雷达走来),微多普勒特征会大幅减弱。这种场景必须配合其他特征源一起用,不能单靠微多普勒一条路走到黑。
4. 目标跟踪:让“瞬时的点”变成“可信的轨迹”
4.1 点迹关联:每个新点该找哪个“老熟人”认亲
检测得到的是每一帧的点迹,但一个目标在一帧里可能产生多个点,不同帧之间也可能出现短暂漏检。目标跟踪要解决的核心问题,就是把这些离散点串成一条平滑、稳定、可预测的轨迹。
第一步是聚类。毫米波雷达的点云不像激光雷达那么密,一个行人一帧可能只有 3~10 个点,直接用 DBSCAN 聚类很容易因为密度太低而拆成多个簇。我的经验是先用简单的距离门限法做粗聚类,把距离和角度都接近的点归到一堆,再用类似于加权中心的算法算出目标位置。这样比上来就调 DBSCAN 的 min_samples 参数要直观得多。
第二步是帧间关联。常用的是全局最近邻(GNN)算法,把上一帧的预测位置和这一帧的检测点做一个代价矩阵,用匈牙利算法找最优匹配。还有个更简单但在室内场景足够好用的方法:只关注距离门限内的候选点,选距离最近的那个作为关联结果。如果目标速度变化不大、目标数量在几个以内,这个朴素方法实测效果并不差。
4.2 滤波与预测:卡尔曼滤波是地基
关联得到的是带噪声的量测,还需要滤波来平滑。卡尔曼滤波是雷达跟踪的标配,它把目标的状态建模为一个高斯分布,分两步走:预测(利用运动模型推算下一帧状态)和更新(结合新量测修正状态)。
目标跟踪和微多普勒不太一样,微多普勒关心“目标内部怎么动”,跟踪关心“目标整体怎么走”。所以状态向量取 (x, y, vx, vy) 就够用。匀速模型的状态转移矩阵长这样:
import numpy as np dt = 0.05 # 帧间隔 50ms F = np.array([ [1, 0, dt, 0], [0, 1, 0, dt], [0, 0, 1, 0], [0, 0, 0, 1], ]) # 观测模型:雷达直接量测的是距离、角度和径向速度 # 如果直接用笛卡尔坐标量测,H就是简单的选择矩阵 H = np.array([ [1, 0, 0, 0], [0, 1, 0, 0], ])但这里有个容易踩的坑:雷达量测通常在极坐标系下,距离和角度是量测值,而状态是直角坐标系下的 x、y。从极坐标到直角坐标是非线性变换,所以严格来说要用扩展卡尔曼滤波(EKF),把观测方程在预测点做泰勒展开线性化。我最初图省事直接用线性卡尔曼,角度一大误差就偏得厉害,后来老老实实改成 EKF 才解决。
滤波里的噪声参数 Q(过程噪声)和 R(量测噪声)怎么设,是很多新手最头疼的。我的办法是先采集一段匀速直线运动的目标数据,统计量测残差来标定 R;Q 则根据目标机动幅度来试,目标机动大就调大 Q,让滤波更快跟上机动,代价是轨迹平滑度下降。没有万能参数,只有适合你场景的参数。
4.3 轨迹生命周期管理:别让闪断变成断送
即使有卡尔曼滤波,也不可能做到每帧都检测到目标。所以轨迹不能“检测不到就删”,需要一套生命周期状态机来管理。我用的是经典的三态管理:
- 候选态(Tentative):新目标第一次出现时进入,此时置信度低,可能是虚警。
- 确认态(Confirmed):连续 M 帧都关联成功(比如 M=3),升级为确认轨迹。
- 删除态(Deleted):确认轨迹连续 N 帧无关联(比如 N=5),删除释放 ID。
这套机制看着简单,但对系统稳定性影响非常大。没有候选态,单帧虚警会产生大量假轨迹;没有删除态的延迟,目标被遮挡一两帧就会丢轨迹,后面重新出现时又得重新识别一遍,用户体验非常割裂。
还有一个小技巧:轨迹的 ID 尽量和识别结果绑定。比如一帧里确认了目标 ID=3 是人类,下一帧即使暂时遮挡,也先按轨迹预测位置继续标注“人”,等遮挡结束再重新确认。实测在办公区域的人体存在检测场景下,这个策略让连续跟踪的成功率从 80% 提升到了 95% 以上。
5. 实战中经常踩的坑和排查思路
5.1 指标在仿真里很漂亮,上了真机就拉胯
这是雷达项目里最常见也最无奈的问题。仿真里信噪比设得很理想,检测识别指标都很好看;一上真机,天线罩损耗、安装位置遮挡、射频前端的相位噪声、温度漂移,全部把性能往下拉。
我踩过最典型的一个坑是天线罩。为了外观把雷达装在一个塑料罩后面,结果罩子的介电常数在扫描带宽内不均匀,导致每个距离单元的相位都有偏差,角度估计直接偏了好几度。后来在罩子选型时专门要求供应商提供毫米波频段的介电常数和损耗角数据,安装后用角反校准才把角度误差压下来。
所以我的建议是:从项目第一天就要以最终安装形态来验证信号链路,而不是在实验桌上调好了再换到真实环境。环境一变,所有参数都得重调。
5.2 多径反射和静止杂波
多径是室内雷达的噩梦。真实目标在地下或墙面反射后,会在对称位置形成“鬼影目标”。排查时有个土办法:让一个真人绕着雷达走一圈,记录每个点的位置,那些出现在墙体另一侧、运动高度关联的轨迹,基本都是多径假目标。
我的处理思路是两条:一是用多帧运动一致性校验,真目标和鬼影的运动会呈现出镜像对称关系,建模后可以相互抵消;二是从安装上抑制,雷达安装高度越高,地面反射路径越长,鬼影强度会相对减弱。
静止目标则要分场景处理。做运动检测时静态杂波可以直接滤除,但做人体存在检测时,人的呼吸和微小体动恰恰是关键信号,这时候静止杂波反而要保留。所以杂波滤除不能一刀切,要看你最终要识别什么。我现在的做法是可配置的杂波图,根据场景动态调整滤除强度。
5.3 关于算力分配的几点经验
4D 毫米波雷达一帧点云可能有几千个点,如果每个环节都堆算法,再强的嵌入式芯片也扛不住。我的分配原则是:前端信号处理(FFT、CFAR)尽量用芯片自带的硬件加速器跑,这部分计算量大但规律性强;微多普勒特征提取和随机森林推理用 CPU 跑;跟踪部分计算量小,和识别放同一个核没问题。
计算流程做成流水线也特别重要。不要在一个线程里同步做完 FFT、CFAR、识别、跟踪再输出,而要把每个环节放到独立线程里,用环形缓冲交接数据。这样即使某一帧识别慢了一点,也不会阻塞后面的检测,整体帧率能稳定很多。
最后说一点做完整套项目之后我自己的体会。这套链路里真正难的,不是某一个算法有多高级,而是每个环节都在有意无意地“丢信息”:距离 FFT 丢相位细节,CFAR 门限丢掉弱目标,聚类丢掉点云结构,关联丢掉瞬时观测值。工程上能做好的,就是想清楚每一级该保什么、该丢什么,然后围绕你的应用场景去取舍。想通这一点,比多调几个参数更重要。
本文还有配套的精品资源,点击获取