第一章 数据采集与模拟:用Python模拟雷达回波信号

各位同学,大家好。我是你们这门课的主讲。在航电系统里摸爬滚打了十几年,我最大的体会就是——数据是雷达的血液。没有真实、可靠的数据流,再牛的算法也是空中楼阁。

今天咱们聊第一章:数据采集与模拟。说白了,就是教你怎么用Python“无中生有”,造出一份像模像样的雷达回波信号。为什么要学这个?因为真实雷达数据太金贵了,调试算法时你总不能天天跑外场吧?

1.1 雷达回波信号长什么样?

先问大家一个问题:雷达发射一个脉冲,碰到目标反射回来,我们收到的是什么?

嗯,其实就是一个被调制了的正弦波。发射信号通常是这样的:

s(t) = A * cos(2π * f₀ * t + φ)

其中f₀是载波频率,φ是初始相位。但目标在动,所以回波会带上多普勒频移。我在项目中遇到过,有一次飞机速度估算偏差大,查了半天,就是多普勒项没加对。

所以,模拟回波的核心公式是:

s_rx(t) = A_r * cos(2π * (f₀ + f_d) * t + φ_r)

这里f_d就是多普勒频移,跟目标径向速度成正比。

1.2 什么是I/Q数据?为什么需要它?

你可能会问:为什么雷达系统不直接处理上面的实信号,非要搞什么I/Q?

原因很简单——实信号丢失了相位信息。你想想看,cos(θ)和cos(-θ)在实信号里是一样的,但雷达要靠相位差来测距、测速。所以我们需要正交解调,把信号拆成两路:

  • I路(同相分量):与本地载波同相
  • Q路(正交分量):与本地载波相差90°

这样,一个复数信号就出来了:z(t) = I(t) + j * Q(t)。它的幅度和相位都能独立提取。

核心要点:I/Q数据是雷达信号处理的“通用语言”。你后面学到的脉冲压缩、MTI、CFAR,全是在I/Q数据上操作的。

1.3 用Python生成I/Q数据

好,理论讲完了,咱们直接上代码。我个人习惯用numpy做数组运算,matplotlib看波形。

import numpy as np
import matplotlib.pyplot as plt

# 参数设置
fs = 10e6          # 采样率 10 MHz
T = 100e-6         # 脉冲宽度 100 μs
f0 = 1e6           # 中频频率 1 MHz
fd = 5e3           # 多普勒频移 5 kHz
A = 1.0            # 幅度

# 时间轴
t = np.arange(0, T, 1/fs)

# 生成I/Q数据
I = A * np.cos(2 * np.pi * (f0 + fd) * t)
Q = A * np.sin(2 * np.pi * (f0 + fd) * t)

# 合成复数信号
complex_signal = I + 1j * Q

# 看一眼波形
plt.figure(figsize=(12, 4))
plt.plot(t * 1e6, I, label='I路')
plt.plot(t * 1e6, Q, label='Q路')
plt.xlabel('时间 (μs)')
plt.ylabel('幅度')
plt.legend()
plt.grid(True)
plt.show()

这段代码跑完,你会看到两条相位差90°的正弦波。嗯,这就是最原始的雷达回波。

小技巧:实际工程中,I/Q数据通常用np.complex64存储,节省内存。我早期用complex128,结果一个数据文件几百兆,后来全改了。

1.4 添加噪声模型

真实世界里没有纯净的信号。雷达接收机里,热噪声、量化噪声、杂波……各种干扰。我们模拟时,最常用的是加性高斯白噪声(AWGN)

为什么是高斯?因为中心极限定理告诉我们,大量独立随机源叠加,分布就趋近于高斯。我在项目中验证过,实测噪声的直方图跟高斯曲线几乎重合。

添加噪声的代码很简单:

def add_awgn(signal, snr_db):
    """
    给信号添加指定信噪比的高斯白噪声
    snr_db: 信噪比,单位dB
    """
    signal_power = np.mean(np.abs(signal)**2)
    noise_power = signal_power / (10**(snr_db / 10))
    noise = np.sqrt(noise_power / 2) * (np.random.randn(len(signal)) + 
                                         1j * np.random.randn(len(signal)))
    return signal + noise

# 使用示例
snr = 10  # 10 dB
noisy_signal = add_awgn(complex_signal, snr)

注意这里噪声功率要除以2,因为I/Q两路各分一半。我曾经犯过这个错,噪声功率算大一倍,结果信噪比总对不上。

1.5 完整的模拟流程

把上面所有步骤串起来,就是一个完整的雷达回波模拟器:

def simulate_radar_echo(fs, T, f0, fd, A, snr_db):
    """
    模拟单目标雷达回波I/Q数据
    """
    t = np.arange(0, T, 1/fs)
    # 生成纯净信号
    I = A * np.cos(2 * np.pi * (f0 + fd) * t)
    Q = A * np.sin(2 * np.pi * (f0 + fd) * t)
    complex_signal = I + 1j * Q
    # 添加噪声
    noisy_signal = add_awgn(complex_signal, snr_db)
    return t, noisy_signal

# 调用
t, data = simulate_radar_echo(
    fs=10e6, T=100e-6, f0=1e6, fd=5e3, A=1.0, snr_db=10
)

避坑指南:我曾经在模拟多目标时,直接把多个回波相加,忘了考虑每个目标的时延差。结果距离维上全混在一起,根本分不清。记住:不同距离的目标,回波到达时间不同,一定要加时延。

1.6 本章小结

今天我们干了三件事:

  1. 理解了雷达回波的数学本质——就是一个带多普勒频移的正弦波
  2. 学会了生成I/Q数据——用复信号保留完整相位信息
  3. 掌握了噪声建模——AWGN是雷达系统最基础的噪声模型

这些代码虽然简单,但它们是后续所有高级处理的基础。你想想看,没有干净的I/Q数据,脉冲压缩怎么做?MTI怎么测速?所以,这一章请务必亲手敲一遍代码。

下一章,我们会把单脉冲扩展成脉冲串,引入脉冲重复间隔(PRI)的概念。到时候,你就可以模拟一个完整的雷达扫描周期了。

好,今天就到这里。有问题欢迎在课程群里讨论,或者直接找我。咱们下章见。


公众号:蓝海资料掘金营,微信deep3321