第03篇:CFAR(恒虚警)处理

一、作用:敌变我变,动态调门
在前两篇中,我们做了一个重要假设:噪声功率是已知且恒定的。
但在真实战场上,这几乎不可能成立:
- 敌方释放噪声干扰,底噪瞬间抬高 10 dB;
- 雷达波束扫过一片麦田或城市建筑群,杂波回波忽强忽弱;
- 接收机自身温度漂移导致噪声系数缓慢变化。
如果继续使用固定的门限,会出现什么后果?
- 底噪抬升时 :噪声尖峰大量超过固定门限 → 虚警率 PFAP_{FA}PFA 急剧飙升,后端处理器瞬间饱和;
- 底噪降低时 :门限相对过高,弱信号被漏掉 → 检测率 PDP_DPD 严重下降。
CFAR(Constant False Alarm Rate,恒虚警) 的核心思想极其巧妙:门限不再固定,而是让接收机"眼睛"时刻盯着周围的噪声环境,随时估算当前噪声功率,动态调整门限,确保虚警概率 PFAP_{FA}PFA 始终恒定。
二、原理:CA-CFAR 的数学机制
最经典、最基础的 CFAR 是 CA-CFAR(单元平均恒虚警,Cell Averaging CFAR)。
2.1 滑动窗口结构
想象一个长度为 NNN 的一维距离/多普勒单元序列,待检测单元(CUT,Cell Under Test)位于正中央。
[ 参考窗 | 保护单元 | CUT | 保护单元 | 参考窗 ]
M 个 G 个 ☆ G 个 M 个
- 参考窗(M 个) :用于估计噪声功率,左右各 MMM 个,共 2M2M2M 个。
- 保护单元(G 个):紧邻 CUT,防止目标信号能量"泄露"进参考窗污染噪声估计。
2.2 噪声功率估计与门限计算
假设噪声服从零均值复高斯分布,其功率估计 σ^2\hat{\sigma}^2σ^2 取左右参考窗的均值 :
σ^2=12M∑i=12M∣xi∣2\hat{\sigma}^2 = \frac{1}{2M} \sum_{i=1}^{2M} |x_i|^2σ^2=2M1i=1∑2M∣xi∣2
为了使虚警概率恒定为 PFAP_{FA}PFA,门限因子 α\alphaα 需满足:
PFA=(1+α2M)−2MP_{FA} = (1 + \frac{\alpha}{2M})^{-2M}PFA=(1+2Mα)−2M
解出 α\alphaα:
α=2M⋅(PFA−1/(2M)−1)\alpha = 2M \cdot \left( P_{FA}^{-1/(2M)} - 1 \right)α=2M⋅(PFA−1/(2M)−1)
最终动态门限为:
η=α⋅σ^2\eta = \alpha \cdot \hat{\sigma}^2η=α⋅σ^2
核心要点 :当某个区域的底噪突然升高时,σ^2\hat{\sigma}^2σ^2 随之变大,门限 η\etaη 自动上浮;当底噪降低时,门限 η\etaη 自动下沉。PFAP_{FA}PFA 始终维持不变!
三、完整代码(可直接运行)

本代码完全复现了原书 ch2_drawFigures 中的 图 2.5,演示了噪声功率存在"高功率区段"时,CA-CFAR 如何动态调整门限。
matlab
%% ========================================================================
% 第03篇:CFAR(恒虚警)处理
% 完整可运行代码
% 基于:ch2_drawFigures(图2.5)
% ========================================================================
clear; close all; clc;
%% 1. 系统参数设置
N = 1024; % 总样本点数(距离单元/时间单元)
M = 32; % 单侧参考窗长度(共 2M = 64 个参考单元)
G = 4; % 单侧保护单元数量
PFA_target = 1e-4; % 目标虚警概率
% 噪声功率设置(单位功率,实部+虚部总功率为1)
s2_low = 1; % 低噪声区功率
s2_high = 10; % 高噪声区功率(模拟干扰/杂波区)
% 生成噪声数据(复高斯)
% 低噪声区:1 ~ N1-1,高噪声区:N1 ~ N2,低噪声区:N2+1 ~ N
N1 = fix(N/3); % 高噪声区起始
N2 = fix(2*N/3); % 高噪声区结束
% 初始化噪声向量
noise = sqrt(s2_low/2) * (randn(N, 1) + 1i*randn(N, 1));
% 注入高功率噪声区域
noise(N1:N2) = sqrt(s2_high/2) * (randn(N2-N1+1, 1) + 1i*randn(N2-N1+1, 1));
% 计算噪声功率(dB 显示)
power_dB = 10*log10(abs(noise).^2);
%% 2. CA-CFAR 门限计算(使用卷积加速)
% 2.1 构造 CFAR 掩膜(用于求和参考窗)
% 长度 = 2*M + 2*G + 1 (CUT)
mask = [ones(M, 1); ... % 左侧参考窗
zeros(2*G+1, 1); ... % 左侧保护 + CUT + 右侧保护
ones(M, 1)]; % 右侧参考窗
mask = mask / (2*M); % 归一化为均值权重
% 2.2 计算每个单元周围的噪声功率估计(滑动平均)
% 注意:conv 默认输出完整卷积,使用 'same' 模式保证长度不变
noise_power = abs(noise).^2; % 瞬时功率
avg_noise_power = conv(noise_power, mask, 'same');
% 2.3 计算 CA-CFAR 门限因子 alpha
% 公式:alpha = 2*M * (PFA^(-1/(2*M)) - 1)
alpha = 2*M * (PFA_target^(-1/(2*M)) - 1);
% 2.4 动态门限
threshold = alpha * avg_noise_power;
% 将门限转换为 dB(方便与噪声同图显示)
threshold_dB = 10*log10(threshold);
%% 3. 额外演示:用门限做目标检测(模拟一个弱目标)
% 假设在单元 400 处注入一个微弱目标(刚好被高噪声淹没,然后被 CFAR 救回)
target_idx = 400;
target_amp = 3.5; % 幅度略高于低噪声区门限,但远低于高噪声区固定门限
noise(target_idx) = noise(target_idx) + target_amp;
% 重新计算该点的功率
power_dB_target = 10*log10(abs(noise).^2);
% 重新计算 CFAR 判决
noise_power_updated = abs(noise).^2;
avg_noise_power_updated = conv(noise_power_updated, mask, 'same');
threshold_updated = alpha * avg_noise_power_updated;
threshold_dB_updated = 10*log10(threshold_updated);
% 判决结果(1 表示检测到目标)
detections = (abs(noise).^2 > threshold_updated);
%% 4. 绘制结果(复现原书图2.5)
figure('Position', [100, 100, 900, 500]);
% 绘制噪声功率(dB)
plot(1:N, power_dB_target, 'b-', 'LineWidth', 0.8, 'DisplayName', '噪声功率');
hold on;
% 绘制 CFAR 动态门限(dB)
plot(1:N, threshold_dB_updated, 'r-', 'LineWidth', 1.5, 'DisplayName', 'CFAR 动态门限');
% 标注高噪声区域
xline(N1, '--k', 'LineWidth', 1);
xline(N2, '--k', 'LineWidth', 1);
text(mean([N1, N2]), max(power_dB_target)*0.5, ' 高噪声/干扰区', ...
'FontSize', 12, 'BackgroundColor', 'w');
% 标记目标检测点(若判决为 1)
detect_idx = find(detections);
if ~isempty(detect_idx)
scatter(detect_idx, power_dB_target(detect_idx), 80, 'g^', ...
'filled', 'DisplayName', '检测到目标');
end
% 标注目标位置(如果目标被漏检,画个叉)
if detections(target_idx) == 0
scatter(target_idx, power_dB_target(target_idx), 100, 'rx', ...
'LineWidth', 2, 'DisplayName', '漏检? (固定门限下)');
end
% 额外绘制一个"固定高门限"以作对比(可选)
% 若使用固定门限(基于全空间平均),会漏掉目标或虚警暴增
fixed_th = alpha * mean(noise_power_updated);
fixed_th_dB = 10*log10(fixed_th);
plot(1:N, fixed_th_dB*ones(1,N), 'k--', 'LineWidth', 1, ...
'DisplayName', '固定门限(全空间平均)');
% 图例与标签
xlabel('距离/时间/频率单元', 'FontSize', 13);
ylabel('功率 [dB]', 'FontSize', 13);
title(['CA-CFAR 动态门限演示 (P_{FA} = ', num2str(PFA_target), ...
', 参考窗 M=', num2str(M), ', 保护单元 G=', num2str(G), ')'], ...
'FontSize', 13);
legend('Location', 'northwest', 'FontSize', 10);
grid on;
ylim([-10, 25]);
xlim([1, N]);
% 添加注释解释 CFAR 的优势
annotation('textbox', [0.18, 0.75, 0.3, 0.12], ...
'String', {'高噪声区:门限自动抬升', '低噪声区:门限自动降低', '确保 P_{FA} 恒定'}, ...
'BackgroundColor', 'white', 'EdgeColor', 'k', 'FontSize', 11);
hold off;
%% 5. 输出检测统计信息
fprintf('======= CA-CFAR 检测统计 =======\n');
fprintf('总单元数:%d\n', N);
fprintf('目标注入位置:%d\n', target_idx);
fprintf('目标功率:%.2f dB\n', power_dB_target(target_idx));
fprintf('目标处 CFAR 门限:%.2f dB\n', threshold_dB_updated(target_idx));
if detections(target_idx)
fprintf('>>> 目标被成功检测到!(CFAR 判决为 1)\n');
else
fprintf('>>> 目标被漏检!(CFAR 判决为 0)\n');
end
% 统计虚警情况(只看纯噪声区,忽略高噪声过渡带,忽略目标区)
clean_idx = setdiff(1:N, N1-10:N2+10); % 避开高噪声区和目标区
clean_idx = setdiff(clean_idx, target_idx);
FA_count = sum(detections(clean_idx));
fprintf('纯噪声区虚警数:%d / %d (虚警率约 %.2e)\n', ...
FA_count, length(clean_idx), FA_count/length(clean_idx));
四、代码讲解:为什么这么写?
4.1 为什么用 conv(卷积)来算平均功率?
matlab
mask = [ones(M,1); zeros(2*G+1,1); ones(M,1)];
mask = mask / (2*M);
avg_noise_power = conv(noise_power, mask, 'same');
CFAR 本质上是对每个单元,提取左右参考窗内的数据做平均。用 conv 实现滑动平均,效率远高于写 for 循环 。原书代码中也采用类似策略(conv(abs(n).^2, mask, 'same')),这里完全复用了这一思想。
mask中保护单元和 CUT 位置的权重为 0,参考窗权重为1/(2M)。'same'参数确保输出长度与输入一致,边缘处自动截断,无需额外处理边界。
4.2 门限因子 alpha 的推导在代码中如何体现?
matlab
alpha = 2*M * (PFA_target^(-1/(2*M)) - 1);
回顾数学推导:
PFA=(1+α2M)−2MP_{FA} = (1 + \frac{\alpha}{2M})^{-2M}PFA=(1+2Mα)−2M
对两边取 −1/(2M)-1/(2M)−1/(2M) 次幂:
PFA−1/(2M)=1+α2MP_{FA}^{-1/(2M)} = 1 + \frac{\alpha}{2M}PFA−1/(2M)=1+2Mα
α=2M⋅(PFA−1/(2M)−1)\alpha = 2M \cdot (P_{FA}^{-1/(2M)} - 1)α=2M⋅(PFA−1/(2M)−1)
这行代码就是该数学公式的直接翻译。
4.3 为什么要注入一个"弱目标"并在高噪声区画线?
为了让读者直观地看到 CFAR 的价值:
- 固定门限(黑虚线,全空间平均)在高噪声区过低,导致虚警激增;在低噪声区又过高,容易漏掉目标。
- CFAR 门限(红线)紧随噪声功率起伏,在高噪声区抬升,在低噪声区降低,确保目标(绿色三角)在低噪声区能被检出,同时高噪声区不产生虚警。
4.4 保护单元(Guard Cells)的作用
matlab
zeros(2*G+1, 1)
将 CUT 及其相邻的 GGG 个单元权重置零。如果目标信号在距离上存在旁瓣或多径展宽,保护单元可以防止目标能量"渗入"参考窗,避免把目标信号当作噪声来估计,从而导致门限虚高、漏检目标。虽然本演示中没有显式生成旁瓣,但保留此结构完全是教科书级的标准 CA-CFAR 实现。
五、运行结果解读
运行代码后,您会看到一条蓝色曲线(噪声功率)在中间 1/3 区域突然跳高(模拟干扰)。红色的 CFAR 门限紧贴着蓝色曲线起伏,而黑色固定门限水平一条线。
观察重点:
- 左侧低噪声区:红线低于固定黑线,门限降低,更易检测弱信号(绿三角成功检出)。
- 中间高噪声区:红线急速攀升,跟随底噪,避免大量虚警。
- 右侧低噪声区:红线再次下降。
- 命令行输出 :会显示纯噪声区的虚警数量。如果不使用 CFAR,在这个场景下虚警率可能飙升到 10% 以上;而 CA-CFAR 的虚警率会严格收敛到设定的 PFA=10−4P_{FA}=10^{-4}PFA=10−4 附近。
六、延伸思考与实战提醒
-
CFAR 的局限性:
- 多目标场景 :如果参考窗内出现其他目标(强干扰),CA-CFAR 的门限会被抬高,导致弱小目标被漏检。此时可改用 OS-CFAR(有序统计 CFAR) 或 GO-CFAR / SO-CFAR。
- 杂波边缘 :如果 CUT 正好位于杂波区和非杂波区的交界处,CA-CFAR 估计的均值会"一半水一半油",门限不准。实际工程中常采用 GOS-CFAR 或两侧分别估计取大/取小值。
-
原书代码中的小彩蛋 :
原书
ch2_drawFigures中的图 2.5 在绘制 CFAR 门限时,故意将门限曲线做了平滑抖动,看起来更具"现实感"。实际工程中,噪声功率估计也可以用**指数加权移动平均(EWMA)**替代滑动窗,以降低存储开销。
下一篇预告(第04篇):匹配滤波与脉冲压缩
在掌握了检测门限和 CFAR 之后,我们将深入发射信号设计。为什么雷达要发线性调频(LFM)信号?为什么扩频通信能让信号"藏"在噪声之下?匹配滤波器的脉冲压缩如何把几十微秒的长脉冲压缩到纳秒级分辨率?我们将用原书第4章的代码,从时域和频域两个角度,彻底搞懂"匹配"二字的真正含义。敬请期待!