简介:《脉冲雷达测速测距.rar》面向雷达技术、电子工程与信号处理方向的学习者,聚焦脉冲雷达如何通过回波时间差完成测距、借助多普勒频移估算目标速度,适合课程实验、原理验证和算法入门时对照理解。压缩包仅含1个.m源码文件,整体约1KB,体积轻便,便于直接在MATLAB类环境中查看、运行和修改,围绕发射脉冲、回波接收、距离公式与频移计算等核心环节展开。已有568人学习,说明该主题具有一定关注度。读者可据此梳理脉冲雷达测速测距的基本流程,结合源码观察时间延迟与频率变化对结果的影响,并在此基础上调整参数、补充注释或扩展实验,形成对雷达信号处理链路的直观认识。
1. 一份雷达信号处理小工程:从回波到距离速度两个数字
雷达测速测距这东西,入门时最劝退的不是公式,是你把回波采回来、做完脉压和 FFT,屏幕上是一片花花绿绿的噪点,根本不知道该信哪个峰。这份脉冲雷达测速测距工程走的是另一条路:波形参数、回波合成、脉冲压缩、距离-多普勒二维处理、CFAR 检测、峰值转物理量,每一环都是能单独跑、单独出图的脚本,改一个参数就能立刻看到结果怎么变。它适合刚接触雷达信号处理、想先把一条完整链路跑通再回头抠公式的人,也适合手里已经有实测数据、需要一个能直接替换数据源的骨架代码的人。整包没有花哨的界面,全是能读、能改、能断点的函数。
2. 工程结构与技术选型:一条脉冲雷达链路怎么搭起来
在动任何一行代码之前,先把「我要算的两个数字从哪来」想清楚,比闷头写 FFT 重要得多。脉冲雷达测速测距的本质是两次信息提取:快时间维上,回波相对发射信号的时延 τ 对应距离 R = cτ/2;慢时间维上,相邻脉冲间回波相位的旋转速率就是多普勒频率 fd,再由 fd = 2v/λ 反推出径向速度。中间隔着的所有处理——脉冲压缩、加窗、二维谱分析、恒虚警检测——都是为了让这两个量在低信噪比下还能被稳定估出来。这套工程就是按这个逻辑切模块的,一个函数只干一件事,出问题的时候能快速定位是哪一环烂了。
2.1 目录结构:脚本、配置、算法、工具四层怎么分
拿到压缩包先别急着点运行,目录树扫一眼就知道作者的分层习惯。这个工程的结构是典型的「配置与算法分离、入口与实现分离」,好处是你改参数不用翻算法文件,换数据源也不用动主流程。
pulse_radar_demo/ ├── main.m % 主流程入口:串起参数、仿真、处理、显示 ├── config/ │ └── radar_params.m % 系统参数集中配置,返回结构体 P ├── src/ │ ├── gen_waveform.m % 生成单个 PRI 的 LFM 发射信号副本 │ ├── gen_echo.m % 按目标距离/速度合成 M 个脉冲的回波 │ ├── pulse_compress.m % 频域匹配滤波,完成脉冲压缩 │ ├── range_doppler_map.m % 慢时间 FFT,形成距离-多普勒图 │ ├── ca_cfar.m % 二维单元平均恒虚警检测 │ └── calc_range_velocity.m % 峰值索引映射为距离、速度物理量 ├── utils/ │ ├── db2pow.m % dB 与线性功率互转 │ └── plot_rd_map.m % RD 图与距离/速度剖面绘制 ├── data/ │ └── echo_sample.mat % 一组预置回波,方便没仿真器时直接验证 └── README.mdconfig/只放参数,不写任何计算逻辑,这样别人接手时第一眼就能看到雷达工作在什么状态。src/里的每个文件对应处理链路的一环,函数签名基本都是function [out] = f(P, in),参数 P 一路透传,避免全局变量。utils/放的是与雷达业务无关的通用工具,比如 dB 换算和绘图,这部分代码可以原样搬到别的项目里。data/里那份echo_sample.mat的价值在于——你还没有采集设备、也还没搞懂仿真参数的时候,能先用它把后面五个函数全跑一遍。
2.2 技术选型:为什么是 LFM + 匹配滤波 + 双维 FFT
波形选线性调频(LFM,也叫 chirp)几乎是这类教学与原型工程的标准答案。单载频脉冲的能量和距离分辨率是矛盾的:想看得远就得更长的脉宽,想要窄的主瓣就得短脉冲,而 LFM 用大时宽带宽积把这个矛盾解开了——脉宽 Tp 决定能量,带宽 B 决定分辨率,两者相互独立,脉冲压缩之后距离分辨率是 c/(2B),跟脉宽没关系。这就是为什么一套 10us 脉宽的雷达,压缩后能拿到 7.5m 的距离分辨率。
处理上,脉冲压缩放在频域做而不是时域做卷积。时域直接conv的运算量是 O(N·M),N 是每个 PRI 的采样点数,M 是脉冲数;频域走fft → 共轭相乘 → ifft,复杂度降到 O(N log N),而且天然支持加窗抑制距离旁瓣。工程实现的定式是先把发射信号副本补零到2^nextpow2(N + Ns - 1),避免循环卷积把远距离目标折叠到近距离门上,这一点后面踩坑章节还会展开。
测速这一侧选的是慢时间 FFT,也就是对一个相干处理间隔(CPI)内同一距离门的 M 个复采样做 FFT。它的物理含义很直白:同一距离单元上的目标,慢时间序列是一个复正弦,频率就是多普勒频率。相比逐个脉冲做相位差分,慢时间 FFT 相当于对 M 个脉冲做了相干积累,增益是 10log10(M) dB,M=128 时差不多 21dB,这在低信噪比下是决定性的。代价是速度分辨率被 CPI 时长锁死,Δv = λ·PRF/(2M),想要更细的速度分辨就只能拉长 CPI 或者提高 PRF。
2.3 参数表:一组能跑通的 10GHz 脉冲雷达配置
参数之间不是随便填的,每改一个都会牵动别处。下表是这套工程默认的一组配置,选的是 X 波段、中等带宽、10kHz 重频,数值都不极端,方便在普通笔记本上秒出结果。
| 参数 | 符号 | 取值 | 说明 |
|---|---|---|---|
| 载频 | fc | 10 GHz | 对应波长 0.03 m,决定多普勒灵敏度 |
| 带宽 | B | 20 MHz | 距离分辨率 c/2B = 7.5 m |
| 脉宽 | Tp | 10 us | 时宽带宽积 200,压缩比 23 dB |
| 调频斜率 | K | 2e12 Hz/s | K = B/Tp |
| 脉冲重复频率 | PRF | 10 kHz | 最大不模糊速度 ±150 m/s |
| 脉冲数 | M | 128 | 相干积累增益约 21 dB |
| 采样率 | fs | 40 MHz | 需大于带宽,留 2 倍余量 |
| 每 PRI 点数 | N | 4000 | N = PRI × fs |
表里最容易被忽略的是两个约束。一是 fs 必须大于 B,否则距离维频谱混叠,脉压主瓣会变宽、旁瓣抬高,实测分辨率达不到理论值,工程上一般取 fs ≥ 1.2B 到 2B。二是 PRF 同时决定了两件事:最大不模糊距离 Rmax = c/(2·PRF) 和最大不模糊速度 Vmax = λ·PRF/2,一个想要大、一个想要小,天生对立。10kHz 这组配置拿到 15km 不模糊距离和 150m/s 不模糊速度,属于折中取值,如果目标换成高速场景,就得考虑多 PRF 解模糊了。
3. 核心算法拆解:从波形生成到点迹输出的四个函数
链路里的每一步都有它存在的理由,缺一步结果就不对。这一章按数据流顺序把四个核心函数拆开讲,代码都是可以直接抄进 MATLAB 跑的完整片段,重点看每个函数的输入输出约定和那几个关键下标——雷达代码里翻车基本都翻在下标和维度上。
3.1 波形生成与回波合成:把物理过程写成复基带模型
发射信号用复基带表示,实部才是真正上天线的那一路,但在仿真里保留复信号能让后续的匹配滤波和多普勒分析干干净净,不用来回翻 cos 和 sin。LFM 的复基带形式是 exp(jπKt²),t 在 [0, Tp) 内,其余时间为零。
function [s_t, t_fast] = gen_waveform(P) % GEN_WAVEFORM 生成一个 PRI 内的 LFM 复基带发射信号 % P : 系统参数结构体 % s_t : 1 x N 复基带发射信号,脉宽外为零 % t_fast : 1 x N 快时间轴,单位秒 t_fast = (0:P.N-1) / P.fs; % 快时间轴覆盖整个 PRI n_pulse = round(P.Tp * P.fs); % 脉宽内的采样点数,10us@40MHz = 400 tt = (0:n_pulse-1) / P.fs; % 脉内时间轴,从 0 开始 s_t = zeros(1, P.N); s_t(1:n_pulse) = exp(1j * pi * P.K * tt.^2); % 复基带 chirp end这里的关键点是发射信号只占了前 400 个采样点,后面 3600 个点全是零。这个「零」不是浪费,它对应的是雷达在等回波——脉冲发出去之后要留出足够长的接收窗,接收窗长度就是最大不模糊距离对应的双程时间。如果把P.N设成和脉宽一样长,那超过 1.5km 的目标全都收不到,这是新手最容易犯的错。
回波合成把每个目标的距离、速度、幅度翻译成时延和多普勒相位。注意这里做了一个工程上常见的简化:时延用整数采样点搬移近似,脉冲内的多普勒忽略不计。
function echo = gen_echo(P, tgt, snr_db) % GEN_ECHO 按目标参数合成 M 个脉冲的回波数据 % tgt : 结构体数组,字段 R(米) / v(米每秒, 正为靠近) / amp(相对幅度) % snr_db : 脉压前单脉冲信噪比,用于标定噪声功率 % echo : M x N 复数矩阵,行是慢时间,列是快时间 M = P.M; N = P.N; n_pulse = round(P.Tp * P.fs); tt = (0:n_pulse-1) / P.fs; base = exp(1j * pi * P.K * tt.^2); % 与发射一致的 chirp 副本 echo = zeros(M, N); for k = 1:M t_slow = (k-1) * P.PRI; % 第 k 个脉冲的慢时间 for i = 1:numel(tgt) R = tgt(i).R - tgt(i).v * t_slow; % 发射时刻目标的真实距离 tau = 2 * R / P.c; % 双程时延 fd = 2 * tgt(i).v / P.lambda; % 多普勒频率,靠近为正 n0 = round(tau * P.fs); % 时延对应的整数采样点 if n0 + n_pulse - 1 <= N echo(k, n0+1 : n0+n_pulse) = echo(k, n0+1 : n0+n_pulse) + ... tgt(i).amp * base * exp(1j * 2 * pi * fd * t_slow); end end end % 按脉压前 SNR 加复高斯白噪声,幅度归一后噪声功率取 10^(-snr/10) noise_pow = 10^(-snr_db/10); echo = echo + sqrt(noise_pow/2) * (randn(M,N) + 1j*randn(M,N)); end代码里n0+1这个下标要留意,MATLAB 从 1 开始索引,时延为 0 的目标落在第 1 个点上,这就是后面距离轴换算要对齐的基准。多普勒项乘在慢时间上,exp(1j*2*pi*fd*t_slow),其中 fd 为正表示目标靠近、相位随慢时间递增,这个符号约定必须和后面 FFT 后的速度轴严格一致,否则速度会整体反号。噪声功率按10^(-snr_db/10)给,是因为基带 chirp 的模恒为 1,信号功率就是 1,这样 SNR 的定义干净、可复现。
提示:
round(tau*P.fs)是整数采样点近似,目标距离不是距离门整数倍时会有最多半个采样点的量化误差,对应 3.75m。真要抠精度,把这一行换成频域相位斜坡实现分数延时。
3.2 频域匹配滤波:脉冲压缩那几十行决定了信噪比
匹配滤波是整条链路里性价比最高的一步:它把 10us 的宽带 chirp 压成一个窄脉冲,压缩比等于时宽带宽积 200,也就是 23dB 的增益。这一步做错,后面所有分析和检测都是在噪声上做无用功。
function [pc, t_axis] = pulse_compress(P, echo, s_t) % PULSE_COMPRESS 频域匹配滤波实现脉冲压缩 % echo : M x N 回波矩阵 % s_t : 1 x N 发射信号副本 % pc : M x N 脉压后数据 % t_axis: 1 x N 快时间轴 Ns = length(s_t); Nfft = 2^nextpow2(P.N + Ns - 1); % 线性卷积所需长度,避免循环混叠 S = fft(s_t, Nfft); % 发射信号频谱,只算一次 pc = zeros(size(echo)); for k = 1:size(echo, 1) X = fft(echo(k,:), Nfft); Y = X .* conj(S); % 匹配滤波 = 频域乘发射谱的共轭 y = ifft(Y); pc(k,:) = y(1:P.N); % 截回 PRI 长度,丢弃拖尾 end t_axis = (0:P.N-1) / P.fs; endNfft = 2^nextpow2(P.N + Ns - 1)这一行是整个函数里最重要的。如果只补零到P.N,ifft做的是循环卷积,卷积结果超出 N 的部分会绕回开头,远距离目标的回波会鬼使神差地出现在近距离开头的位置,而且两个目标可能互相污染。补零到 N + Ns - 1 之后再做 2 的幂次对齐,绕回问题就没了。
Y = X .* conj(S)里的共轭不能省。匹配滤波器的时域形式是 h(t) = s*(-t),对应频域就是 S 的共轭。写成X .* S相当于做了个反向的色散滤波,输出不仅没有压缩效果,还会把 chirp 展得更宽,看起来像「目标变胖了」,实际是滤波器接反了。pc(k,:) = y(1:P.N)截取的起点也有讲究——理论上峰值在时延 τ 处,但只要发射副本 s_t 是从 t=0 定义的,线性卷积的峰值就正好落在 τ 对应的采样点上,不需要额外减脉宽,减了反而会引入 1500m 的系统偏差。
3.3 慢时间 FFT:多普勒通道与速度解算
脉冲压缩之后,每个脉冲得到一行距离门数据;把所有 M 行的同一列取出来,就是那个距离门上的慢时间复序列。对着一列做 FFT,目标的多普勒频率就表现为一个峰。
function [rd_map, r_axis, v_axis] = range_doppler_map(P, pc) % RANGE_DOPPLER_MAP 慢时间 FFT 形成距离-多普勒图 % pc : M x N 脉压后数据 % rd_map : Md x N 复数 RD 图,行是多普勒通道,列是距离门 M = size(pc, 1); Nfft_d = 2^nextpow2(M); D = fft(pc, Nfft_d, 1); % 沿第 1 维(慢时间)做 FFT rd_map = fftshift(D, 1); % 零频移到中心,速度轴才对称 r_axis = (0:P.N-1) / P.fs * P.c / 2; % 距离轴,1 x N v_axis = (-Nfft_d/2 : Nfft_d/2-1) * ... (P.lambda * P.PRF / (2 * Nfft_d)); % 速度轴,1 x Md endfft(pc, Nfft_d, 1)的第三个参数 1 是指定沿第一维变换,也就是每个距离门内部跨脉冲做变换。这个参数漏写、或者数据矩阵转置过,FFT 就会跑到距离维上去,出来的图在距离方向一片模糊,速度方向反而纹丝不动——这是排查时最该先看的地方。fftshift(D, 1)同样是沿第 1 维搬移,把零多普勒搬到中间,这样 v_axis 才是从负到正对称的。
速度轴公式 v = k · λ·PRF/(2·Nfft_d),其中 k 是 fftshift 之后的通道偏移量。这个换算是线性的,P.lambda和P.PRF任何一个填错,速度就整体缩放。比如把 PRF 写成 1000 而不是 10000,所有速度读数会小十倍,但图上峰的位置看着挺正常,属于那种不看数值只信图就会翻车的错。
3.4 CA-CFAR 与点迹输出:把二维图变成一串坐标
RD 图出来了,人的眼睛能看出亮点,但程序要的是坐标。恒虚警检测的作用就是用自适应门限把「噪声起伏」和「真目标」分开,保证在噪声功率变化时虚警率仍然可控。
function [det, th] = ca_cfar(pow_map, gr, rr, gd, rd, pfa) % CA_CFAR 二维单元平均恒虚警检测 % pow_map : 功率图 |RD|^2,尺寸 Md x N % gr, rr : 距离维单侧保护单元数、参考单元数 % gd, rd : 多普勒维单侧保护单元数、参考单元数 % pfa : 期望虚警概率 % det : 与 pow_map 同尺寸的逻辑矩阵 % th : 每个单元对应的检测门限 W = ones(2*gr + 2*rr + 1, 2*gd + 2*rd + 1); % 外窗:含保护区 G = ones(2*gr + 1, 2*gd + 1); % 内窗:保护目标自身 nref = numel(W) - numel(G); % 实际参考单元个数 noise = (conv2(pow_map, W, 'same') - conv2(pow_map, G, 'same')) / nref; alpha = nref * (pfa^(-1/nref) - 1); % CA-CFAR 门限因子 th = alpha * noise; det = pow_map > th; end用conv2而不是双重 for 循环,是因为 128×4000 的图逐点滑动窗口在 MATLAB 里能跑上几分钟,卷积实现则是毫秒级。内窗外窗相减的写法把「保护单元」和「参考单元」拆得很清楚:保护单元是为了不让目标主瓣和近旁瓣的能量漏进噪声估计,导致大目标把自己抬高门限从而漏检。
calc_range_velocity.m只需要把det里为真的下标取出来,分别索引v_axis和r_axis,再按功率从大到小排序即可。排序这一步别省,实际回波里点迹往往有几十个,按功率降序排完,主目标永远排第一行,调试的时候一眼就能看到是不是自己想要的那个。
4. 跑起来:环境准备、参数配置与出图验证
代码读懂了是一回事,能在自己机器上跑出正确结果又是另一回事。这一章从零开始把工程跑起来,包括环境依赖、主流程的每一步在做啥、以及三张图怎么判读——毕竟雷达这东西,数字不看图很难建立直觉。
4.1 环境依赖与运行入口
整个工程没有用到 Phased Array System Toolbox 之类的付费工具箱,纯基础 MATLAB 就能跑,R2016b 之后的版本都可以,因为代码里没有用隐式扩展之外的语法。如果手头只有开源环境,核心函数(脉压、FFT、CFAR)可以一比一翻译成 Python + NumPy,conv2对应scipy.signal.convolve2d,其余几乎不用改。
启动方式就是切到工程根目录跑main.m:
# 进入工程目录后,在 MATLAB 命令行执行 cd pulse_radar_demo mainmain.m里第一件事是addpath(genpath(pwd)),把src、config、utils一次性加进搜索路径,避免手动一个个 addpath。下面是主流程的完整骨架,一步对应链路里的一环:
% main.m —— 脉冲雷达测速测距主流程 clear; clc; close all; addpath(genpath(pwd)); P = radar_params(); % 1) 载入系统参数 tgt = struct('R', {3000, 8000}, ... % 2) 两个目标 'v', {60, -40}, ... 'amp', {1.0, 0.6}); [s_t, ~] = gen_waveform(P); % 3) 发射副本 echo = gen_echo(P, tgt, 10); % 4) 合成回波,SNR 10dB [pc, t_fast] = pulse_compress(P, echo, s_t); % 5) 脉冲压缩 [rd_map, r_axis, v_axis] = range_doppler_map(P, pc); % 6) 距离-多普勒图 pow_rd = abs(rd_map).^2; [det_mask, ~] = ca_cfar(pow_rd, 2, 8, 1, 4, 1e-4); % 7) 二维 CFAR det_list = calc_range_velocity(rd_map, r_axis, v_axis, det_mask); for n = 1:numel(det_list) fprintf('点迹%d: R = %7.1f m, v = %7.2f m/s\n', ... n, det_list(n).range, det_list(n).velocity); end两个目标的设定是有讲究的:3000m 处的目标靠近、幅度 1.0,8000m 处的目标远离、幅度 0.6。前者验证近距离高信噪比下的测距测速精度,后者同时验证三件事——远距离目标有没有被截断、速度符号是不是负的、弱目标能不能被 CFAR 检出来。如果跑完只输出了一个点迹,十有八九是 CFAR 门限太高或者第二个目标距离超出了接收窗。
4.2 参数怎么改:三个最常动的旋钮
工程默认参数不是给你照抄的,是给你改的。真正调试时,90% 的时间都在拧这三个参数。
第一个是P.B带宽。想提高距离分辨率就加大带宽,但从 20MHz 加到 100MHz,距离分辨率从 7.5m 变成 1.5m,代价是P.fs必须跟着涨到至少 120MHz,数据量和计算时间同步翻几倍。而且带宽变大之后,同一段距离上的噪声功率也变大,实际信噪比会掉,所以分辨率和作用距离是要权衡的。
第二个是P.M脉冲数。它直接决定相干积累增益和速度分辨率,M 从 128 加到 512,积累增益多 6dB,速度分辨率细 4 倍,代价是 CPI 时长从 12.8ms 变成 51.2ms。如果目标在 CPI 内运动剧烈、距离走动超过一个距离门,积累反而会散焦,增益全赔进去。判断标准是:目标在 CPI 内的距离变化 v·M·PRI 小于半个距离门,否则就得做距离走动补偿。
第三个是 CFAR 的四个窗口参数。gr/rr和gd/rd不是一个固定的经验值,要跟目标在 RD 图上的展宽匹配:距离维主瓣宽度约等于 c/(2B) 对应的采样点数,多普勒维主瓣宽度约等于 1/(M·PRI) 对应的通道数。保护单元至少覆盖主瓣半宽,参考单元总数建议在 32 到 128 之间,太少噪声估计方差大、虚警不稳,太多则对非均匀背景反应迟钝。
4.3 结果判读:三张图看懂对不对
跑完之后plot_rd_map.m会画三张图,判读顺序建议固定下来,形成习惯。
第一张是脉压后的距离剖面,取第一个脉冲那一行画20*log10(abs(pc(1,:)))。正确的图上应该看到两个尖锐的峰,主瓣宽度对应 7.5m,峰值比噪声底高 20dB 以上(10dB 输入 SNR 加上 23dB 压缩增益,扣除处理损失大致如此)。如果峰很宽、坡度很缓,说明匹配滤波没起作用,先查conj有没有漏。
第二张是 RD 图的 dB 灰度图,横轴距离、纵轴速度。两个目标应该是两个近似「十字」的亮点,沿距离方向有一定宽度,沿速度方向也有一定宽度。如果整张图呈现出斜条纹,那是某个强目标的距离走动在多普勒维上的表现,不是 bug,是物理。如果整张图是均匀的噪点、完全没有亮点,先看fft的维度参数是不是写反了。
第三张是 CFAR 检测结果叠加图,把det_mask为真的点用标记画在 RD 图上。理想的输出是每个目标附近只有一两个点被检出,而不是一片。如果一片点迹围着一个目标,说明参考窗设得太小,噪声估计被目标本身污染;如果目标本体反而没检出,说明保护单元不够,门限被自己抬高了。
5. 避坑与排查:脉冲雷达链路里最容易翻车的五处
这套代码我在不同机器、不同参数组合下跑过很多轮,下面这几处是反复出现、而且症状和原因隔得比较远的问题。按「现象 → 原因 → 解决」记下来,下次遇到类似的图能少绕半小时。
5.1 远距离目标跑到近处:补零长度不够导致的循环卷积
现象是明明设了 8km 的目标,测出来却在 2km 附近,或者两个目标的
本文还有配套的精品资源,点击获取