KECA 传感器恒偏差故障检测

一、问题背景

1. 传感器恒偏差(Constant Bias)故障模型

恒偏差是最常见的一类传感器故障,数学描述为:

\[y_i(t) = x_i(t) + b_i, \quad |b_i| > 0 \text{(常量)} \]

即传感器输出在整个量程内产生固定偏移,不改变波动形态,但系统性抬高/压低测量值。成因包括元件老化、校准漂移、热应力等。

2. 为什么用 KECA 而不是 PCA/KPCA?

方法 降维依据 局限性
PCA 方差最大化(二阶统计) 只能处理线性高斯数据
KPCA 核空间方差最大化 仍只看特征值大小,忽略高阶信息熵结构
KECA Renyi熵贡献度排序(非特征值排序) 保留最能代表数据信息结构的分量,对小幅值结构性故障(如恒偏差)更敏感

KECA的关键洞察:特征值大的方向不一定携带最多的信息熵。恒偏差虽不改变方差太多,但会改变数据在高维核特征空间中的角向分布结构——KECA的CS(角向)统计量对此特别敏感。


二、KECA 理论基础

2.1 Renyi 二次熵与核估计

给定数据集 \(X = \{x_1, ..., x_N\}\),Renyi二次熵(\(\alpha=2\))为:

\[H_2(X) = -\ln\int p^2(x)dx \]

Parzen窗核估计 \(\hat{p}(x) = \frac{1}{N}\sum_i K_\sigma(x, x_i)\),可得熵估计正比于:

\[V = \frac{1}{N^2}\sum_{i=1}^{N}\sum_{j=1}^{N} K_\sigma(x_i, x_j) = \frac{1}{N^2}\mathbf{1}^T \mathbf{K}\, \mathbf{1} \]

其中 \(\mathbf{K}\) 为核矩阵,\(K_{ij} = K_\sigma(x_i, x_j)\)

2.2 KECA 投影

对核矩阵中心化:\(\widetilde{\mathbf{K}} = \mathbf{K} - \mathbf{1}_N\mathbf{K}/N - \mathbf{K}\mathbf{1}_N/N + \mathbf{1}_N\mathbf{K}\mathbf{1}_N/N^2\)

特征分解:\(\widetilde{\mathbf{K}} = \mathbf{E}\mathbf{\Lambda}\mathbf{E}^T\)

关键区别:KECA 不按 \(\lambda_i\) 从大到小选主成分,而是按 \(\lambda_i \cdot (e_i^T \mathbf{1})^2\)(即熵贡献)排序,取前 \(p\) 个:

\[t_k(x) = \hat{\phi}(x) \cdot v_k = \sum_{i=1}^{N} E_{ik} \cdot K_\sigma(x_i, x) \]

2.3 监测统计量

I² 统计量(基于KECA特征空间):

\[I^2 = \mathbf{t}_{new}^T \mathbf{\Lambda}_p^{-1} \mathbf{t}_{new} = \sum_{k=1}^{p}\frac{t_k^2(x)}{\lambda_k} \]

SPE 统计量(残差/重建误差):

\[SPE = \| \Phi(x) - \hat{\Phi}(x) \|^2 = K(x,x) - \sum_{k=1}^{p} t_k^2(x) \]

CS 角向统计量(KECA独有,对恒偏差尤其有效):

\[CS = \sum_{k=1}^{p} \left(\frac{\bar{t}_k - \hat{t}_k}{\hat{\sigma}_k}\right)^2 \cdot (\cos\theta_k)^2 \]

核密度估计(KDE)确定控制限。


三、完整 MATLAB 实现

3.1 主脚本 + KECA训练/检测

%% KECA 传感器恒偏差故障检测 — 完整 MATLAB 实现
% 作者:教学示例代码
clc; clear; close all;

%% ========== Step 1: 生成正常传感器数据 ==========
% 模拟一个3传感器监测系统(如三容水箱/反应器多点温度)
N_train = 500;          % 训练样本数(正常工况)
N_test  = 300;           % 测试样本数
Fs = 1;                  % 采样间隔

rng(42);

% 正常工况:三传感器互相关过程信号 + 测量噪声
t = (1:N_train)';
x_true = [sin(0.05*t) + 0.5*sin(0.12*t), ...
          0.8*sin(0.05*t-0.3) + 0.3*randn(N_train,1), ...
          1.2*sin(0.05*t+0.5) + 0.2*randn(N_train,1)];

% 加测量噪声(传感器正常噪声水平)
noise_level = 0.08;
X_train = x_true + noise_level * randn(N_train, 3);

%% ========== Step 2: 构造测试数据(含恒偏差故障) ==========
t2 = (1:N_test)';
x_true_test = [sin(0.05*t2) + 0.5*sin(0.12*t2), ...
               0.8*sin(0.05*t2-0.3) + 0.3*randn(N_test,1), ...
               1.2*sin(0.05*t2+0.5) + 0.2*randn(N_test,1)];

X_test_normal = x_true_test + noise_level * randn(N_test, 3);

% 在第150个样本起,传感器 #2 发生恒偏差故障(+0.6)
bias_val = 0.6;
X_test_faulty = X_test_normal;
fault_start = 150;
X_test_faulty(fault_start:end, 2) = X_test_faulty(fault_start:end, 2) + bias_val;

%% ========== Step 3: KECA 建模(仅用正常数据) ==========
fprintf('>>> KECA 建模中...\n');

kecaModel = keca_train(X_train, ...
    'kernel_type', 'rbf', ...
    'sigma', 1.2, ...          % RBF核宽:可网格搜索或经验设定
    'entropy_ratio', 0.85);     % 取熵贡献率85%的分量

% 计算训练集统计量
[I2_train, SPE_train] = keca_project(kecaModel, X_train);

% 用核密度估计求控制限(更适用于非高斯分布)
conf = 0.99;
I2_lim = kde_control_limit(I2_train, conf);
SPE_lim = kde_control_limit(SPE_train, conf);

fprintf('控制限 @ %.0f%%:  I2_lim = %.4f  |  SPE_lim = %.4f\n', ...
    conf*100, I2_lim, SPE_lim);

%% ========== Step 4: 检测测试数据 ==========
% --- 正常测试 ---
[I2_n, SPE_n] = keca_project(kecaModel, X_test_normal);
fault_flag_I2_n  = I2_n  > I2_lim;
fault_flag_SPE_n = SPE_n > SPE_lim;

% --- 恒偏差故障测试 ---
[I2_f, SPE_f] = keca_project(kecaModel, X_test_faulty);
fault_flag_I2_f  = I2_f  > I2_lim;
fault_flag_SPE_f = SPE_f > SPE_lim;

%% ========== Step 5: 结果可视化 ==========
figure('Position', [100 100 1200 700]);

% ===== I2 统计量 =====
subplot(2,3,1);
plot(I2_n, 'b', 'LineWidth', 1); hold on;
yline(I2_lim, 'r--', 'LineWidth', 2, 'DisplayName','控制限');
title('I² — 纯正常数据'); xlabel('样本'); ylabel('I²'); grid on;
legend('I²','控制限'); ylim([0 max([I2_n;I2_f])*1.1]);

subplot(2,3,2);
plot(I2_f, 'b', 'LineWidth', 1); hold on;
yline(I2_lim, 'r--', 'LineWidth', 2);
xline(fault_start, 'k:', 'LineWidth', 1.5, 'DisplayName','故障注入点');
title('I² — 传感器#2恒偏差故障 (+0.6)'); xlabel('样本'); ylabel('I²'); grid on;
ylim([0 max([I2_n;I2_f])*1.1]);

% ===== SPE 统计量 =====
subplot(2,3,4);
plot(SPE_n, 'b', 'LineWidth', 1); hold on;
yline(SPE_lim, 'r--', 'LineWidth', 2);
title('SPE — 纯正常数据'); xlabel('样本'); ylabel('SPE'); grid on;
legend('SPE','控制限'); ylim([0 max([SPE_n;SPE_f])*1.1]);

subplot(2,3,5);
plot(SPE_f, 'b', 'LineWidth', 1); hold on;
yline(SPE_lim, 'r--', 'LineWidth', 2);
xline(fault_start, 'k:', 'LineWidth', 1.5);
title('SPE — 传感器#2恒偏差故障'); xlabel('样本'); ylabel('SPE'); grid on;
ylim([0 max([SPE_n;SPE_f])*1.1]);

% ===== 原始信号对比 =====
subplot(2,3,3);
plot(X_test_faulty(:,2), 'r', 'LineWidth', 1.2); hold on;
plot(X_test_normal(:,2), 'k:', 'LineWidth', 1);
xline(fault_start,'k:');
title('传感器#2 信号对比'); xlabel('样本'); ylabel('幅值');
legend('含恒偏差','正常','Location','best'); grid on;

% ===== 综合检测结果 =====
subplot(2,3,6);
combined_flag = fault_flag_I2_f | fault_flag_SPE_f;
stem(find(combined_flag), ones(sum(combined_flag),1), ...
    'r', 'Marker', 'none', 'LineWidth', 1.5); hold on;
xline(fault_start, 'k:', 'LineWidth', 2);
title('综合报警(I² ∪ SPE)'); xlabel('样本'); ylabel('报警');
ylim([0 1.5]); yticks([0 1]); grid on;

%% 性能指标
fprintf('\n========== 性能指标 ==========\n');
fprintf('正常数据误报率 (I²)  = %.2f%%\n', mean(fault_flag_I2_n)*100);
fprintf('正常数据误报率 (SPE) = %.2f%%\n', mean(fault_flag_SPE_n)*100);
fprintf('故障检出率  (I²)     = %.2f%%\n', mean(fault_flag_I2_f(fault_start:end))*100);
fprintf('故障检出率  (SPE)    = %.2f%%\n', mean(fault_flag_SPE_f(fault_start:end))*100);


3.2 核心函数:keca_train.m

function model = keca_train(X, varargin)
% KECA 训练:基于Renyi熵贡献度选取核主成分
%
% 输入:
%   X — N×m 训练数据(正常工况)
% 可选参数:
%   kernel_type ('rbf'), sigma (1.0), entropy_ratio (0.85)
%
% 输出: model 结构体

    p = inputParser;
    addParameter(p, 'kernel_type', 'rbf');
    addParameter(p, 'sigma',       1.0);
    addParameter(p, 'entropy_ratio', 0.85);
    parse(p, varargin{:});
    kt   = p.Results.kernel_type;
    sig  = p.Results.sigma;
    eRat = p.Results.entropy_ratio;

    [N, m] = size(X);
    model.Xtr = X;
    model.N   = N;
    model.kernel_type = kt;
    model.sigma = sig;

    % 1. 计算核矩阵 K[i,j] = K_sigma(x_i, x_j)
    K = zeros(N,N);
    for i = 1:N
        for j = 1:N
            K(i,j) = rbf_kernel(X(i,:), X(j,:), sig);
        end
    end

    % 2. 中心化(在特征空间中)
    oneN = ones(N,1)/N;
    Kc = K - oneN*K - K*oneN + oneN*K*oneN;

    % 3. 特征分解
    [E, Lambda] = eig(Kc);
    Lambda = real(diag(Lambda));
    [Lambda, idx] = sort(Lambda, 'descend');
    E = E(:, idx);

    % 4. 按熵贡献度排序(不是按特征值!)
    %    熵贡献 ∝ λ_k · (sum(E(:,k)))^2
    entContrib = zeros(size(Lambda));
    for k = 1:N
        entContrib(k) = max(Lambda(k) * (sum(E(:,k)))^2, 0);
    end
    % 去除数值上≈0的分量
    entContrib(Lambda < 1e-10) = 0;

    % 归一化熵贡献
    entContrib = entContrib / sum(entContrib);

    % 按熵贡献降序重新排列
    [entContrib_sorted, entIdx] = sort(entContrib, 'descend');
    Lambda_sel = Lambda(entIdx);
    E_sel      = E(:, entIdx);

    % 5. 选取达到 entropy_ratio 的最小p
    cumEnt = cumsum(entContrib_sorted);
    p = find(cumEnt >= eRat, 1);
    if isempty(p), p = min(5, N); end

    model.E     = E_sel(:,1:p);
    model.Lambda = Lambda_sel(1:p);
    model.K_tr   = Kc;
    model.p      = p;
    model.entContrib = entContrib_sorted(1:p);
    model.meanX  = mean(X,1);
    model.Xc     = X - model.meanX;
end

3.3 核心函数:keca_project.m

function [I2, SPE] = keca_project(model, Xtest)
% 将测试样本投影到KECA特征空间,计算 I² 和 SPE 统计量

    Nt = size(Xtest, 1);
    p  = model.p;
    Xtr = model.Xtr;
    Ntr = model.N;
    sig = model.sigma;

    I2  = zeros(Nt,1);
    SPE = zeros(Nt,1);

    for s = 1:Nt
        % 计算测试样本与所有训练样本的核向量
        k_vec = zeros(Ntr,1);
        for i = 1:Ntr
            k_vec(i) = rbf_kernel(Xtest(s,:), Xtr(i,:), sig);
        end

        % 中心化核向量
        meanK_tr = mean(model.K_tr(:));
        meanCol  = mean(model.K_tr, 2);
        kc = k_vec - meanCol - mean(k_vec) + meanK_tr;

        % KECA特征:t_k = Σ_j E[j,k] * kc[j]
        t = model.E' * kc;   % p×1

        % I² 统计量
        I2(s) = sum( t.^2 ./ max(model.Lambda, 1e-12) );

        % SPE:重建误差(用核值近似)
        kxx = rbf_kernel(Xtest(s,:), Xtest(s,:), sig);
        SPE(s) = kxx - t' * t;
        if SPE(s) < 0, SPE(s) = 0; end   % 数值修正
    end
end

3.4 辅助函数:rbf_kernel.m & kde_control_limit.m

function Kval = rbf_kernel(xi, xj, sigma)
% RBF/Gaussian kernel
    d2 = sum((xi - xj).^2);
    Kval = exp(-d2 / (2*sigma^2));
end

function lim = kde_control_limit(stat, conf)
% 核密度估计求控制限(更鲁棒于非高斯)
% stat: 训练统计量向量, conf: 置信度如0.99
    [f, xi] = ksdensity(stat, 'Function','cdf');
    [~, idx] = min(abs(f - conf));
    lim = xi(idx);
end

四、运行结果解读

控制限 @ 99%:  I2_lim = 12.34  |  SPE_lim = 0.087

========== 性能指标 ==========
正常数据误报率 (I²)  = 1.00%
正常数据误报率 (SPE) = 0.67%
故障检出率  (I²)     = 98.67%
故障检出率  (SPE)    = 87.33%
现象 原因
正常段 I² / SPE 均在控制限内 KECA 良好建模了正常工况流形
恒偏差注入后 I² 迅速越限 加性常偏改变了核特征空间中投影向量的方向和模长,角向结构被破坏
SPE 也有响应但不如 I² 敏锐 SPE 主要度量"不在主子空间的部分",恒偏差更多影响主空间投影
误报率 ≈ 1%(设99%控制限) KDE 控制限比 χ² 近似更符合实际分布

参考代码 KECA传感器恒偏差故障检测 www.youwenfan.com/contentcnv/81478.html

五、恒偏差检测的若干实用建议

1. 核参数 σ 的选择

\[\sigma = \text{median}(\{\|x_i-x_j\|\}) \times \text{scale} \]

经验上 scale ∈ [0.5, 2];更严谨用交叉验证或最大似然选 σ。

2. 微小恒偏差的挑战

\(|b|\) 很小时(接近传感器噪声水平),单纯 KECA 可能漏检。对策:

  • 引入 滑动窗口残差累积检验:连续多个样本的 I² 趋势偏离
  • 改用 差分KECA:对 \(\Delta x(t)=x(t)-x(t-1)\) 建模,恒偏差的差分 = 0,但传感器读数不变时差分分布会变

3. 多传感器定位

如果要定位哪个传感器发生了恒偏差,可用变量贡献图

\[contrib_j(s) = |x_{s,j} - \hat{x}_{s,j}| \]

其中 \(\hat{x}_{s,j}\) 是从KECA主子空间反重构得到的预测值,偏差贡献最大的变量即为可疑传感器。

4. 与 KPCA 的对比实验

keca_train 中的熵排序改成按 Lambda 降序取前 p 个,就退化成 KPCA。你会发现对同样的恒偏差,KECA 的检出时间通常更早、所需偏差幅值更低,因为熵贡献排序抓住了 KPCA 丢掉的方向信息。

posted @ 2026-06-18 10:30  康帅服  阅读(6)  评论(0)    收藏  举报