做雷达信号处理的同学,十有八九绕不开LFM(线性调频)信号和脉冲压缩这对组合。说实话,我第一次跑通脉冲压缩仿真的时候,看到那个又窄又高的尖峰从一大片展开的回波里冒出来,确实挺震撼的。今天这篇就把LFM脉冲压缩从原理到Matlab实现完整过一遍:为什么需要脉冲压缩、LFM是怎么做到“又长又宽”还能保持高分辨率、匹配滤波的代码怎么写、加窗怎么做、峰值位置怎么换算距离。适合刚接触雷达信号处理的学生、刚入职的雷达工程师,以及所有想把理论公式落地成可运行代码的人。
1. 为什么雷达非要“又长又宽”:脉冲压缩解决的问题
1.1 距离分辨率与探测距离的矛盾
先抛一个问题:雷达想看得远,又想把两个靠得很近的目标分清,难点到底在哪?
看得远意味着发射能量要足够大。在峰值功率受发射机硬件限制的情况下,最常用的手段就是加大脉冲宽度,让能量在时间上累积,这就是我们常说的“长脉冲”。但脉冲一旦拉长了,距离分辨率就开始变差。雷达的距离分辨率由式ΔR = cτ/2决定,τ就是脉冲宽度。举个例子:一个10μs的发射脉冲,ΔR = 3×10^8 × 10×10^-6 / 2 = 1500米。两个相隔几百米的目标,在这个脉宽下根本分不开,显示器上就是一坨重叠的大回波。
所以矛盾点很明显:要探测距离远,脉冲就得宽;要距离分辨率高,脉冲就得窄。“鱼和熊掌不可兼得”,一直是早期雷达设计里最让人头疼的问题。脉冲压缩技术就是用来解开这个死结的。
1.2 LFM是如何同时满足两者的
LFM(Linear Frequency Modulation,线性调频)的核心思路很巧妙:把脉冲做宽,保证足够的能量;同时让脉冲内部的载波频率随时间线性变化。这样一来,脉冲内部不同时间位置就对应不同的频率,接收端完全可以靠频率信息来区分时间差,进而恢复出距离细节。
打个比方:一支队伍很长,一眼望不到头,但如果每个队员身上的编号都按顺序递增,你只要扫一眼任意一个人的编号,就能大致算出他在队伍里的位置。LFM里的“编号”就是频率——频率随时间匀速爬升,接收端看到哪个频率,就知道回波对应脉冲的哪一段。
具体来说,假设脉冲宽度是T,带宽是B,调频斜率K = B/T。雷达发射时,频率从f0 - B/2线性爬升到f0 + B/2(这叫up-chirp,上扫频;反过来叫down-chirp,下扫频)。回波经过匹配滤波处理后,输出脉宽被压缩到约1/B。于是:
- 探测距离由大脉宽T决定,能量足够,看得远;
- 距离分辨率由大带宽B决定,压缩后脉宽足够窄,看得清。
这样,LFM就同时满足了原本互相矛盾的两个需求。这也是现代雷达里LFM被广泛使用的最根本原因。
1.3 时宽带宽积:脉冲压缩增益的物理来源
关键参数来了:时宽带宽积D = T×B,也叫压缩比或脉冲压缩增益。它衡量的是脉冲被压缩了多少倍。
举个例子,T = 100μs,B = 10MHz,那么D = 1000。经过匹配滤波之后,信号脉宽从100μs压到0.1μs(约等于1/B),压缩了1000倍。在接收机噪声受限的前提下,输出信噪比大约提升D倍(功率意义上)。
这个增益的物理直觉其实不复杂。匹配滤波器的本质是做相干积累,把脉冲内所有采样点的能量在某一时刻叠加到同一个峰上。如果脉冲内有N个独立采样点(N ≈ T×fs),峰值信噪比就约提升N倍,而采样率一般取fs ≥ B,所以N ≈ T×B。简单说就是:你发了一长串信号,匹配滤波把这些信号的能量在时间上“捏”到了一起,峰值自然就冒出来了。
原理先讲到这儿,接下来把公式落到代码上。
2. 从公式到波形:LFM信号与匹配滤波原理
2.1 LFM的数学表达与瞬时频率
基带LFM信号的复包络表达式为:
s(t) = rect(t/T) exp(jπKt²)
其中K = B/T是调频斜率,rect是矩形窗函数,表示信号仅在[-T/2, T/2]内存在。瞬时频率是相位的导数:
f_inst(t) = (1/2π)·d(πKt²)/dt = Kt
从t = -T/2跑到T/2,瞬时频率恰好从-B/2线性上升到B/2。这就是“线性调频”名字的来历:频率随时间线性变化,波形在时频图上就是一条倾斜直线。
注意这里用的是复数基带表达式。实际发射的是实信号Re[s(t)·exp(j2πf0t)],但基带仿真里直接处理复信号就足够了。用复信号的好处在于保存了完整的相位信息,而匹配滤波需要用到相位共轭,少了相位信息,压缩效果会完全不对。
2.2 匹配滤波:为什么压缩后是sinc函数
匹配滤波器的冲激响应是发射信号的共轭时间反转:
h(t) = s*(-t)
这个式子的含义是:用一段和发射信号“镜像对称”的波形去和回波做卷积。回波x(t) = s(t-τ)(即发射信号延迟τ后的版本),匹配滤波输出为:
y(t) = x(t) * h(t) = ∫ x(u)·s*(u-t)du
代入x(t) = s(t-τ),经过变量代换后可以得到:
y(t) = ∫ s(p)·s*(p + τ - t)dp = R_s(τ - t)
也就是说,匹配滤波输出本质上是发射信号的自相关函数,峰值出现在t = τ处,也就是目标回波的真实延迟位置。
当s(t)是LFM信号时,自相关函数近似为一个sinc函数:
y(t) ≈ T·sinc(B(t-τ))
sinc函数的第一个零点在|t - τ| = 1/B处,所以压缩后的主瓣宽度约为2/B,-3dB宽度约0.886/B。这就是LFM脉冲压缩的基本波形形态。
2.3 压缩后的脉宽、旁瓣与分辨率关系
压缩后的信号有几个重要性质,直接关系到雷达系统的性能指标:
- 主瓣宽度与带宽B成反比,B越大,压缩后主瓣越窄,距离分辨率越高;
- 旁瓣电平约为-13.2dB,这是sinc函数本身的第一旁瓣高度。对于雷达来说,这个旁瓣水平太高了,可能掩盖附近的小目标或在强目标附近形成“假目标”;
- 实际系统中几乎都会对信号加窗处理,把旁瓣压下去,但代价是主瓣会被展宽约1.3到1.5倍。
这些现象在下面Matlab代码里都能直观看到。先看代码怎么写。
3. Matlab代码实战:从单目标到多目标脉冲压缩
3.1 环境准备与仿真参数设计
Matlab版本不需要太新,R2018之后都能跑。核心命令就是fft、ifft、conv、plot这些基础函数,不需要额外装工具箱。如果电脑上还没有Matlab,装一个R2020以上版本即可。连基础版都没有的话,用GNU Octave跑也完全没问题,代码几乎不用改。
仿真参数我给一套经充分验证的推荐值,大家可以根据自己的场景调整:
| 参数 | 符号 | 数值 | 说明 |
|---|---|---|---|
| 脉冲宽度 | T | 40 μs | 决定探测能量与距离 |
| 信号带宽 | B | 5 MHz | 决定距离分辨率 |
| 采样频率 | fs | 20 MHz | 4倍过采样,留足余量 |
| 目标1距离 | R1 | 900 m | 回波延迟6μs |
| 目标2距离 | R2 | 1500 m | 回波延迟10μs |
| 目标3距离 | R3 | 2000 m | 回波延迟13.33μs |
| 光速 | c | 3×10^8 m/s | 电磁波传播速度 |
先用理论算一下分辨率:ΔR = c/(2B) = 3×10^8 / (2×5×10^6) = 30m。三个目标间距分别是600m、500m,远大于30m,理论上能分得很开。如果想测区分能力,可以把间距缩到接近30m试试。
3.2 生成LFM发射信号
先把参数摆出来,生成时间轴和发射信号:
%% 参数设置 c = 3e8; % 光速 m/s T = 40e-6; % 脉宽 40us B = 5e6; % 带宽 5MHz fs = 20e6; % 采样频率 20MHz K = B / T; % 调频斜率 % 生成时间轴,从 -T/2 到 T/2 t = -T/2 : 1/fs : T/2 - 1/fs; N = length(t); % 采样点数 % LFM基带信号 s_tx = exp(1j * pi * K * t.^2);为什么要让时间轴从-T/2到T/2,而不是从0到T?因为LFM信号的相位πKt²是关于t=0对称的,只有时间轴关于零点对称,产生的信号相位才是关于零点对称的。虽然从0到T也能生成一个可用的LFM,但后续推导匹配滤波、理解群延迟关系时容易乱了套。我这里建议一开始就养成对称时间轴的习惯。
生成之后最好验证一下频谱,确认带宽真的是5MHz:
% 查看时域波形与幅度谱 f_axis = (-N/2 : N/2-1) * fs / N; figure; subplot(2,1,1); plot(t*1e6, real(s_tx)); xlabel('时间 / us'); ylabel('幅度'); title('LFM信号实部'); grid on; subplot(2,1,2); plot(f_axis/1e6, fftshift(abs(fft(s_tx)))); xlabel('频率 / MHz'); ylabel('幅度'); title('LFM信号的幅度谱'); grid on;跑完你会看到,频谱大致是矩形,带宽从-2.5MHz到+2.5MHz,宽约5MHz。这就是LFM的典型频谱特征。时域波形则是一个频率从低到高的震荡信号,越靠近两端频率越高(这是基带情况,载频为0)。
3.3 构造目标回波与系统噪声
现在设置三个目标,幅度设定为不同值来模拟不同RCS(雷达散射截面)的目标回波。回波本质就是把发射信号延时、缩放后叠加,再加噪声。
%% 目标参数 R_targets = [900, 1500, 2000]; % 目标距离,单位m amp = [1, 0.8, 1.2]; % 回波幅度,模拟不同RCS delays = 2 * R_targets / c; % 双程延迟 delay_samples = round(delays * fs); % 延迟对应的采样点数 % 初始化回波信号,预留足够长度 N_total = N + max(delay_samples) + 100; s_rx = zeros(1, N_total); % 叠加各目标回波 for k = 1:length(R_targets) s_rx(delay_samples(k)+1 : delay_samples(k)+N) = ... s_rx(delay_samples(k)+1 : delay_samples(k)+N) + amp(k) * s_tx; end % 加高斯白噪声,模拟接收机噪声 SNR_dB = 10; % 信噪比10dB signal_power = mean(abs(s_tx).^2); noise_power = signal_power / (10^(SNR_dB/10)); noise = sqrt(noise_power/2) * (randn(1, N_total) + 1j*randn(1, N_total)); s_rx = s_rx + noise;这里有几个细节值得说明。延迟量用双程延迟2R/c,因为雷达信号是从发射到反射回来,走的是双倍距离,这个2倍关系特别容易在初学者代码里漏掉。噪声功率这里用了复信号的处理方式,实部虚部各分配一半功率,所以噪声幅度的均方差是sqrt(noise_power/2),这个细节如果不注意,实际信噪比会差3dB。
3.4 匹配滤波的三种实现方式
匹配滤波的实现方式有很多种,我在不同项目里分别用过下面三种,效果一样,但适用场景不同。
方式一:时域卷积,最直观
%% 方式1:时域卷积 h_ref = conj(fliplr(s_tx)); % 匹配滤波器冲激响应 y_conv = conv(s_rx, h_ref); y_conv = y_conv / max(abs(y_conv));这里h_ref的构造逻辑就是匹配滤波器原理中的“共轭时间反转”。fliplr把时间轴翻转,conj取共轭,两者组合在一起就是s*(-t)的离散形式。这是所有实现方式里最本质、最容易和理论对应的一种,缺点是计算量大。当发射信号和回波都很长时,conv的计算复杂度是O(NM),会慢得让人着急。
方式二:频域相乘,工程上最常用
%% 方式2:频域相乘 N_fft = length(s_rx) + length(h_ref) - 1; % 线性卷积结果长度 nfft = 2^nextpow2(N_fft); % 取2的幂加速FFT S_rx = fft(s_rx, nfft); H_ref = fft(h_ref, nfft); y_freq = ifft(S_rx .* H_ref, nfft); y_freq = y_freq(1:N_fft); y_freq = y_freq / max(abs(y_freq));这段代码的核心依据是“时域卷积等价于频域乘积”。FFT的好处是复杂度O(N log N),处理长序列时的优势是数量级的差距。注意这里nfft必须取到length(s_rx) + length(h_ref) - 1,否则会发生循环卷积导致的时域混叠,我见过不少人在这里栽跟头,后面常见问题里细说。
方式三:直接共轭参考信号,最简洁
很多论文和资料给的“脉冲压缩”代码其实是这样的:
%% 方式3:直接对发射信号共轭做频域匹配滤波 nfft2 = 2^nextpow2(length(s_rx) + length(s_tx) - 1); Y = ifft(fft(s_rx, nfft2) .* conj(fft(s_tx, nfft2)), nfft2);这个写法省去了fliplr这一步,利用DFT的性质:对信号共轭的FFT等价于对原信号频谱共轭取反转,本质上和前面的方式是一致的,但峰值位置会有N-1个采样点的偏移。原因是fliplr相当于在时域上补了一个线性相位,具体对齐关系我有专门一小节讲。
三种方式画出来的压缩波形形态完全一致,但初学者很容易被“峰值位置不同”这个问题绕晕,我在第4节专门解释。
3.5 峰值检测与距离解算
压缩完之后,关键的一步是找到峰值位置,并把它换算成目标距离。沿用方式一的y_conv结果:
%% 找峰值并换算距离 [~, idx] = findpeaks(abs(y_conv), ... 'MinPeakHeight', 0.3, ... 'MinPeakDistance', round(0.886*fs/B)); % 关键:换算距离,N是发射信号长度 t_est = (idx - N) / fs; R_est = c * t_est / 2; % 打印结果 for k = 1:length(R_est) fprintf('检测到目标:索引 %d,估计距离 %.2f m\n', idx(k), R_est(k)); end为什么这里换算距离时要用idx - N?因为卷积输出中,匹配滤波器自身的固有延迟恰好等于发射信号采样点数N。也就是说,即使目标回波延迟为0(目标就在雷达旁边),卷积后的峰值也会出现在第N个点(而不是第1个点)。所以真实的回波延迟应该是峰值索引减去N,再除以采样率,才是目标回波的时间延迟。
这是我踩过最久的一个坑。最初我直接把idx除以fs再乘c/2,结果算出来的目标距离整体偏大了许多,而且三个目标之间的间距是对的,就是整体偏了一个固定值。就是这个固定偏移量,让我排查了很久。
如果你用方式三的直接共轭写法,峰值位置和方式一大约差N-1个采样点,换算公式也要相应调整,务必先跑一个单目标零延迟的case把offset标定出来,再谈数据处理。
4. 常见问题与调试技巧
4.1 频域实现和时域conv结果对不齐
这是初学者最容易纠结的问题。理论上,只要nfft长度不小于两个序列长度之和减1,频域相乘再ifft的结果就应该与时域conv完全一致。但很多人直接写ifft(fft(s_rx).*conj(fft(s_tx))),这里的fft默认取的是length(s_rx)点,结果做循环卷积,尾部混叠直接把波形搞乱了。
建议的判断方法:画图对比y_conv和y_freq的前N_fft个点。如果频域结果的幅度在末尾又冒出来一段“尾巴”,说明nfft取小了,发生了循环卷积。把nfft补到2^nextpow2(length(s_rx)+length(h_ref)-1),再对比,两条线就应该完全重合。
4.2 脉冲压缩增益的数值验证与误区
很多人在仿真里验证增益时,喜欢直接看峰值幅度。但这里有个陷阱:匹配滤波输出峰值等于信号能量的累积,发射信号每个采样点幅度为1时,峰值约等于N=T×fs。如果fs = 20MHz,T = 40μs,那么N = 800,峰值幅度约800(不归一化时)。但脉冲压缩的理论增益是T×B = 200,不是800。差的这4倍来自过采样因子fs/B。
正确的验证方法是看信噪比改善:分别测量匹配滤波前后信号峰值与噪声标准差的比值,两者相除,应该接近D = T×B。如果你用功率比,这个值就是200,折合23dB。用幅度比则是sqrt(D) ≈ 14.14。别把峰值幅度直接当作压缩增益,否则你会觉得理论对不上。
4.3 旁瓣太高怎么办:加窗处理
sinc函数的旁瓣电平是-13.2dB,这个值在雷达里是不可接受的。两个高旁瓣可能会盖住附近的小目标,或者造成虚假目标告警。解决办法是加窗,常用的有汉明窗、汉宁窗、凯塞窗等。
%% 加窗匹配滤波 win = hann(N).'; h_win = conj(fliplr(s_tx .* win)); % 对参考信号加窗 % 频域实现 nfft = 2^nextpow2(N_total + N - 1); y_win = ifft(fft(s_rx, nfft) .* fft(h_win, nfft), nfft); y_win = y_win(1:N_total+N-1); y_win = y_win / max(abs(y_win));加窗后的效果是:第一旁瓣降到-40dB以下,但主瓣宽度从理论上1/B展宽到大约1.3/B到1.5/B,也就是距离分辨率略变差。这就是经典的“主瓣宽度和旁瓣电平”折中。实际工程里,选择哪种窗函数,要看你更看重低旁瓣还是高分辨率。我做过对比,汉明窗的旁瓣压制和主瓣展宽的平衡性比较适合多数场景,凯塞窗可以通过β参数更灵活地调节这个折中。
4.4 参数选择时的几个坑
根据我跑过的仿真和实测数据,这几个参数相关的坑特别常见:
采样率fs不能小于2B。LFM信号带宽是B,如果采样率低于奈奎斯特率,频谱会混叠,压缩之后波形上会出现“假峰”,而且这个假峰的位置还会随参数变化,特别迷惑人。建议至少取fs ≥ 4B。
噪声的SNR定义要统一。我见过一个项目里,有同事把单边功率谱密度当成了总噪声功率,又忽略了复信号的3dB,结果加出来的噪声比预期高了将近6dB,后面很多分析全部偏离。
目标距离太远时,回波长度会超过N_total。我上面代码里预留了N + max(delay_samples) + 100,但如果目标距离改成几十公里,这个长度就不够了,回波会被截断,压缩结果直接乱掉。外加噪声时,每一轮仿真得到的结果都有随机波动,这是正常的。我在做多轮蒙特卡洛仿真时发现,SNR=10dB时峰值位置基本稳定,但SNR降到0dB以下时,出现峰值偏移的概率会明显增加。
4.5 距离解算的标定技巧
每次写新的仿真代码时,建议先用单目标零延迟做一次标定。具体做法:设置R=0,即delay_samples=0,回波从第1个采样点开始。跑完匹配滤波后,记录峰值索引idx0。这时你会发现idx0恰好等于发射信号长度N(使用方式一时)。有了这个基准值,任意目标的峰值索引idx对应的延迟就是(idx - idx0)/fs。用这个方式去写距离换算,比硬记公式更不容易出错。
这个方法在我做半实物仿真时也救过场。硬件采集回来的数据存在各种固定延迟(线缆延迟、滤波器群延迟、FPGA处理延迟),直接用公式算距离总是偏大。后来就是用“放一个已知距离的角反射器,反推零位偏移”的办法,把系统延迟一次性标定掉,后续距离解算才真正准了。代码里的原理和这里完全一样,就是标定idx0。
5. 扩展:从单帧脉冲到多脉冲相参积累
脉冲压缩只是雷达信号处理链条里的第一步。做完匹配滤波之后,一个完整的雷达处理流程通常还会接着做多普勒处理(MTD,Moving Target Detection)和相参积累。这里简单提一句,因为很多读者跑通脉冲压缩后,下一步自然就会遇到多脉冲处理的问题。
LFM脉冲压缩的输出是一维距离像(快时间维),而多个脉冲沿慢时间维排列后,可以对每个距离单元做FFT,提取目标的多普勒频率,进而测速。这就是经典的“距离-多普勒”二维处理。仿真的基本思路是:外层循环发射多个脉冲,每个脉冲经过目标延迟和相位变化(多普勒效应体现在慢时间维的相位旋转),然后先做脉冲压缩,再沿慢时间维做FFT。你会发现,脉冲压缩做完后,目标的能量被压到某个距离单元里;再做多普勒FFT,能量又会聚焦到某个多普勒单元里。这种二维聚焦的思路,和脉冲压缩的本质是相通的,都是对信号进行匹配积累。
我给自己的仿真框架里会保留两个接口:一个是匹配滤波参考信号h_ref,一个是慢时间维的多普勒滤波器组(一般是FFT)。这样从单脉冲脉冲压缩到多普勒处理,代码可以无缝扩展,不需要重写核心逻辑。
最后再分享一个小技巧。做脉冲压缩仿真时,我习惯把“发射信号”“回波”“压缩后结果”三条线画在同一张图上,加了噪声之后尤其有用。发射信号是频率爬升的条纹状,回波是发射信号的延迟版本,压缩后则是几个窄尖峰。每次跑通新参数,先别急着看数据,眼睛扫一眼这三条曲线的形态对不对,很多时候一眼就能发现问题,比看数值快得多。时间轴永远是雷达信号处理里最容易搞错的地方,画出来是最直接的验证手段。