☰
MATLAB在线核聚类雷达辐射源分选:PDW流式聚类与参数调优
2026/10/16 13:39:04 网站建设 项目流程

简介:雷达辐射源在线核聚类分选MATLAB代码,面向雷达信号处理、电子侦察及相关专业研究人员,针对复杂电磁环境下多目标信号难以实时准确分选的问题,提供一套基于核映射与聚类算法的在线分选实现。压缩包共7个m文件,整体仅4KB,代码结构紧凑,涵盖信号生成、数据预处理、核聚类、类别更新与删除等关键环节,便于理解算法流程并快速嵌入自有系统。该实现采用核技巧将低维不可分信号映射到高维特征空间,结合K均值或谱聚类进行分组,可提升辐射源识别准确率。用户只需输入雷达数据,程序即可自动完成滤波、映射、聚类与结果展示,适合算法验证、课程设计及科研对比。目前已有1164人学习下载,是研究雷达辐射源分选技术的一份实用参考资料。

1. 雷达辐射源在线核聚类分选为什么比离线难做

雷达辐射源在线核聚类分选,核心是把截获到的脉冲描述字(PDW)按辐射源身份实时聚类,难点全在“在线”两个字。离线场景下你可以把一整段脉冲流收齐,反复迭代聚类直到收敛;在线场景里脉冲一批批到达,簇数未知且会随时间增加或消失,同一部雷达还会因为频率捷变、模式切换在特征空间里分裂成多个子簇,线性聚类基本分不开。核聚类把特征隐式映射到高维空间,能刻画这种非线性边界,但核方法本身依赖历史样本做距离计算,搬到流式场景就必须解决缓存、增量更新和簇生命周期管理。

本文给一套不依赖任何工具箱的 MATLAB 实现,包含仿真 PDW 脉冲流生成、在线核聚类主循环、簇合并/过期清理,以及两个能直接用的分选评估指标。适合做电子侦察算法仿真、信号分选预研的工程师,或者正在把离线聚类算法改成流式版本的开发者。代码可以整段复制运行,参数和坑都写在对应章节里。

2. 核聚类怎么用进辐射源分选:PDW 特征与在线聚类选型

2.1 分选问题的输入:PDW 脉冲描述字

辐射源分选也叫去交织,输入是侦察接收机对截获脉冲形成的 PDW 记录。一条典型的 PDW 包含载频 RF、脉宽 PW、到达角 DOA、到达时间 TOA、脉幅 PA 等参数。其中 RF、PW、DOA 是辐射源身份最直接的刻画,TOA 一般留到后续做重频模式估计,聚类分选这一步通常不把它作为特征。于是每个脉冲对应一个三维特征向量 x=(rf, pw, doa),分选就变成:把混在一起的脉冲流按辐射源分成若干组,每组对应一部雷达。

实际信号里这组特征并不干净。频率捷变雷达的 RF 会在中心频率附近跳变,同一部雷达在不同搜索/跟踪模式下的 PW 完全不同,DOA 受多径和测向误差影响呈现多峰分布。也就是说,一个辐射源在特征空间中不是紧凑的高斯团,而是由多个分离子簇组成的非线性结构。用欧氏距离做线性聚类,比如经典 k-means,会把同一个辐射源的不同子簇硬切开,或者把两部 RF 相近的雷达并成一个簇。

核聚类的动机就在这里。RBF 核 κ(x, y)=exp(-‖x-y‖²/(2σ²)) 把样本隐式映射到高维核空间,在高维空间中原本交叉的子簇可能变成线性可分。对雷达辐射源分选来说,核方法不需要预先知道每部雷达的参数调制规律,只要核宽度 σ 和簇数给得合理,就能拟合出 RF 捷变、模式跳变这类非线性边界。

2.2 在线核聚类要做什么:缓存、距离与簇心更新

普通核 k-means 是批处理算法:每次迭代都要计算所有样本两两之间的核矩阵,然后重新分配每个样本到最近的核簇心。这个流程在离线没问题,在线场景下一是不允许反复扫描历史数据,二是脉冲流的分布会随时间漂移——一部雷达关机后它的簇应该被遗忘,新开机一部雷达时又要能立刻形成新簇。

在线核聚类最常见的做法,是给每个簇保留一个固定容量的缓存样本集合,用缓存样本的核均值来近似簇心在核空间中的像。设簇 c 的缓存为集合 S_c,簇心的像为 φ(x) 在 S_c 上的平均。新样本 x 到该簇心的核距离平方可以展开成:

d² = κ(x,x) + (1/n²)·ΣΣ_{i,j∈S_c} κ(x_i,x_j) - (2/n)·Σ_{i∈S_c} κ(x,x_i)

第一项是常数,第二项是簇内核值平均,记为 inner,可以在簇结构里维护,第三项是样本到缓存所有点的核值平均。整个距离计算只需要遍历缓存,不需要全局核矩阵,这让流式处理成为可能。

在线更新也围绕这个公式展开:样本被分配进簇后,把它加进缓存,同时增量更新 inner;缓存超限时丢弃最旧样本,再增量扣掉它的贡献。这样簇心像就一直由最近若干个样本决定,天然带了一个滑动窗口式的遗忘机制。簇的增删则由新簇阈值、合并阈值和过期清理控制。这套方案在工程实现上只依赖基础矩阵运算,不需要统计工具箱,给 MATLAB 落地留出了很大的自由度。

3. 用 MATLAB 把在线核聚类跑起来:仿真数据与核心函数

3.1 生成仿真 PDW 脉冲流

先写一个仿真数据生成函数,模拟 3 部雷达的脉冲流。第 1 部雷达 RF 中心 9.2 GHz、PW 1.0 μs、DOA 35°,第 2 部雷达 RF 中心 9.8 GHz、PW 1.8 μs、DOA 55°,第 3 部雷达 RF 中心 10.4 GHz、PW 0.7 μs、DOA 60°。前两部雷达 RF 距离比较近,第三部还加了 30% 概率的正负跳变,用来模拟频率捷变造成的非线性子簇。每批 40 个脉冲,共 50 批,最后混入 5% 的杂散噪声脉冲,真实类别用 0 标记。

function [batchX, batchY] = genRadarStream() % 仿真生成在线到达的雷达PDW脉冲流 % 输出 batchX: 1xB cell,每个cell是 n_b x 3 的特征矩阵,列为[RF, PW, DOA] % batchY: 1xB cell,每个cell是 n_b x 1 的真实辐射源编号,0为杂散脉冲 rng(42); nBatch = 50; batchSize = 40; noiseRatio = 0.05; centerRF = [9.2, 9.8, 10.4]; centerPW = [1.0, 1.8, 0.7]; centerDOA = [35, 55, 60]; nRadar = numel(centerRF); batchX = cell(nBatch,1); batchY = cell(nBatch,1); for b = 1:nBatch X = zeros(batchSize,3); y = zeros(batchSize,1); row = 0; for r = 1:nRadar n = floor(batchSize/nRadar); % 频率捷变:30%概率在中心值上再叠一个随机符号跳变 rf = centerRF(r) + 0.3*randn(n,1) ... + 0.4*(rand(n,1)>0.7).*sign(randn(n,1)); pw = centerPW(r) + 0.15*randn(n,1); doa = centerDOA(r) + 2.0*randn(n,1); X(row+1:row+n,:) = [rf, pw, doa]; y(row+1:row+n) = r; row = row + n; end % 杂散噪声脉冲 nNoise = floor(batchSize*noiseRatio); if nNoise > 0 X(row+1:row+nNoise,:) = [8.5+2.0*rand(nNoise,1), ... 0.3+1.2*rand(nNoise,1), 30+50*rand(nNoise,1)]; y(row+1:row+nNoise) = 0; row = row + nNoise; end idx = randperm(row); batchX{b} = X(idx,:); batchY{b} = y(idx); end end

这个函数把整个仿真流切成 50 个 batch,符合在线处理的输入形式。这里刻意让第 2 和第 3 部雷达的 DOA 中心仅差 5°,加上测向噪声后有重叠;第 1 和第 2 部雷达 PW 有明显区分,但 RF 捷变会让线性边界失效。这样生成的仿真数据能直接暴露线性聚类的问题。

3.2 核距离计算与簇结构设计

簇结构我用 MATLAB struct 数组表示,每个簇包含四个字段:X 是缓存样本矩阵,n 是当前样本数,inner 是簇内核值平均,lastSeen 记录最后被命中的 batch 序号。inner 字段对应公式里的 (1/n²)·ΣΣκ,维护好它,距离计算每次只需要算样本到缓存的一行核值。

function d2 = kernelDist(x, cl, sigma2) % 样本x与簇结构cl在RBF核空间的距离平方 % d2 = k(x,x) + cl.inner - 2*mean_u k(x,u) if cl.n == 0 d2 = inf; return; end Kxv = exp(-sum((cl.X - x).^2, 2)/sigma2); d2 = 1 - 2*mean(Kxv) + cl.inner; end

sigma2 传的是 2σ²,这是 RBF 核的常量,提前算好避免重复除法。距离计算的核心是 exp(-‖x-cache‖²/sigma2),矩阵写法一次算出样本到缓存所有点的核值,mean 后代入展开式。如果簇缓存为空,距离返回 inf,让主循环直接走新建分支。

样本加入簇时,需要增量维护 inner。加入新样本 y 后,新的核值总和等于旧总和加 2 倍新旧交叉核值之和再加 1(自己到自己的核值为 1),再除以新的 n 平方。缓存超限丢弃最旧样本 x_old 时,从总和里减去 2 倍 x_old 与剩余样本的交叉核值之和,再减 1,同时 n 减 1。

function cl = addToCluster(cl, x, M, sigma2, b) % 新样本x加入簇cl,并增量维护inner;缓存超过M时丢弃最旧样本 oldN = cl.n; if oldN == 0 cl.X = x; cl.n = 1; cl.inner = 1; cl.lastSeen = b; return; end Kxv = exp(-sum((cl.X - x).^2, 2)/sigma2); newSum = cl.inner * oldN^2 + 2*sum(Kxv) + 1; cl.n = oldN + 1; cl.inner = newSum / cl.n^2; cl.X = [cl.X; x]; if size(cl.X,1) > M xold = cl.X(1,:); remX = cl.X(2:end,:); Kold = exp(-sum((remX - xold).^2, 2)/sigma2); oldSum = cl.inner * cl.n^2; newSum = oldSum - 2*sum(Kold) - 1; cl.n = cl.n - 1; cl.inner = newSum / cl.n^2; cl.X = remX; end cl.lastSeen = b; end

这段函数体现了在线核聚类最关键的增量更新。每次加入新样本只减少量 O(M·d) 的核值计算,而不是重算整个缓存核矩阵。丢弃最旧样本后,inner 仍然对应剩余缓存样本的精确核距离分母,不会累积误差。M 的典型值是 20,太小会让簇心像只能记住最近 20 个脉冲,太大则每个样本的距离计算都会变慢。

3.3 主循环:分配、新建、合并与过期清理

主脚本按 batch 顺序读入仿真脉冲流,每批先按物理范围归一化特征到 [0,1],然后逐样本计算到所有现有簇的核距离。最近距离小于 thNew 就归入该簇并更新;否则以该样本为种子新建一个簇。每 5 个 batch 做一次簇合并和过期清理。

% demo_online_radar_cluster.m 主脚本 clear; clc; [batchX, batchY] = genRadarStream(); % 归一化边界:按侦察接收机先验物理范围设置 xmin = [8.0, 0.3, 30]; xmax = [12.0, 2.5, 90]; sigma = 0.3; sigma2 = 2*sigma^2; % RBF核宽度 M = 20; % 每簇样本缓存上限 thNew = 0.15; % 核距离超过阈值 -> 新建簇 thMerge = 0.03; % 簇心核距离阈值 -> 合并 expire = 8; % 连续8批未命中 -> 删除 clusters = struct('X',{}, 'n',{}, 'inner',{}, 'lastSeen',{}); allLabels = cell(numel(batchX),1); for b = 1:numel(batchX) Xb = (batchX{b} - xmin) ./ (xmax - xmin); labels = zeros(size(Xb,1),1); for i = 1:size(Xb,1) x = Xb(i,:); best = 0; dmin = inf; for k = 1:numel(clusters) d2 = kernelDist(x, clusters(k), sigma2); if d2 < dmin dmin = d2; best = k; end end if best > 0 && dmin < thNew labels(i) = best; clusters(best) = addToCluster(clusters(best), x, M, sigma2, b); else clusters(end+1).X = x; %#ok<AGROW> clusters(end).n = 1; clusters(end).inner = 1; clusters(end).lastSeen = b; labels(i) = numel(clusters); end end allLabels{b} = labels; if mod(b, 5) == 0 clusters = mergeClusters(clusters, thMerge, sigma2, M); clusters = removeExpired(clusters, b, expire); fprintf('batch %d: 当前簇数 %d\n', b, numel(clusters)); end end

归一化边界 xmin/xmax 用的是侦察接收机先验范围,不是统计得到的最小最大值,这保证了特征流里即使后续出现更大范围的新雷达,归一化尺度也不会被历史数据带偏。主循环的逻辑是最朴素的在线分配:先找最近簇,够近就归入,不够近就开新簇。注意这里没有遍历所有样本重新分配旧样本,符合在线聚类的流式约束。

合并函数用簇心核距离判断两个簇是否来自同一辐射源。两个簇心的核距离平方等于 inner_a + inner_c 减去 2 倍跨簇核值平均,这一步只需要两个簇的缓存就能算。合并后如果缓存超过 M,就只保留最近 M 行,避免缓存无界增长。过期清理的逻辑更简单:一个簇连续 expire 个 batch 都没有新样本命中,就认为这部雷达已经关机,直接删除。

function clusters = mergeClusters(clusters, thMerge, sigma2, M) % 贪心合并:按簇心核距离从小到大合并,每个簇最多参与一次 K = numel(clusters); merged = false(1,K); newList = struct('X',{}, 'n',{}, 'inner',{}, 'lastSeen',{}); for a = 1:K if merged(a), continue; end bestC = 0; bestD = inf; for c = a+1:K if merged(c), continue; end Xa = clusters(a).X; Xc = clusters(c).X; D = sum(Xa.^2,2) + sum(Xc.^2,2)' - 2*(Xa*Xc.'); D(D<0)=0; crossMean = mean(mean(exp(-D/sigma2))); d2 = clusters(a).inner + clusters(c).inner - 2*crossMean; if d2 < bestD bestD = d2; bestC = c; end end if bestC > 0 && bestD < thMerge cl = clusters(a); cl.X = [clusters(a).X; clusters(bestC).X]; cl.n = size(cl.X,1); D = sum(cl.X.^2,2) + sum(cl.X.^2,2)' - 2*(cl.X*cl.X.'); D(D<0)=0; cl.inner = mean(mean(exp(-D/sigma2))); cl.lastSeen = max(clusters(a).lastSeen, clusters(bestC).lastSeen); if cl.n > M cl.X = cl.X(end-M+1:end,:); cl.n = M; D = sum(cl.X.^2,2) + sum(cl.X.^2,2)' - 2*(cl.X*cl.X.'); D(D<0)=0; cl.inner = mean(mean(exp(-D/sigma2))); end newList(end+1) = cl; %#ok<AGROW> merged([a bestC]) = true; else newList(end+1) = clusters(a); %#ok<AGROW> merged(a) = true; end end clusters = newList; end function clusters = removeExpired(clusters, bNow, expire) % 连续expire个batch没有被命中的簇直接删除 keep = false(1,numel(clusters)); for k = 1:numel(clusters) keep(k) = (bNow - clusters(k).lastSeen) < expire; end clusters = clusters(keep); end

mergeClusters 用跨簇核均值代替传统质心距离,是核空间里的严格度量,不会因为缓存里样本数量不同而偏向小簇。这里故意每 5 批才检查一次合并,而不是每样本都做,是为了避免 O(K²) 的合并扫描拖垮在线吞吐。等到主循环结束,你可以用 labels 和真实类别作图检查分选效果,能看到三个主要辐射源各自形成完整簇,杂散脉冲被分散到边界上的小簇里。

4. 参数标定与分选效果评估:关键参数和两个指标

4.1 四个核心参数的标定顺序

在线核聚类有四个参数直接决定分选结果:核宽度 σ、新簇阈值 thNew、缓存上限 M、过期代数 expire。调参顺序不能乱,我一般先固定 M 和 expire,再单独标 σ 和 thNew,最后回头调 M。四个参数的失效表现各不相同,可用下面这张表做对照:

参数符号归一化后建议范围调大的后果调小的后果
RBF核宽度sigma0.1 ~ 1.0所有簇心距离趋近0,簇全部合并任意两个点都像“远邻”,每个脉冲都成新簇
新簇阈值thNew0.05 ~ 0.3漏掉新开机的雷达,新样本强塞进旧簇杂散脉冲也被开簇,簇数量暴涨
缓存上限M10 ~ 50距离计算变慢,旧样本遗忘过慢簇心漂移剧烈,雷达一个模式切换就被拆成多簇
过期代数expire5 ~ 15关机雷达的僵尸簇长期滞留偶尔丢个 batch 就误删正常簇

标定 σ 最直接的依据是看距离分布。先用一批不带噪声的仿真数据跑一遍,记录每个样本到最近簇的核距离,σ 的取值范围应该让正常归属样本的距离集中在 0.01~0.2 之间。σ 太大时距离普遍趋近 0,thNew 怎么调都分不开;σ 太小时正常样本的距离也在 0.5 以上,每个点都触发新建。对归一化后的三维 PDW 特征,0.25~0.35 是个合理起点。

thNew 的标定要在 σ 固定后做。它本质上是你对“一个新辐射源离已知簇要多远才算新”的容忍度。我习惯先设一个较大的 thNew 让主循环跑完,看簇总数;再逐渐减小,直到簇总数突然上涨的点就是临界值,取临界值的 1.2 倍左右作为工作点。这样既不会漏簇,也不会被噪声带偏。

4.2 分选效果评估:一对一匹配准确率与 ARI

评估在线分选结果不能直接拿 labels 和真实编号逐位比较,因为第 2 个聚类簇可能对应第 3 部雷达,聚类的簇编号是任意的。准确率的正确做法是先把簇和真实类别做一对一匹配,再统计正确率。下面的 matchAccuracy 用带约束的贪心匹配,保证一个真实类别最多被一个簇认领;ARI(调整兰德指数)则不依赖标签命名,直接度量两个划分的相似度,是更稳健的指标。

function acc = matchAccuracy(labels, truth) % 一对一贪心匹配聚类簇与真实类别,计算分选准确率 ids = unique(labels); K = numel(ids); T = max(truth); C = zeros(K, T+1); for i = 1:K for t = 0:T C(i,t+1) = sum(labels==ids(i) & truth==t); end end total = 0; Ctmp = C; for iter = 1:K [v, idx] = max(Ctmp(:)); if v == 0, break; end [i, j] = ind2sub(size(Ctmp), idx); total = total + v; Ctmp(i,:) = -1; Ctmp(:,j) = -1; % 同一簇/同一真值不可复用 end acc = total / numel(labels); end function ari = adjRandIndex(labels, truth) % 调整兰德指数:不受簇编号命名影响的分选一致性指标 n = numel(labels); C = full(sparse(labels, truth, 1)); nij = C .* (C-1) / 2; si = sum((sum(C,2)).*(sum(C,2)-1)/2); sj = sum((sum(C,1)).*(sum(C,1)-1)/2); sij = sum(nij(:)); tt = n*(n-1)/2; ari = (sij - si*sj/tt) / (0.5*(si+sj) - si*sj/tt); end

在主脚本末尾加上评估循环,每 10 个 batch 对最近 10 个 batch 的脉冲做一次滑窗评估。滑窗评估能看出聚类性能是否随时间退化,而不是只给一个全局平均分。

% 每10个batch做一次滑窗评估 nB = numel(batchX); for b = 10:10:nB lb = vertcat(allLabels{b-9:b}); tr = vertcat(batchY{b-9:b}); acc = matchAccuracy(lb, tr); ari = adjRandIndex(lb, tr); fprintf('batch %3d: 分选准确率=%.2f, ARI=%.2f\n', b, acc, ari); end

用默认参数跑完整段仿真,准确率通常在 0.9 以上,ARI 在 0.85 左右。如果你发现准确率不低但 ARI 明显低,说明有某个辐射源被拆成了多个子簇,一对一匹配会强行把它们映射到不同类别,而同一真实类别内的分割在 ARI 里会被扣分。这时候应该先调合并阈值,而不是调新簇阈值。

4.3 在线调参的注意点

在线场景的调参有个容易忽略的差别:离线调参可以对整段数据反复实验,在线系统只能看着当前窗口调。仿真阶段我的做法是“离线滚动调参”——先整段跑一遍收集所有 batch 的 labels 和距离统计,然后离线微调参数重跑,最后再用固定参数做一次真正的在线前向推理。这样既能看到参数对全局分选的影响,又不会在真实运行时反复改参数。调参时不要同时动 σ 和 thNew,两个参数在距离阈值上的作用是强耦合的,同时改会分不清是哪个参数引起的簇数变化。先固定 σ 找到 thNew,再小幅调整 σ 看簇数稳定性,两个参数交替标定两轮,基本就能收敛到一组可用值。

5. 在线核聚类分选的常见问题与排查

5.1 特征没归一化,核函数“失灵”

现象:σ 从 0.1 试到 5,分选准确率始终上不去,甚至正常样本之间的核距离比杂散脉冲还大。

原因:PDW 里三个特征量纲差距太大。RF 在 9~10 GHz,PW 在 0.5~2 μs,DOA 在 30~70°,欧氏距离几乎被 DOA 的数值差主导。RBF 核里的 ‖x-y‖² 算出来,PW 的 0.1 μs 差异在 DOA 的 10° 差异面前可以忽略,核函数的相似度完全由最大数值的特征决定。

解决:先做固定物理范围的 min-max 归一化再进入核距离计算。接收机本身知道自己的测频范围、测向范围和脉宽测量范围,用这些标称范围归一化即可,不要用流式统计的均值方差,否则范围外的新雷达样本会把归一化尺度带偏。我前面主脚本里的 xmin/xmax 就是这么设的。

5.2 簇数虚增:杂散脉冲引发“新建簇雪崩”

现象:运行到第 20 个 batch 时簇数已经超过 60,而真实辐射源只有 3 部,大量簇里只有一两个样本。

原因:杂散噪声脉冲和干扰脉冲离所有已知簇的核距离都远,thNew 一宽松就直接触发新建簇。噪声脉冲在特征空间里均匀分布,每来一个都可能开一个新簇,而这些簇之后几乎没有后续样本命中,既影响距离比较效率,也会在评估时被误认为辐射源。

解决:给新建簇加一个“观察期”,不要一出现就转正。可以给簇结构加一个 pendingN 字段,新建簇先标记为临时簇,只有累计命中超过 5 个样本才参与合并和距离比较,否则只是在等待期内做累积。另一个办法是把 thNew 从临界值往上调 20%,让少量噪声点直接归到最近的大簇里去,代价是微弱噪声点会把簇心稍微带偏。工程上我倾向于两者结合:thNew 适当放大,同时对临时簇做最小样本数过滤。

5.3 距离计算越跑越慢:缓存和核矩阵失控

现象:前 20 个 batch 每批处理时间还能接受,跑到 40 批以后明显卡顿,延迟从几十毫秒涨到几百毫秒。

原因:两个地方失控。一是某个簇的缓存样本没有被 M 限制住,内部核矩阵重算或者 inner 没有增量维护,导致距离计算退化成 O(M²)。二是临时簇太多,每个样本都要和全部 K 个簇做一次距离计算,当 K 从 3 涨到 100,单样本延迟线性恶化。

解决:缓存超限要通过 addToCluster 里的丢最旧逻辑严格截断,合并后的缓存也要重新截断到 M。簇数量控制要靠 expire 删除和合并检查,同时给每个样本增加一个候选簇预筛:先用 RF、DOA 两个特征做粗略的区间过滤,只有落在候选范围内的簇才计算核距离;核聚类本质上是精细判定,不应该承担全局搜索的任务。我通常会把“粗筛候选簇数”限制在 5 个以内,在线吞吐能稳定提升数倍。

5.4 簇心追不上雷达参数跳变

现象:某部雷达在第 30 个 batch 切换工作模式,RF 中心跳变了 0.6 GHz,之后的脉冲被反复开新簇又合并,稳定下来需要十多个 batch。

原因:在线簇心像完全由缓存里最近 M 个样本决定,跟踪能力其实不差。但合并检查间隔太长,或者 M 设置得偏大时,旧模式的样本还留在缓存里,把簇心钉在两个模式之间,导致新模式脉冲持续距离超阈值。

解决:把合并检查从每 5 批缩短到每 2 批,代价是集群合并扫描频率升高。另一种做法是让缓存带时间衰减权重——丢弃样本时优先丢弃离当前时间最远的,而不是单纯丢最旧;前面的 addToCluster 丢最旧已经实现了时间遗忘,但 M 较大时反应偏慢。雷达参数快变的场景建议 M 取 10~15,让簇心像大约只保留最近 10~15 个脉冲的记忆,模式切换基本能在 2 个 batch 内收敛。

5.5 评估结果和真值对不上:标签错位

现象:分选准确率算出来只有 30%,但看聚类图明显三个辐射源分得很清楚。

原因:评估代码直接把聚类簇编号和真实辐射源编号做了逐位相等比较。在线聚类过程中簇的编号会随新建、合并、删除而变化,第 5 号簇可能上一批还是第 2 部雷达,这批合并后已经变成第 3 部雷达。编号是逻辑标识,不是身份标识。

解决:任何在线聚类评估都必须先做簇-真值匹配,再算准确率。matchAccuracy 和 adjRandIndex 这两个函数一个做一对一映射,一个完全绕开标签命名问题,用它们就能避开这个坑。注意 matchAccuracy 虽然带约束贪心,但严格场景应该用匈牙利算法做全局最优匹配,尤其是当簇数很多、两个簇同时对一个真值类别都超过 50% 时,贪心可能选错映射方向,ARI 此时更可靠。

6. 进阶:用离线重标定验证在线簇结构

在线聚类有一个始终让人不放心的点:簇心像一直在被增量更新,你怎么确认它没有慢慢“跑偏”?我习惯每隔一段窗口做一次离线核 k-means 重标定,用同样的归一化特征、同样的核参数,把最近一段时间的脉冲重新离线聚类,然后和在线结果做一致性对比。这个技巧不需要改动在线主循环,只把当前的簇数量作为候选 K,跑一个有限的批处理核 k-means。

function consistency = offlineRefitCheck(winX, onlineLabels, sigma, Krange) % 离线重标定:用批处理核K均值重分最近窗口,与在线结果比较ARI n = size(winX,1); D = sum(winX.^2,2) + sum(winX.^2,2)' - 2*(winX*winX.'); D(D<0)=0; Kmat = exp(-D/(2*sigma^2)); bestARI = 0; for K = Krange rng(7); label = randi(K, n, 1); % 随机初始化 for it = 1:20 dist = zeros(n,K); for k = 1:K mk = find(label==k); if isempty(mk) dist(:,k) = inf; else dist(:,k) = 1 - 2*mean(Kmat(:,mk),2) ... + mean(mean(Kmat(mk,mk))); end end [~, label] = min(dist, [], 2); end ari = adjRandIndex(label, onlineLabels); bestARI = max(bestARI, ari); end consistency = bestARI; end

调用时取最近 10 个 batch 的归一化特征拼接成 winX,Krange 用在线簇数的上下各 1 个值,比如在线当前有 4 个簇,就试 3、4、5。如果 bestARI 超过 0.9,说明在线缓存的簇结构和离线全量聚类高度一致,分选是可信的;如果低于 0.85,说明在线簇心漂移明显,优先调 M 和合并阈值,而不是盲目加大 thNew。

这个离线重标定还有一个用处:给新簇确认身份。在线新出现一个簇时,可以用离线重标定的结果回看一眼,如果这个新簇在离线结果里被拆散进多个大簇,它多半是杂散脉冲;如果离线结果里它独立成簇,才值得在后续 batch 中保留并持续观察。我每次跑完仿真都会保留最后一批的簇缓存,用它做一次重标定再下结论,分选指标看得见,簇结构也经得起复核。希望这套代码和调参路径能帮你在雷达辐射源在线分选上少走几步弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询