简介:LFM线性调频(chirp)信号及其匹配滤波器设计这份MATLAB算法资源,面向雷达、通信与信号处理方向的初学者与工程人员,用于理解LFM信号频率随时间线性变化的特性,并通过匹配滤波实现脉冲压缩、提高信噪比与分辨率。压缩包共5个文件,以4个.m脚本为主,覆盖chirp信号生成、匹配滤波实现、卷积辅助处理以及双线性调频(dchirp)信号的生成演示;另含1个txt说明文件,用于补充理论背景或仿真步骤,整个资源包仅1KB,轻量易用。已有276人学习下载,适合通过短小精悍的代码快速掌握雷达信号处理中关键模块的基本流程。具体可获得LFM信号的参数设计方法、基于ifft与conj的匹配滤波实现思路、卷积操作在滤波分析中的应用,以及通过改变频率斜率生成DCHIRP信号的技巧,便于在MATLAB中直接运行与二次开发,提升对脉冲压缩机制的实际感知。
1. LFM 线性调频(chirp)信号是什么:一次扫频为什么能换来 1/B 的主瓣
LFM 线性调频(chirp)信号是雷达、声呐和超声成像里最常见的发射波形,它把能量摊在带宽 B 上扫出去,接收端再用匹配滤波器把能量重新“捏”成一个尖峰。第一次做脉压实验的人,往往盯着输出波形发愣:明明发出去的是个大扫频,回来怎么变成一杆“针”了?这杆针的宽度约等于 1/B,B 越大针越细,距离分辨力越高。这篇笔记就把这件事从表达式、参数选择到 MatLab 实现讲透,送给刚接手雷达信号处理、或者正在仿真脉压链路又不想只看框图的人。
2. 三个决定脉压效果的参数:带宽、时宽与时间带宽积怎么选
2.1 时域表达式里藏着哪些参数:从 10MHz 到 100MHz 带宽怎么定
LFM 信号的复基带表达式只有一行:
s(t) = A * rect(t/T) * exp(1j * pi * K * t^2);其中rect(t/T)表示时宽 T 内的矩形包络,K 是调频斜率,单位 Hz/s。瞬时频率是相位的导数:
f(t) = (1/2π) * d(πKt²)/dt = K*t所以频率从 -B/2 线性扫到 +B/2,B = K*T。这里有个新手常问的点:为什么用复指数exp(jπKt²)而不是cos(2πf0t + πKt²)?因为复基带信号只有单边频谱,后续匹配滤波、抽取都干净;用实信号做脉压,正负频率分量会各出一个峰,属于自找麻烦。我一般直接生成复信号,需要实信号发射时再取实部上变频。
参数选择上,B 由距离分辨力决定,脉压后主瓣宽度约 1/B,对应距离分辨力 ΔR = c/(2B)。比如要 1.5m 分辨力,B ≥ 100MHz;要 15m,B ≥ 10MHz 就够。T 由发射能量和平均功率决定,T 越大能量越足,但在同带宽下 K 越小,多普勒容限也变。工程上先定 B 和 T,K 只是它们的比值,不用单独纠结。
2.2 时间带宽积 TB:为什么说它是 LFM 的“黑匣子开关”
时间带宽积 D = BT 是 LFM 最核心的无量纲参数。匹配滤波的输出峰值幅度正比于 T,而噪声是宽带白噪声,脉压后信噪比提升约 D 倍,也就是 10log10(D) dB。我把常用档位列在下面,方便选型时直接查:
| 时间带宽积 D | 处理增益 | 主瓣宽度 | 适用场景 |
|---|---|---|---|
| 10 | 10 dB | 1/B | 短脉冲超声、低占空比系统 |
| 100 | 20 dB | 1/B | 常规雷达、声呐 |
| 1000 | 30 dB | 1/B | 远程雷达、SAR 成像 |
| 10000+ | 40 dB+ | 1/B | 超远程、深空探测 |
注意主瓣宽度始终是 1/B,D 越大只是把“摊开的能量”收得越狠,尖峰越突出,并不会把主瓣变窄。很多刚入门的人误以为增大 T 能提高分辨力,实际上分辨力只由 B 决定,T 买的是能量和信噪比。选型时先按分辨力定 B,再按作用距离和累积增益定 D,最后 T = D/B 自然落地。
D 还有个隐藏影响:当 D > 100 时,LFM 的多普勒敏感性开始显现,目标有速度时脉压输出会出现时延偏移,偏移量约 Δt = f_d/K。这个效应在 D 很小时可以忽略,D 大了必须做多普勒补偿,否则测距会系统性偏大或偏小。设计链路时我会在仿真里把fd = 0和fd = 5kHz两种工况各跑一遍,对比峰值位置偏移,确认系统容限。
2.3 三角波 LFM 测距:为什么上扫频和下扫频的差拍频率不一样
热词里常出现“三角波线性调频信号测距”,这里多说一句。实际雷达里发射机往往不是单次扫频,而是锯齿波或三角波连续调频。三角波包含上扫频和下扫频两段,每段斜率互为相反数。对静止目标,上下扫频的差拍频率相同,测距公式为:
R = c * f_b / (2 * K)其中 f_b 是差拍频率,由发射信号与回波混频得到。但目标有径向速度时,多普勒频率 f_d 会叠加在差拍频率上:上扫频段测到 f_b - f_d,下扫频段测到 f_b + f_d。把两个差拍频率相加相减,距离和速度就分开了:
R = c * (f_b_up + f_b_down) / (4 * K) v = c * (f_b_down - f_b_up) / (4 * f0)用 MatLab 仿真三角波测距时,我习惯把上扫频和下扫频生成两段 LFM,分别做 FFT 找差拍峰,再按上面对公式配对。需要注意 f_b 的符号,不同文献对上下扫频的定义可能相反,配对前先画一帧时频图确认极性,省得后面算出的速度为负还查半天。这个坑在下面避坑章还会展开。
3. 匹配滤波器基本原理:为什么频域共轭相乘是最优接收机
3.1 从信噪比最大的角度理解匹配滤波
匹配滤波器的思路不是“把信号还原”,而是在白噪声背景下让输出信噪比最大。设接收信号为 r(t) = s(t) + n(t),滤波器冲激响应为 h(t),输出在 t0 时刻的信号瞬时功率与噪声平均功率之比为:
SNR = |∫S(f)H(f)e^(j2πft0)df|² / (N0/2 * ∫|H(f)|²df)由施瓦茨不等式,当 H(f) 正比于 S*(f)e^(-j2πft0) 时 SNR 取最大值。也就是说,匹配滤波器的频率响应是发射信号频谱的共轭,再乘一个线性相位因子。去掉这个相位因子,时域上 h(t) 就是 s(-t) 的共轭——把发射波形在时间上反转再取共轭,作为卷积核。
这里有个很容易绕晕的点:为什么是共轭而不是直接反转?因为 s(t) 通常是复信号,匹配滤波要求的是“相位对齐”,共轭保证在某个时刻所有频率分量同相叠加。若用实信号,共轭等于本身,但复信号下漏掉共轭,输出峰值会消失或者变成色散的一团。写代码时conj是绝对不能省的一步。
3.2 脉压输出为什么是 sinc 形状:主瓣宽度 1/B 的由来
把匹配滤波器作用到 LFM 信号上,输出是信号的自相关函数。对复基带 LFM,自相关可以解析算出来:
y(t) = T * sinc(Bt) * exp(jπKt²) 的包络近似严格说幅度项带一个残余相位项和近似,但工程上关心的包络就是 |sinc(Bt)|。sinc 的第一个零点在 t = ±1/B,所以主瓣宽度按 -3dB 算约 0.886/B,按第一零点算正好 1/B。这也是整篇笔记里最值得记住的数字:带宽 B 的倒数决定压缩后脉冲宽度,和 T 无关。
sinc 的旁瓣第一峰约 -13.2dB,这个值对多数雷达系统都不够。两个靠近的目标,强目标的旁瓣会把弱目标主瓣盖掉,所以实际系统几乎都要加窗。加窗的本质是失配:牺牲一点主瓣宽度和信噪比,换取更低的旁瓣。表里我把常用窗的代价列全,方便做链路预算时直接扣:
| 窗类型 | 第一旁瓣电平 | 主瓣展宽因子 | 信噪比损失 |
|---|---|---|---|
| 矩形 | -13.2 dB | 1.0 | 0 dB |
| Hamming | -42.6 dB | 1.47 | 1.34 dB |
| Hann | -31.5 dB | 1.62 | 1.76 dB |
| Blackman | -58.1 dB | 1.73 | 2.37 dB |
选窗的原则是:距离分辨力余量大就上 Blackman,余量小就 Hamming,追求极限分辨力就矩形。我一般默认 Hamming,它在旁瓣和主瓣之间折中最好。
3.3 从卷积到频域相乘:实现方式的等效前提
时域匹配滤波是 h(t) 与 r(t) 的卷积,直接算复杂度 O(N²)。LFM 的时宽带宽积动辄几百上千,点数一多就必须换频域实现:
Y(f) = R(f) * H(f) = R(f) * conj(S(f)) y(t) = IFFT(Y(f))频域相乘等效于时域卷积,前提是 FFT 点数足够,避免循环卷积的混叠。工程上我取 Nfft ≥ length(s) + length(r) - 1,或者直接Nfft = 2^nextpow2(length(s)),省心且不留坑。频域做法还有个附带好处:窗函数可以直接乘在 H(f) 上,不需要额外做一次时域卷积,后面第 4 章就是按这个路子写的。
4. 用 MatLab 实现 LFM 生成与匹配滤波:从频域共轭相乘到加窗对比
4.1 生成 LFM 信号:参数、采样率与复信号
先定义一组典型参数:时宽 T = 10μs,带宽 B = 10MHz,采样率取 fs = 100MHz。为什么取 100MHz 而不是 20MHz?因为脉压后要做加窗对比和图谱分析,采样率高一点时域波形更平滑,且多留带宽余量。仿真里采样率不是越高越好,点数多了 FFT 慢,100MHz 对这个量级刚好。
%% 参数定义 T = 10e-6; % 时宽 10us B = 10e6; % 带宽 10MHz K = B / T; % 调频斜率 1e12 Hz/s fs = 100e6; % 采样率 100MHz N = round(T * fs); % 采样点数 1000 %% 生成复基带 LFM 信号 t = (0 : N-1) / fs; t = t - T/2; % 以 0 为中心,方便看对称性 s = exp(1j * pi * K * t.^2); % 复基带 LFM,单位幅度 %% 查看瞬时频率(验证斜率) f_inst = K * t; % 理论瞬时频率逻辑说明:exp(1j*pi*K*t.^2)生成的是复数信号,实部是 cos 调频,虚部是 sin 调频,两者构成解析信号。t减去 T/2 让信号关于零时刻对称,这样匹配滤波后峰值正好落在 t=0,后续画图、测旁瓣都直观。若不做这一步,峰值会偏移一个固定时延,不影响结果但增加读数难度。
参数说明:K 的单位是 Hz/s,这里 K = 10MHz / 10μs = 1e12。瞬时频率 f_inst 从 -5MHz 线性上升到 +5MHz,频谱宽度正好 B = 10MHz,验证了 B = K*T 的关系。N = round(T*fs)得到 1000 点,FFT 时补零到 1024 点即可。
4.2 频域匹配滤波最小实现:三行代码出结果
匹配滤波的频域实现一共三步:对接收信号做 FFT,乘上参考信号频谱的共轭,再 IFFT 回来。我把它封装成可以直接抄的代码:
%% 频域匹配滤波 Nfft = 2^nextpow2(N); % 补零到 1024 点,避免循环卷积混叠 S = fft(s, Nfft); % 发射信号频谱 H = conj(S); % 匹配滤波器:共轭即可,不含额外时延 y = ifft(S .* H, Nfft); % 匹配滤波输出 y = fftshift(y); % 把零时刻移到中心 t_axis = (-Nfft/2 : Nfft/2-1) / fs; %% 归一化并画包络 y_abs = abs(y) / max(abs(y)); plot(t_axis * 1e6, y_abs); xlabel('时间 (us)'); ylabel('归一化幅度');逻辑说明:H = conj(S)是把发射信号频谱取共轭,相当于时域用conj(s(-t))做卷积核。fftshift是因为ifft输出的零时刻在第一个点,转成中心对称后主瓣落在 t=0,便于和理论值sinc(Bt)对照。归一化用max(abs(y)),脉压峰值对应发射信号能量 T,单位幅度输入时峰值恰好为 N。
参数说明:Nfft取 1024 而不是直接用 1000,是让 FFT 长度超过两个信号长度的和,保证频域相乘严格等效时域线性卷积。若省掉补零,循环卷积会把尾部混叠到头部,旁瓣测量会偏大。这段代码跑完,主瓣宽度应该约 0.1μs(=1/B),可以用后面的自检脚本验证。
4.3 加窗匹配滤波:窗要加在频域参考信号上
加窗的目的前面说过,是压低 sinc 旁瓣。实现上不是对接收信号加窗,而是对匹配滤波器的频率响应加窗。写成代码区别就在这里:
%% 生成窗函数并作用到滤波器频响 win = ifftshift(hamming(Nfft)); % 窗函数移到零频居中 Hw = conj(S) .* win; % 加窗后的匹配滤波器 yw = ifft(S .* Hw, Nfft); % 加窗脉压输出 yw = fftshift(yw); yw_abs = abs(yw) / max(abs(yw)); %% 对比旁瓣电平 [peak_main, idx_main] = max(y_abs); sidelobe_region = y_abs; exclude = max(1, idx_main - round(fs/B)) : min(Nfft, idx_main + round(fs/B)); sidelobe_region(exclude) = 0; % 抠掉主瓣区域再找旁瓣峰 sidelobe_db = 20 * log10(max(sidelobe_region));逻辑说明:ifftshift(hamming(Nfft))这一步是容易被忽略的细节。hamming 窗默认定义在 0~N-1 区间,直接乘到 H 上会给滤波器引入一个固定的相位斜坡,输出峰值会偏移。用 ifftshift 把窗中心移到零时刻,保持滤波器零相位,峰值才继续留在 t=0。旁瓣测量用“抠主瓣”的办法:主瓣范围取 ±fs/B 个点(即 ±1/B 秒),其余区域的最大值就是第一旁瓣。
参数说明:hamming(Nfft)返回列向量,如果 S 是行向量需要转置统一维度,代码里S是列向量,所以直接点乘。exclude范围取 ±fs/B,覆盖到主瓣第一零点之外,防止把主瓣能量算进旁瓣。实际测出来矩形窗旁瓣约 -13.2dB,Hamming 窗旁瓣约 -42dB,和理论表对得上。
4.4 验证输出:主瓣宽度、旁瓣电平和信噪比增益
脉压做完了,不能只看一眼波形就说“成了”。我习惯用三个数值验收:主瓣 -3dB 宽度、第一旁瓣电平、脉压峰值相对噪声底的门限。下面的代码把三个指标一次性算出来:
%% 计算 -3dB 主瓣宽度 y_db = 20 * log10(y_abs + eps); half_idx = find(y_db > -3.01); % 找高于 -3dB 的区间 main_width = (max(half_idx) - min(half_idx)) / fs; % 主瓣宽度,单位秒 theoretical_width = 0.886 / B; % 理论 -3dB 宽度 fprintf('实测 -3dB 主瓣宽度: %.3f ns\n', main_width*1e9); fprintf('理论 -3dB 主瓣宽度: %.3f ns\n', theoretical_width*1e9); %% 信噪比增益验证 SNR_in = 10 * log10(sum(abs(s).^2)); % 输入信号能量 SNR_out = 10 * log10(max(abs(y).^2)); % 输出峰值功率 fprintf('脉压增益约: %.1f dB (理论 10log10(TB)=%.1f dB)\n', ... SNR_out - SNR_in, 10*log10(T*B));逻辑说明:find(y_db > -3.01)找到主瓣功率高于 -3dB 的所有采样点索引,宽度 = 点数/采样率。理论宽度 0.886/B 对应 sinc 的 -3dB 带宽,和 1/B 的第一零点宽度不是一个值,验收时要用对公式,否则会误判失败。脉压增益这里用峰值功率减输入能量近似,严格说还要折算噪声带宽,仿真无噪声时这个近似已经足够看趋势。
参数说明:eps是防止log10(0)报错。0.886这个常数来自 sinc 函数 -3dB 点的数值解,10MHz 带宽下理论宽度 88.6ns,实测应该在 85~92ns 之间。若偏差超过 5%,基本可以断定是采样率不足或 Nfft 选小导致频谱混叠,回 4.2 节查参数。
5. 匹配滤波器实现的 5 个典型坑:方向、混叠与加窗位置排查
5.1 匹配方向反了:峰值消失或位置颠倒
现象:脉压输出没有尖峰,或者出现一个幅度很低、位置在时间轴反向的“山包”。自己生成 LFM 自己匹配时最容易出这个问题。
原因:匹配滤波器时域核是h(t) = conj(s(-t)),频域是H(f) = conj(S(f))。如果时域实现写成conv(s, conj(s))而不是conv(s, conj(fliplr(s))),卷积核没反转,输出变成互相关而不是自相关,相位永远对不齐。
解决:优先用频域conj(fft(s))实现,天然满足反转+共轭。若必须用时域,写成conv(s, conj(fliplr(s)))。写完先打印输出峰值位置,确认落在 t=0 处,再继续后面的流程。
5.2 采样率低于带宽导致旁瓣抬高
现象:脉压输出主瓣看起来正常,但第一旁瓣从理论 -13.2dB 变成 -10dB 甚至 -8dB,加 Hamming 窗也压不到 -40dB。
原因:复基带 LFM 的瞬时频率范围是 ±B/2,频谱宽度为 B。采样率取 fs ≥ B 时才满足奈奎斯特条件;低于 B 时频谱混叠,混叠分量叠加在旁瓣区域,直接把旁瓣抬高。很多教程写“采样率大于 2 倍信号频率”,套到复信号上就翻车。
解决:基带复信号按fs >= 2*B取保守值,工程仿真我一般取 fs = 10B 看细节,资源受限时最低取 fs = 2.5B。若用实信号上变频,则要按带通采样重新计算,不能直接套基带公式。
5.3 把窗加到了发射信号上
现象:加窗后主瓣宽度不仅没按 1.47 倍展宽,反而出现主瓣分裂,峰值凹陷。
原因:窗函数应该作用于匹配滤波器的频响H(f),有人图省事直接写s_win = s .* hamming(N),把窗乘到了时域发射信号上。这等于发射信号被幅度调制,频谱不再是矩形,脉压输出自然畸变。
解决:记住一句白话——窗是用来“整形”滤波器的,不是用来“整形”信号的。实现上只动Hw = conj(S) .* ifftshift(win),发射信号 s 保持矩形包络不变。区分不了就画频谱:发射信号频谱应该是矩形,滤波器频谱才是窗形。
5.4 复数信号画出“毛刺”:只画了实部
现象:plot(t_axis, y)画出来是一堆密集振荡,看不到主瓣和旁瓣的轮廓,以为匹配滤波失败了。
原因:脉压输出是复信号,包含幅度和相位信息,直接画实部会看到载频级别的振荡。仿真里这个振荡频率等于 LFM 的瞬时频率变化,看起来就是一团乱麻。
解决:画包络用plot(t_axis, abs(y)),看相位用angle(y),测旁瓣用20*log10(abs(y)/max(abs(y)))。这条是最低级的坑,但几乎每个月都能在同事的代码里见到一次,写在这里省得大家再浪费时间。
5.5 循环卷积混叠:Nfft 选太小没提示的错
现象:脉压输出尾部出现一个“假目标”,幅度比主瓣低十几 dB,位置在时间轴末端。加大 Nfft 后假目标消失。
原因:频域相乘默认是循环卷积,当 Nfft 小于两个信号长度之和减 1,卷积结果尾部会绕回头部。LFM 信号长 1000 点,Nfft 恰好取 1024 时余量不足,绕回的能量落在末端形成假峰。
解决:Nfft 取2^nextpow2(2*N - 1),或者直接2^nextpow2(4*N),余量给足。匹配滤波后用max(abs(y))找峰值之前,先用find检查是否有接近 Nfft 端点的局部峰,有就是混叠。
%% 混叠自检:看尾部是否有异常峰 tail_region = y_abs(end-50 : end); if max(tail_region) > 0.05 warning('疑似循环卷积混叠,检查 Nfft 是否足够'); end这段代码加在脉压之后,50 个点对应 0.5μs,正常 sinc 旁瓣在这里应该衰减到 -20dB 以下。若触发警告,把 Nfft 翻倍重跑。这个检查花两秒钟,能省掉后面排半天数据的时间。
6. 用一杆“针”验真身:主瓣宽度自检脚本与多普勒敏感性
6.1 自检脚本:理论、仿真一把梭
每次写完脉压代码,我都会跑一个固定格式的自检脚本,确认主瓣宽度、旁瓣电平、处理增益三个量同时达标,再开始做系统联调。这个脚本可以复用,换参数时直接改 B、T、fs。
%% 脉压自检:主瓣宽度与旁瓣电平 T = 10e-6; B = 10e6; fs = 100e6; K = B/T; N = round(T*fs); t = (0:N-1)/fs - T/2; s = exp(1j*pi*K*t.^2); Nfft = 2^nextpow2(4*N); S = fft(s, Nfft); y = fftshift(ifft(S .* conj(S), Nfft)); y_abs = abs(y)/max(abs(y)); % 主瓣 -3dB 宽度 y_db = 20*log10(y_abs + eps); wid = (max(find(y_db > -3.01)) - min(find(y_db > -3.01))) / fs; assert(abs(wid - 0.886/B) < 0.05/B, ['主瓣宽度异常: ', num2str(wid)]); % 第一旁瓣电平 idx = find(y_abs == max(y_abs)); exc = max(1, idx-round(fs/B)) : min(Nfft, idx+round(fs/B)); copy = y_abs; copy(exc) = 0; sl = 20*log10(max(copy)); assert(sl < -12.5, ['旁瓣电平异常: ', num2str(sl)]); fprintf('自检通过:主瓣 %.1fns,旁瓣 %.1fdB\n', wid*1e9, sl);逻辑说明:assert是 MatLab 自带断言,条件不满足直接抛错,比打印警告更能强制发现问题。主瓣断言用 5% 容差,因为 FFT 离散化会让主瓣宽度有少量偏差;旁瓣断言要求低于 -12.5dB,给矩形窗的 -13.2dB 理论值留 0.7dB 余量。若加 Hamming 窗,把旁瓣断言改成sl < -40即可。
参数说明:exc抠掉主瓣的范围取 ±fs/B,也就是 ±1/B 秒,保证第一零点之外才开始搜旁瓣。4*N的补零量对 1000 点信号产生 4000 点 FFT,混叠余量充足,同时分辨率足够让主瓣宽度读出至少 40 个采样点。
6.2 多普勒敏感性:进阶验证别忘了速度项
LFM 匹配滤波在目标有径向速度时,回波变成s(t-τ)*exp(j2πfd*t),匹配输出峰值位置会偏移fd/K。D 越大偏移越明显。我习惯在做完静态自检后,加一次多普勒扫描:
fd = 5e3; % 5kHz 多普勒 r = s .* exp(1j*2*pi*fd*t); % 简化:只加多普勒,忽略时延 y_dop = fftshift(ifft(fft(r, Nfft) .* conj(S), Nfft)); [~, idx_dop] = max(abs(y_dop)); shift = (idx_dop - Nfft/2) / fs; fprintf('多普勒 %dHz 引起时移 %.1fns,理论 %.1fns\n', ... fd, shift*1e9, fd/K*1e9);这段代码跑通后,如果系统要求测距精度高于这个偏移量,就必须在脉压前做多普勒补偿,或者改用多普勒不敏感的波形。这也是 LFM 系统的边界所在——匹配滤波不是万能的,理解它在什么条件下失效,比理解它怎么工作更重要。
我自己的习惯是:任何脉压代码提交前,先把 6.1 的自检脚本跑一遍,主瓣和旁瓣同时过线才继续;再多普勒项加不加、加多大会在文档里写明。这套流程救过我不少次,希望帮到你。
本文还有配套的精品资源,点击获取