ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

FMCW雷达设计代码与MATLAB信号处理实战:从原始IQ到距离-多普勒图

FMCW雷达设计代码与MATLAB信号处理实战:从原始IQ到距离-多普勒图

简介:这份资源面向雷达信号处理初学者与从事雷达系统设计的工程师,提供FMCW雷达从原理到MATLAB实现的完整学习材料,帮助理解线性调频连续波在测距、测速与角度估计中的工作机制。压缩包共16个文件,约7.95MB,包含5个m脚本、5个wsv波形数据、2份pdf文档,以及mdl仿真模型、pptx讲稿、db与zip等辅助文件,覆盖调频信号生成、回波加噪、混频、FFT频差分析、距离速度估计等关键环节,并配有实测雷达波形数据便于对照验证。资源中既有可运行的信号处理脚本,也有模糊函数、脉冲压缩与拉伸处理等算法示例,适合边读理论边跑代码,加深对多普勒效应与滑窗技术的理解。目前已有3462人学习下载,可作为课程设计、科研入门与工程调试的参考素材。

1. 从一段拍脑袋的雷达回波说起:FMCW 信号处理到底在算什么

很多人第一次接触 FMCW 雷达,是被一段“看起来像噪声”的 IQ 数据劝退的。硬件给回来的是一串复数采样,横轴是时间,纵轴是幅度,既没有距离,也没有速度,更谈不上目标。可只要把这串数据按 chirp 切开、做一次距离维 FFT,再沿 chirp 维做一次多普勒 FFT,距离和速度就自己冒出来了。这就是 FMCW 雷达信号处理最核心的一条链路,也是“FMCW 雷达设计代码、fmcw 雷达信号处理、matlab”这个标题真正要解决的问题:用 MATLAB 把从原始中频采样到距离-多普勒图的整条链路跑通,并且知道每个参数为什么这么设。

它适合两类人:一类是刚拿到毫米波雷达开发板、想先把算法验证清楚的嵌入式工程师;另一类是做感知算法、需要自己造距离-多普勒数据做检测和跟踪验证的研究生。硬件平台可以是 AWR2243 这类带原始数据输出的雷达,也可以是自己用 MATLAB 仿出来的回波。核心不在板子,而在你能不能把发射波形、混频、采样、FFT 这几步的数学关系对上号。下面我按自己搭链路的顺序,把设计代码、信号处理和 MATLAB 实现拆开讲。

2. FMCW 波形与中频信号的数学关系:先把参数定死再写代码

2.1 从 chirp 斜率到拍频:距离是怎么被编码进频率的

FMCW 雷达发射的是频率随时间线性变化的连续波,一个 chirp 内频率从 f0 扫到 f0+B,扫频时间 Tc,斜率 S=B/Tc。目标距离 R 处回波延迟 τ=2R/c,混频后得到的中频信号频率就是拍频 f_b=S·τ=2SR/c。这一步是整个信号处理的根:距离信息被搬到了频率上,所以后面做距离维 FFT,峰值位置对应的频率乘 c/(2S) 就是距离。

速度则靠多个 chirp 之间的相位变化。相邻 chirp 同一距离单元上的相位差 Δφ=4π·v·Tc/λ,所以沿 chirp 维做 FFT,峰值位置对应多普勒频率,再换算成速度。这里有个容易翻车的点:最大不模糊速度由 chirp 间隔决定,v_max=λ/(4Tc),而最大不模糊距离由采样率决定,R_max=c·fs/(2S)。这两个约束是互相拉扯的,Tc 调大速度范围变小,S 调大距离范围变小,参数必须在需求里先平衡好。

我一般会先写一个参数结构体,把所有量集中管理,避免后面改一个参数到处对不上。下面这段是典型的 77GHz 毫米波雷达参数设定,数值按常见车载短距场景取。

% FMCW 雷达系统参数(77GHz 毫米波,短距场景) c = 3e8; % 光速 m/s fc = 77e9; % 载频 77GHz lambda = c / fc; % 波长 B = 1e9; % 扫频带宽 1GHz Tc = 50e-6; % 单个 chirp 扫频时间 50us S = B / Tc; % 调频斜率 Hz/s fs = 10e6; % 中频采样率 10MHz Nc = 128; % 每帧 chirp 数 Ns = 256; % 每 chirp 采样点数 R_max = c * fs / (2 * S); % 最大不模糊距离 v_max = lambda / (4 * Tc); % 最大不模糊速度 dR = c / (2 * B); % 距离分辨率 dv = lambda / (2 * Nc * Tc); % 速度分辨率 fprintf('R_max=%.1fm, v_max=%.1fm/s, dR=%.3fm, dv=%.3fm/s\n', ... R_max, v_max, dR, dv);

这段代码的关键不是语法,而是四个派生量:R_max、v_max、dR、dv。它们决定了你这套雷达能不能覆盖目标场景。比如 dR=c/(2B),1GHz 带宽对应 15cm 距离分辨率,想分辨更近的两个目标就得加带宽。dv 和观测时间 Nc·Tc 成反比,128 个 chirp、50us 间隔只有 6.4ms 观测窗,速度分辨率约 0.6m/s,做行人微动就偏粗。参数说明里我最常提醒的是:S 不要盲目取大,斜率越大同样距离的拍频越高,超过 fs/2 就混叠,R_max 反而缩水。

2.2 用 MATLAB 生成回波:把多目标叠加写清楚

参数定完,先别急着上板子,用 MATLAB 造一段可控回波验证链路。造回波的好处是目标距离、速度、角度你都知道真值,后面 FFT 出来的峰对不对一眼就能判断。下面这段生成两个目标的中频信号,包含距离延迟和 chirp 间多普勒相位。

% 生成多目标中频回波(复数 IQ) targets = [ 30, 5; % [距离(m), 速度(m/s)] 60, -8 ]; Nt = size(targets, 1); t = (0:Ns-1) / fs; % 单 chirp 内采样时间轴 if_sig = zeros(Ns, Nc); % 中频数据矩阵:行=快时间,列=慢时间 for k = 1:Nc for i = 1:Nt R = targets(i,1); v = targets(i,2); tau = 2 * R / c; % 往返延迟 fb = S * tau; % 拍频 % 距离项相位 + 慢时间多普勒项相位 phase = 2*pi*fb*t + 4*pi*v*(k-1)*Tc/lambda; if_sig(:,k) = if_sig(:,k) + exp(1j*phase).'; end end % 叠加复高斯噪声,模拟接收机热噪声 if_sig = if_sig + 0.05*(randn(size(if_sig)) + 1j*randn(size(if_sig)));

逻辑上,快时间 t 承载距离拍频,慢时间索引 (k-1)·Tc 承载多普勒相位,两者相加就是完整的回波相位模型。参数上 targets 每行是距离和速度,噪声系数 0.05 控制信噪比,调大就能看到底噪抬高、弱目标被淹没。这里有个细节:多普勒相位用的是 4πv(k-1)Tc/λ,系数是 4π 不是 2π,因为往返双程。我见过不少人写成 2π,结果速度算出来差一倍,这种错在仿真里不报错,上板子才发现,属于典型血泪经验。

3. 距离-多普勒处理链:两次 FFT 怎么摆、窗怎么加

3.1 距离维 FFT:加窗、补零与峰值提取

拿到 if_sig 后,第一步沿快时间维做 FFT。直接 FFT 会有频谱泄漏,强目标的旁瓣会盖住邻近弱目标,所以先加窗。汉明窗是通用选择,主瓣稍宽但旁瓣低;如果两个目标很近、更看重分辨率,可以用矩形窗但接受高旁瓣。补零不增加真实分辨率,只是让峰值位置插值更细,做峰值提取时方便。

% 距离维处理 win_r = hamming(Ns); % 距离维窗 sig_win = if_sig .* win_r; % 按列乘窗 Nfft_r = 2^nextpow2(Ns) * 4; % 补零到 4 倍,细化峰值 range_fft = fft(sig_win, Nfft_r, 1); % 沿第 1 维(快时间)FFT range_fft = range_fft(1:Nfft_r/2, :);% 取正频率半轴 % 频率轴换算成距离轴 f_axis = (0:Nfft_r/2-1) * fs / Nfft_r; r_axis = c * f_axis / (2 * S); % 在第一个 chirp 上找峰值验证 [~, idx] = max(abs(range_fft(:,1))); fprintf('检测距离 = %.2f m\n', r_axis(idx));

这段里 Nfft_r 取 4 倍补零,是为了让 r_axis 足够密,峰值索引换算距离更准。窗函数按列乘,因为每一列是一个 chirp。取半轴是因为实信号混频后虽是复数,但正频率对应正距离,负频率是镜像。跑完应该看到 30m 和 60m 附近两个峰,如果只有一个峰,多半是两个目标幅度相同、补零不够导致峰融合,或者窗选得太宽。参数上 fs 和 S 必须和生成回波时一致,否则 r_axis 整体偏移,这是最常见的对不上号问题。

3.2 多普勒维 FFT:速度符号与静止目标处理

距离维做完,矩阵每一列是同一 chirp 的距离谱,每一行是同一距离单元随 chirp 的变化。沿第 2 维做 FFT 就得到多普勒谱。这里要注意 fftshift,把零频搬到中心,否则正负速度分居两端,看图很别扭。速度符号约定也要统一:目标靠近雷达,距离减小,多普勒为正还是负,取决于你的相位模型,仿真和实测必须用同一套约定。

% 多普勒维处理 win_d = hamming(Nc).'; % 慢时间窗,转成行向量 rd_win = range_fft .* win_d; % 按行乘窗 Nfft_d = 2^nextpow2(Nc) * 4; rd_map = fft(rd_win, Nfft_d, 2); % 沿第 2 维(慢时间)FFT rd_map = fftshift(rd_map, 2); % 零频移到中心 % 速度轴 v_axis = (-Nfft_d/2:Nfft_d/2-1) * (lambda / (2 * Nc * Tc)) / (Nfft_d/Nc); % 画距离-多普勒图 imagesc(r_axis, v_axis, 20*log10(abs(rd_map))); xlabel('距离 (m)'); ylabel('速度 (m/s)'); axis xy; colorbar; title('距离-多普勒图');

rd_map 就是最终的距离-多普勒图,横轴距离、纵轴速度,两个目标应该落在 (30,5) 和 (60,-8) 附近。v_axis 的换算里除了 Nfft_d 是因为补零后频率格点变密,除以 (Nfft_d/Nc) 把格点还原到真实多普勒频率。如果速度符号反了,把 v_axis 取负或者调整相位模型即可,但一定要在仿真阶段定死。静止目标会落在零多普勒线上,实际场景里杂波也在这条线上,所以后面做动目标检测时,零频附近通常要挖掉或做 MTI 对消。

3.3 从 RD 图到检测:CFAR 为什么不能照搬一维

RD 图出来只是中间结果,真正要输出目标列表还得做恒虚警检测。一维 CFAR 在距离维滑窗,二维 CFAR 要在距离和多普勒两个方向同时估计噪声。常见做法是 CA-CFAR,取待检测单元周围的参考单元求平均,乘门限因子。门限因子由虚警概率决定,虚警率越低门限越高,弱目标越容易漏。二维 CFAR 的参考窗不能包含待检测单元和邻近的保护单元,否则目标能量泄漏进噪声估计,门限被抬高,自己把自己检没了。

% 简化二维 CA-CFAR(示意,工程需加保护单元) function det = ca_cfar_2d(rd_map, guard_r, guard_d, ref_r, ref_d, alpha) [Nr, Nd] = size(rd_map); mag = abs(rd_map); det = zeros(Nr, Nd); for i = 1+ref_r+guard_r : Nr-ref_r-guard_r for j = 1+ref_d+guard_d : Nd-ref_d-guard_d % 取参考窗内除保护单元外的均值 win = mag(i-ref_r-guard_r:i+ref_r+guard_r, ... j-ref_d-guard_d:j+ref_d+guard_d); win(ref_r+1:ref_r+2*guard_r+1, ref_d+1:ref_d+2*guard_d+1) = []; noise = mean(win(:)); if mag(i,j) > alpha * noise det(i,j) = 1; end end end end

alpha 是门限因子,由目标虚警概率 Pfa 和参考单元数 N 决定,近似 alpha=N·(Pfa^(-1/N)-1)。参考单元越多,噪声估计越稳但计算越慢,且要求场景均匀。实际道路场景杂波非均匀,CA-CFAR 在杂波边缘会虚警飙升,这时候要换 OS-CFAR 或 GO-CFAR。我一般先在仿真数据上把 alpha 调到虚警可控,再上实测,直接拿实测调参很容易被非均匀杂波带偏。

4. 避坑与排查:FMCW 信号处理里最容易翻车的五件事

4.1 距离峰对不上:先查斜率和采样率是否一致

现象是仿真里目标在 30m,实测或换一组参数后峰跑到 45m。原因几乎都是 S 或 fs 在生成和处理两处不一致,或者把 B/Tc 算错。解决方法是把 S、fs、c 三个量写进同一个配置结构体,生成和处理都从这个结构体取,禁止手抄。另外确认拍频没有超过 fs/2,超过就是混叠,峰位置会折叠到错误距离。

4.2 速度符号反了:相位模型和 fftshift 要一起看

现象是目标实际远离,RD 图上却显示靠近。原因是多普勒相位用了 2π 而非 4π,或者 fftshift 后速度轴方向没跟着调整。解决方法是固定一套约定:以 4πvTc/λ 建模,fftshift 后正速度在中心上方,实测对比一次就能确认。这个坑不报错,只出错结果,属于最隐蔽的一类。

4.3 弱目标被强目标旁瓣淹没:窗和动态范围要一起调

现象是近距离强目标旁边的小目标检不出来。原因是没加窗或窗旁瓣太高,强目标泄漏盖住弱目标。解决方法是距离维和多普勒维都加汉明窗,必要时用泰勒窗压旁瓣,同时检查接收链路增益是否饱和,饱和会制造虚假谐波。仿真里把强弱目标幅度比设到 40dB 以上,能提前暴露这个问题。

4.4 CFAR 虚警爆炸:参考窗撞上杂波边缘

现象是杂波边缘或目标密集区虚警成片。原因是 CA-CFAR 假设参考窗内噪声均匀,边缘处一半是杂波一半是噪声,均值被拉低。解决方法是换 GO-CFAR 取两侧较大均值,或 OS-CFAR 取有序统计量,再不行就缩小参考窗并加保护单元。工程上还会先做一次杂波图估计,把静态杂波先扣掉再检测。

4.5 补零当分辨率用:FFT 点数不等于真实分辨能力

现象是补零到 8 倍后以为能分辨更近的两个目标,结果还是糊在一起。原因是补零只在频域插值,真实分辨率由带宽和观测时间决定,补零不会创造新信息。解决方法是需要更高距离分辨率就加带宽 B,需要更高速度分辨率就加 chirp 数或观测时间,补零只用于峰值定位细化。把这点想清楚,参数设计就不会走偏。

5. 进阶技巧:用 MATLAB 把仿真和实测数据接进同一条处理链

真正让这套代码有价值的,是它能同时吃仿真数据和实测数据。我习惯把处理链写成一个函数,输入是 IQ 矩阵加配置结构体,输出是 RD 图和检测点,仿真和实测只是数据来源不同。这样调参一次,两边通用。下面这个函数骨架是我常用的组织方式。

function [rd_map, r_axis, v_axis, det] = fmcw_process(iq, cfg) % iq: [Ns x Nc] 复数中频数据 % cfg: 含 c, S, fs, Tc, lambda, Nc, Ns 等字段 win_r = hamming(cfg.Ns); Nfft_r = 2^nextpow2(cfg.Ns) * 4; range_fft = fft(iq .* win_r, Nfft_r, 1); range_fft = range_fft(1:Nfft_r/2, :); f_axis = (0:Nfft_r/2-1) * cfg.fs / Nfft_r; r_axis = cfg.c * f_axis / (2 * cfg.S); win_d = hamming(cfg.Nc).'; Nfft_d = 2^nextpow2(cfg.Nc) * 4; rd_map = fft(range_fft .* win_d, Nfft_d, 2); rd_map = fftshift(rd_map, 2); v_axis = (-Nfft_d/2:Nfft_d/2-1) * ... (cfg.lambda / (2 * cfg.Nc * cfg.Tc)) / (Nfft_d / cfg.Nc); det = ca_cfar_2d(rd_map, 2, 2, 8, 8, 12); % 门限按虚警率标定 end

这个骨架的好处是配置和算法分离,换雷达平台只改 cfg,算法不动。实测数据接入时,唯一要额外做的是把板子输出的原始数据按 [Ns x Nc] 重排,很多平台给的是交错或分帧格式,重排错了 RD 图会一片糊。我一般先拿一个已知角反或静止目标验证,确认距离峰位置对得上再跑动目标。

验证方法上,我固定做三步:第一步仿真双目标,确认距离速度都对;第二步加噪声和杂波,看 CFAR 能不能稳住虚警;第三步接一段实测静态场景,看零多普勒线是否干净、有没有镜像假峰。三步都过,这套链路才算能用。参数标定上,alpha 我通常从 10 到 15 之间试,配合实测虚警率微调,不迷信公式算出来的理论值,因为实际噪声分布往往偏离高斯。

最后说个我自己的习惯:每次改参数前先把当前 RD 图存一份,改完对比,不然调着调着就忘了哪组参数最好。FMCW 信号处理参数之间耦合太强,靠脑子记迟早翻车。把仿真和实测接进同一条链、每次改动留痕,是我做这个方向最省后悔药的做法。希望帮到你。

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

RELATED READING

延伸阅读

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