第4章:距离维FFT(Range FFT)——从时域到距离维的魔法

各位同学,欢迎来到第四讲。前面我们聊了ADC采样和CFAR检测的铺垫,今天终于要进入雷达信号处理的核心环节之一——距离维FFT。说白了,就是把回波信号从时间维度变换到距离维度。你想想看,雷达发射一个脉冲,目标反射回来,我们怎么知道它有多远?靠的就是这个FFT。

我个人习惯把Range FFT比作「一把尺子」。这把尺子的刻度就是距离门,而FFT就是帮我们把尺子读出来的过程。嗯,这里要注意,FFT本身不产生距离信息,它只是把隐藏在频率里的距离信息给「翻译」出来。

4.1 为什么要加窗?——窗函数的选择

在做FFT之前,有一个步骤很容易被新手忽略——加窗。我记得刚入行时,有一次做雷达测距,直接拿原始数据做FFT,结果频谱里全是旁瓣,目标都分不清。后来老工程师跟我说:「你试试加个窗。」从那以后,我再也不敢跳过加窗这一步了。

加窗的本质是什么?说白了,就是抑制频谱泄露。因为我们的采样是有限长的,截断信号会在频谱上产生旁瓣。旁瓣会掩盖弱目标,甚至造成虚警。所以,加窗就是给信号「戴个帽子」,让它在边界处平滑过渡。

4.1.1 汉宁窗(Hanning Window)

汉宁窗是我最常用的窗函数之一。它的主瓣宽度适中,旁瓣衰减不错,适合大多数场景。

// 汉宁窗生成(C语言示例,NXP S32R平台)
float hanning_window[N];
for (int i = 0; i < N; i++) {
    hanning_window[i] = 0.5 * (1 - cos(2 * M_PI * i / (N - 1)));
}

汉宁窗的旁瓣峰值约为-31dB,主瓣宽度是矩形窗的两倍。这意味着什么?你分辨两个相近目标的能力会下降,但弱目标更容易被检测到。我在项目中遇到过这样的情况:用矩形窗时,一个强目标的旁瓣直接盖过了旁边的弱目标,换成汉宁窗后,弱目标就清晰可见了。

4.1.2 布莱克曼窗(Blackman Window)

如果你需要更低的旁瓣,布莱克曼窗是个好选择。它的旁瓣衰减能达到-58dB左右,但代价是主瓣更宽。

// 布莱克曼窗生成
float blackman_window[N];
for (int i = 0; i < N; i++) {
    blackman_window[i] = 0.42 - 0.5 * cos(2 * M_PI * i / (N - 1)) 
                         + 0.08 * cos(4 * M_PI * i / (N - 1));
}

我的经验之谈: 在NXP的S32R系列处理器上,我通常这样选窗:

  • 如果目标是检测远距离弱目标,优先用布莱克曼窗
  • 如果目标是分辨两个很近的强目标,用汉宁窗
  • 如果只是做粗略测距,矩形窗也不是不能用,但要做好心理准备

4.2 FFT实现——在NXP处理器上跑起来

加完窗,下一步就是做FFT。在NXP的雷达处理器上,我们通常用硬件加速器或者优化的软件库来实现FFT。我个人建议优先用硬件FFT加速器,因为它的速度和功耗都优于软件实现。

4.2.1 软件FFT实现(参考)

如果你需要在Cortex-M7或R5F核心上做软件FFT,可以参考下面的代码。这是基2的FFT实现,支持任意2的幂次长度。

// 基2 FFT实现(定点数优化版本,适用于NXP DSP扩展)
void range_fft(complex_t *data, int n) {
    // 位反转排序
    for (int i = 1, j = 0; i < n; i++) {
        int bit = n >> 1;
        for (; j & bit; bit >>= 1)
            j ^= bit;
        j ^= bit;
        if (i < j) {
            complex_t tmp = data[i];
            data[i] = data[j];
            data[j] = tmp;
        }
    }
    
    // 蝶形运算
    for (int len = 2; len <= n; len <<= 1) {
        float w_re = 1.0f, w_im = 0.0f;
        float theta = -2 * M_PI / len;
        float s = sin(theta * 0.5f);
        float w_temp_re = -2 * s * s;
        float w_temp_im = sin(theta);
        
        for (int i = 0; i < n; i += len) {
            w_re = 1.0f;
            w_im = 0.0f;
            for (int j = 0; j < len / 2; j++) {
                int idx1 = i + j;
                int idx2 = i + j + len / 2;
                
                float t_re = w_re * data[idx2].re - w_im * data[idx2].im;
                float t_im = w_re * data[idx2].im + w_im * data[idx2].re;
                
                data[idx2].re = data[idx1].re - t_re;
                data[idx2].im = data[idx1].im - t_im;
                data[idx1].re += t_re;
                data[idx1].im += t_im;
                
                // 更新旋转因子
                float new_w_re = w_re * w_temp_re - w_im * w_temp_im + w_re;
                float new_w_im = w_re * w_temp_im + w_im * w_temp_re + w_im;
                w_re = new_w_re;
                w_im = new_w_im;
            }
        }
    }
}

注意: 在NXP S32R274上,我建议使用硬件FFT加速器(FFT Engine),它的处理速度比软件快10倍以上。上面的代码仅用于理解原理,实际产品中请调用SDK提供的硬件API。

4.3 距离门计算——把FFT结果映射到物理距离

做完FFT,我们得到的是频域数据。每个频点对应一个距离门(Range Bin)。距离门的计算公式很简单:

距离门分辨率: ΔR = c / (2 × B)

其中c是光速,B是信号带宽。

最大不模糊距离: R_max = (Fs × c) / (2 × α)

其中Fs是采样率,α是调频斜率。

举个例子,假设我们的雷达带宽是150MHz,采样率是10MHz,调频斜率是10MHz/μs:

参数 数值 说明
距离分辨率 ΔR 1.0米 c / (2 × 150MHz)
最大距离 R_max 150米 (10MHz × c) / (2 × 10MHz/μs)
距离门数量 150个 R_max / ΔR

我在项目中遇到过一个问题:采样率设置得太高,导致最大不模糊距离变短,远距离目标出现了距离模糊。后来我调整了采样率,才解决了这个问题。所以,距离门计算不是简单的数学公式,它直接决定了你的雷达能看多远、看得多清楚。

4.4 距离-幅度谱生成——把数据变成可视化的结果

最后一步,把FFT结果的复数形式转换成幅度谱。这一步很简单,但有个坑要注意:

// 距离-幅度谱生成
float range_spectrum[N/2];  // 只取正频率部分
for (int i = 0; i < N/2; i++) {
    float re = fft_output[i].re;
    float im = fft_output[i].im;
    range_spectrum[i] = sqrt(re * re + im * im);
    
    // 归一化(加窗补偿)
    range_spectrum[i] /= (N * window_gain);
}

关键点:

  • 只取前N/2个点(正频率部分),因为实信号的频谱是对称的
  • 别忘了加窗补偿!加窗会降低信号能量,需要除以窗函数的增益系数
  • 汉宁窗的增益系数约为0.5,布莱克曼窗约为0.42

生成的距离-幅度谱,横轴是距离门(对应物理距离),纵轴是幅度(对应目标反射强度)。你可以在这个谱上看到不同距离处的目标。嗯,这就是我们做CFAR检测的输入数据。

我曾经调试过一个项目,距离-幅度谱上总是出现周期性的尖峰。排查了很久,发现是电源噪声耦合到了ADC采样时钟上。所以,当你看到奇怪的谱线时,先别急着怀疑算法,检查一下硬件环境。

小结

这一章我们聊了距离维FFT的四个关键步骤:加窗、FFT实现、距离门计算、距离-幅度谱生成。说白了,就是从时域信号里提取出距离信息。下一章我们会讲多普勒维FFT,到时候就能同时知道目标的距离和速度了。

最后送大家一句话:做雷达信号处理,不要只盯着公式看,多动手跑跑数据,你会发现很多书本上没有的细节。我在NXP的S32R平台上调试了无数个晚上,才积累出这些经验。希望你们能少走一些弯路。