基于 Python + NumPy 的雷达信号处理仿真平台设计与实现------从 LFM 波形到 CFAR 检测的完整信号链
本文环境:Python 3.9 + NumPy 1.24 + Matplotlib 3.7 + PyYAML 6.0
一、项目背景
雷达信号处理是雷达系统的核心环节,一条完整的处理链通常包含:波形生成 → 目标回波仿真 → 脉冲压缩 → MTD(多普勒 FFT)→ CFAR 检测 。
实际工程中,做雷达信号处理的研究者长期面临两个痛点:
- 真实数据获取难:雷达实测数据多涉密,公开数据集极少,标注成本高;
- 算法验证成本高:在真实硬件上测试新算法周期长、代价大。
本文介绍一个用纯 Python 实现的端到端雷达信号处理仿真平台 Radar-Sim,它同时具备两块能力:
- 教学演示:19 张原理图解,覆盖天线、波形、信号链、关键概念;
- 数据生产:带真值标签的仿真数据生成,可直接对接机器学习训练管道。
整个项目架构如下:
radar-sim-main/
├── lib/ # 核心库(参数、信号、目标模型、场景编排)
│ ├── radar_params.py
│ ├── signal_utils.py
│ ├── target_model.py
│ └── scene_manager.py
├── simulator/ # 端到端仿真引擎
│ └── radar_simulator.py
├── demo/ # 7 大专题 Demo(相控阵/STAP/MIMO 等)
├── tutorials/ # 可视化教程脚本
└── output/ # 生成的 19 张教学图
二、雷达参数建模
所有仿真围绕统一的参数结构展开,使用 dataclass 封装物理量,保证单位一致(距离 m、功率 W、频率 Hz、时间 s)。
python
@dataclass
class RadarParams:
# 发射参数
pt: float = 1e6 # 峰值功率 1 MW
gain_db: float = 30.0 # 天线增益 30 dB
freq_hz: float = 10e9 # X 波段 10 GHz
bandwidth_hz: float = 50e6 # 带宽 50 MHz
pulse_width_s: float = 10e-6 # 脉宽 10 μs
prf_hz: float = 1000.0 # PRF 1 kHz
# 接收参数
noise_figure_db: float = 3.0 # 噪声系数 3 dB
# 信号处理参数
num_pulses: int = 64 # CPI 内脉冲数(2 的幂次,便于 FFT)
@property
def wavelength_m(self) -> float:
"""波长 λ = c / f"""
return SPEED_OF_LIGHT / self.freq_hz
@property
def noise_power_w(self) -> float:
"""接收机噪声功率 P_n = k * T * F * B"""
return BOLTZMANN * STANDARD_TEMP * self.noise_figure_linear * self.bandwidth_hz
@property
def time_bandwidth_product(self) -> float:
"""时宽带宽积 T*B = 500 → 脉压增益约 27 dB"""
return self.pulse_width_s * self.bandwidth_hz
@property
def range_resolution_m(self) -> float:
"""距离分辨率 ΔR = c / (2B) = 3 m"""
return SPEED_OF_LIGHT / (2 * self.bandwidth_hz)
几个值得注意的推导关系:
- 时宽带宽积
T*B = 10μs × 50MHz = 500,即脉冲压缩可获得约 27 dB 的信噪比增益; - 距离分辨率
ΔR = c/(2B) = 3 m,只与带宽有关,与脉宽无关------这正是 LFM 能"又长又锐"的原因; - PRF = 1 kHz 时最大不模糊距离为 150 km,不模糊速度为
λ·PRF/4,这就是后面会遇到的测距测速模糊问题。
三、LFM 波形生成与脉冲压缩
3.1 线性调频信号生成
LFM 是应用最广的雷达波形:频率随时间线性变化,使信号同时拥有大时宽(高能量)和大带宽(高分辨率)。
python
def generate_lfm(bandwidth_hz, pulse_width_s, sample_rate_hz,
up_chirp: bool = True) -> np.ndarray:
"""生成 LFM 复基带信号:s(t) = rect(t/T) * exp(jπKt²),K = ±B/T"""
num_samples = int(round(pulse_width_s * sample_rate_hz))
t = np.arange(num_samples) / sample_rate_hz # 时间轴 [0, T)
sign = 1.0 if up_chirp else -1.0 # 上调频 / 下调频
chirp_rate = sign * bandwidth_hz / pulse_width_s
phase = np.pi * chirp_rate * t**2 # 相位为时间的二次函数
return np.exp(1j * phase)
3.2 频域匹配滤波
匹配滤波器是白噪声背景下检测已知波形的最优线性滤波器,频域实现即为 Y(f) = X(f)·H(f)。
python
def matched_filter(signal: np.ndarray, template: np.ndarray) -> np.ndarray:
"""频域匹配滤波,输出在目标位置形成尖峰"""
n_fft = len(signal) + len(template) - 1
n_fft = int(2 ** np.ceil(np.log2(n_fft))) # 补到 2 的幂次加速 FFT
S = np.fft.fft(signal, n_fft)
H = np.fft.fft(template, n_fft) # template 已是时域反转共轭
result = np.fft.ifft(S * H)
return result[:len(signal) + len(template) - 1] # 线性卷积完整结果
核心结论:脉压后脉冲宽度 ≈ 1/B(3 m 分辨率),峰值幅度获得 T·B 倍增益;LFM 距离旁瓣约 -13.2 dB(sinc 包络第一旁瓣),工程上常用 Hamming 窗压低到 -43 dB 左右,代价是主瓣略有展宽。
四、目标模型:运动轨迹与 RCS 起伏
真实目标不是"静止的点",目标模型覆盖了两种关键特性。

4.1 运动模型
支持三类常用运动模型:
| 模型 | 英文 | 说明 |
|---|---|---|
| 匀速直线 | CV | 速度恒定,最简单的直线运动 |
| 匀加速直线 | CA | 恒定加速度,位置满足 x₀+v₀t+½at² |
| 协调转弯 | CT | 水平面内恒定转弯速率,z 不变(飞机典型机动) |
4.2 Swerling RCS 起伏模型
同一目标的 RCS 随视角、时刻剧烈变化,工程上用 Swerling 模型描述统计特性:
python
def generate_rcs(config: RCSConfig, num_samples: int,
num_pulses_per_sample: int = 1, rng=None) -> np.ndarray:
"""按 Swerling 模型生成 RCS 序列"""
rcs = np.zeros(num_samples * num_pulses_per_sample)
if config.model == RCSModel.SWERLING_0:
rcs[:] = config.mean_rcs_m2 # 非起伏
elif config.model in (RCSModel.SWERLING_I, RCSModel.SWERLING_III):
for i in range(num_samples): # 慢起伏:扫描间变化
rcs_val = _swerling_sample(config, rng)
rcs[i * num_pulses_per_sample:(i + 1) * num_pulses_per_sample] = rcs_val
elif config.model in (RCSModel.SWERLING_II, RCSModel.SWERLING_IV):
for i in range(num_samples * num_pulses_per_sample): # 快起伏:脉冲间变化
rcs[i] = _swerling_sample(config, rng)
return rcs
其中 Swerling I/II 服从指数分布(Rayleigh 幅度,等效 2 个独立散射点),Swerling III/IV 服从 4 自由度卡方分布(等效 4 个散射点)。这个细节对 ML 训练至关重要------训练数据必须包含 RCS 起伏多样性,模型才能学到鲁棒特征。
五、场景编排与回波仿真
5.1 场景生成
场景编排器负责把目标模型和雷达参数组合成完整仿真场景,支持随机多目标、批量生成、YAML 序列化:
python
config = SceneConfig(
num_targets=(1, 3), # 目标数量范围
target_range_range_m=(10e3, 100e3), # 距离范围
target_velocity_range_ms=(50, 500), # 速度范围
target_rcs_range_m2=(1.0, 100.0), # RCS 范围
rcs_model=RCSModel.SWERLING_I,
trajectory_model=MotionModel.CV,
)
generator = SceneGenerator(config, params, rng)
scenes = generator.generate_batch(3, seeds=[100, 200, 300]) # 批量生成
5.2 回波合成(雷达方程)
每个目标回波的幅度由完整雷达方程决定:
Pr = (Pt · G² · λ² · σ) / ((4π)³ · R⁴)
核心实现(节选自 _generate_echo_data):
python
# 目标延迟(距离→时延→采样点)
target_delay_s = 2.0 * target_range_m / SPEED_OF_LIGHT
target_delay_samples = int(round(target_delay_s * self.sample_rate_hz))
# 多普勒频移:fd = 2·vr/λ,叠加慢时间相位
fd_hz = 2.0 * radial_velocity_ms / self._wavelength_m
doppler_phase = np.exp(1j * 2.0 * np.pi * fd_hz * n * self._t_pri)
# 雷达方程计算接收功率,再归一化到噪声基底得到幅度
pr_w = (pt_w * gain_linear**2 * wavelength**2 * rcs_val) / \
((4 * np.pi)**3 * target_range_m**4)
amplitude = np.sqrt(pr_w / noise_power)
# 叠加回波 + 热噪声 → 匹配滤波 → 距离对齐
received[target_delay_samples:end_idx] += amplitude * doppler_phase * self._waveform[:echo_len]
注意 1/R⁴ 衰减------距离翻倍,回波功率降为 1/16,这是远距离目标检测难的根本原因。

六、信号处理链:MTD 与 CFAR 检测
6.1 MTD:慢时间 FFT 得到 RDM
对同一距离单元上的 64 个脉冲回波做 FFT,分离多普勒频率,得到距离-多普勒图(RDM):
python
def _process_signal(self, iq_data: np.ndarray):
"""MTD:慢时间加窗 + FFT,生成距离-多普勒图"""
n_pulses, n_range = iq_data.shape
slow_time_window = np.hamming(n_pulses) # 降低多普勒旁瓣
rdm = np.zeros((n_pulses, n_range), dtype=np.complex128)
for r in range(n_range):
slow_data = iq_data[:, r] * slow_time_window
rdm[:, r] = np.fft.fftshift(np.fft.fft(slow_data))
rdm_power = np.abs(rdm) ** 2
return rdm, rdm_power
6.2 CFAR 检测
固定门限在噪声变化时必然漏检或虚警,CFAR 用自适应门限解决:对被测单元(CUT)前后取训练单元估计噪声功率,紧邻单元作保护单元防止目标能量泄漏。
python
def _detect_targets(self, rdm_power: np.ndarray):
"""简化 CFAR:中值估计噪声基底 + 峰值搜索"""
noise_floor = np.median(rdm_power) # 噪声基底估计
indices = np.unravel_index(
np.argsort(rdm_power.ravel())[::-1], rdm_power.shape)
for d_idx, r_idx in zip(indices[0], indices[1]):
# 与已选峰值的间距约束,避免同一目标重复检测
if not is_far_enough:
continue
snr_db = 10 * np.log10(peak_power / noise_floor)
if snr_db > 3.0: # 过门限才记录
detections.append(DetectionResult(
range_bin=r_idx,
doppler_bin=d_idx,
range_m=r_idx * self._sample_spacing_m,
velocity_ms=self._doppler_bin_to_velocity(d_idx, n_pulses),
snr_db=snr_db,
))
CFAR 的价值在于:环境噪声变大时门限自动抬高,变小时自动降低,虚警率恒定,这就是"恒虚警"的含义。
七、可视化教学模块
项目一键生成 19 张教学图,覆盖四个领域:
| 领域 | 示例图 |
|---|---|
| 天线 | 阵元方向图、栅瓣演示、阵列增益 vs 阵元数 |
| 波形 | LFM 时域、频域、时频谱图 |
| 信号链 | 雷达工作流程、信号流详细图、RDM 形成过程 |
| 关键概念 | 脉冲压缩对比、多普勒模糊、距离模糊、CFAR 原理 |
典型如 v10_pulse_compression_comparison.png(不同 T·B 值脉压对比)和 v14_rdm_formation.png(RDM 形成流程),初学者一眼就能理解"长脉冲高能量 + 频率调制高分辨率"的思想。
八、验证体系
项目内置完整验证框架,81 项理论 vs 仿真验证全部通过,每项都带明确的误差容限。验证粒度覆盖:
- 目标模型:轨迹运动学公式、RCS 均值与分布特性、径向速度计算;
- 场景编排:目标数量范围、批量场景独立性、YAML 读写一致;
- 仿真器:输出数据结构、检测命中率、批量仿真独立性、真值对比。
这意味着仿真的每个环节都有"理论值可对照",不是黑盒生成,适合作为算法验证的基准环境。
九、总结
从 LFM 波形生成、匹配滤波、雷达方程回波合成,到 MTD 和 CFAR 检测,Radar-Sim 用约 2000 行纯 Python 实现了雷达信号处理的完整链路,并额外覆盖了相控阵、STAP、MIMO 等进阶专题。它既能当教学工具,也能作为带真值标签的仿真数据工厂,为雷达检测 ML 模型提供训练数据。
工程代码与配套可视化脚本见: