4. 特征提取技术:峰值检测、斜率变化点、积分面积比、主成分分析(PCA)

各位做刻蚀工艺的同行,咱们今天聊聊特征提取。说白了,就是从原始的光学发射光谱(OES)信号里,把那些能反映刻蚀终点的小特征给揪出来。

我刚开始做终点检测那会儿,总觉得原始信号挺干净的,直接设个阈值不就完了?后来被现实狠狠教育了一顿——等离子体噪声、腔室老化、气体流量波动,哪个都能把信号搞得面目全非。所以,特征提取这一步,其实比你想的重要得多。

4.1 峰值检测:最直观的终点信号

峰值检测,名字听着简单,做起来有讲究。我们通常关注的是特定波长的发射强度变化。比如刻蚀SiO₂时,我会盯着483nm的CO谱线——当氧化物被刻穿,CO信号会突然下降,形成一个明显的峰值拐点。

核心思路: 不是找最高点,而是找变化最剧烈的点。

我习惯用一阶导数来辅助判断。代码实现也不复杂:

import numpy as np

def peak_detection(signal, threshold=0.05):
    """
    基于一阶导数的峰值检测
    signal: 原始OES信号
    threshold: 变化率阈值
    """
    diff = np.diff(signal)
    # 找导数由正转负的点
    peaks = []
    for i in range(1, len(diff)):
        if diff[i-1] > threshold and diff[i] < -threshold:
            peaks.append(i)
    return peaks

嗯,这里要注意。阈值设得太小,噪声会被当成终点;设得太大,可能终点都过了还没触发。我一般会先用一段历史数据跑一遍,看看正常终点的导数幅值大概在什么范围。

实战技巧: 我曾经遇到过一个案例,信号里有个假峰,跟终点峰长得几乎一模一样。后来我加了宽度约束——真正的终点峰通常持续几十毫秒,而噪声尖峰一闪就没了。加个最小宽度过滤,效果立竿见影。

4.2 斜率变化点:捕捉细微的转折

有些刻蚀工艺,终点信号不是陡降,而是缓慢变化。比如深硅刻蚀,终点信号可能就是个平缓的斜坡。这时候峰值检测就不太灵了。

斜率变化点,说白了就是找信号斜率发生明显改变的位置。我常用的是滑动窗口线性拟合:

def slope_change_point(signal, window=10):
    """
    滑动窗口计算斜率变化
    """
    slopes = []
    for i in range(len(signal) - window):
        x = np.arange(window)
        y = signal[i:i+window]
        coeffs = np.polyfit(x, y, 1)
        slopes.append(coeffs[0])
    
    # 找斜率变化最大的位置
    slope_diff = np.diff(slopes)
    change_point = np.argmax(np.abs(slope_diff))
    return change_point

你想想看,窗口大小怎么选?窗口太小,对噪声敏感;窗口太大,反应迟钝。我个人习惯窗口大小取采样率的1/10左右,比如采样率100Hz,窗口就设10个点。

避坑指南: 我曾经在刻蚀氮化硅时用过这个方法,结果发现斜率变化点总是滞后。后来排查发现,是因为窗口内包含了终点后的数据,把斜率拉平了。解决办法是只取终点前一段数据做拟合,或者用加权窗口,越靠近当前时刻权重越大。

4.3 积分面积比:抗噪声的利器

积分面积比,这招是我从老工程师那儿学来的。原理很简单:选两条谱线,一条对刻蚀过程敏感(比如反应物),一条相对稳定(比如载气)。然后计算它们的积分面积比值。

为什么这招好用?因为噪声通常是共模的——两条谱线同时受干扰,但比值变化不大。这就相当于做了个共模抑制。

谱线选择 敏感谱线 参考谱线 应用场景
SiO₂刻蚀 483nm (CO) 703nm (Ar) 氧化物刻蚀终点
Si刻蚀 288nm (Si) 703nm (Ar) 硅刻蚀终点
金属刻蚀 396nm (Al) 750nm (Ar) 铝刻蚀终点
def integral_ratio(signal_sensitive, signal_ref, window=50):
    """
    计算积分面积比
    """
    int_sensitive = np.sum(signal_sensitive[-window:])
    int_ref = np.sum(signal_ref[-window:])
    
    if int_ref == 0:
        return 0
    return int_sensitive / int_ref

我建议积分窗口不要太大,50个采样点左右就够。窗口太大,会把历史数据也卷进来,终点信号反而被稀释了。

4.4 主成分分析(PCA):降维的艺术

PCA,听起来高大上,其实核心就一句话:把多个波长的信号压缩成几个关键成分。你想想看,OES数据动辄几百个波长通道,每个通道都分析一遍,不现实。PCA能帮你找到那些「最能解释数据变化」的方向。

我做PCA的流程是这样的:

  1. 数据标准化:每个波长通道减去均值,除以标准差。这一步不能省,不然强信号通道会主导分析结果。
  2. 计算协方差矩阵:看看各通道之间的相关性。
  3. 特征值分解:找到主成分方向。
  4. 投影:把原始数据投影到前几个主成分上。
from sklearn.decomposition import PCA

def oes_pca(data, n_components=2):
    """
    data: 形状为 (n_samples, n_wavelengths) 的OES数据
    n_components: 保留的主成分数量
    """
    # 标准化
    data_std = (data - np.mean(data, axis=0)) / np.std(data, axis=0)
    
    # PCA
    pca = PCA(n_components=n_components)
    transformed = pca.fit_transform(data_std)
    
    # 查看各主成分的方差解释比例
    explained_variance = pca.explained_variance_ratio_
    print(f"前{n_components}个主成分解释了 {np.sum(explained_variance)*100:.1f}% 的方差")
    
    return transformed, pca

关键点: 我一般保留前2-3个主成分。第一个主成分通常代表整体强度变化,第二个主成分往往能捕捉到终点信号。为什么?因为终点信号是局部变化,跟整体漂移是正交的。

我记得有一次,客户说他们的终点信号被腔室老化干扰得完全看不出来。我拿PCA一跑,第二主成分里终点信号清清楚楚。说白了,PCA帮你把「有用信号」和「干扰信号」分到了不同的通道里。

实战建议: PCA模型不是一劳永逸的。腔室条件变了、工艺配方改了,之前训练的主成分可能就不适用了。我习惯每周重新训练一次PCA模型,或者在新工艺验证时重新跑一遍。

4.5 四种方法的对比与选择

说了这么多,到底该用哪种?我整理了个对比表,方便你根据实际情况选:

方法 优点 缺点 适用场景
峰值检测 简单直观,计算快 对噪声敏感,需要调参 信号变化剧烈的工艺
斜率变化点 能捕捉缓慢变化 窗口大小难选 深硅刻蚀等缓变工艺
积分面积比 抗噪声能力强 需要选两条合适的谱线 噪声大的工艺环境
PCA 自动降维,提取关键信息 需要训练数据,解释性差 多波长、复杂工艺

我个人习惯是:先用PCA看看数据里有没有明显的终点特征。如果有,再根据信号特点选峰值检测或斜率变化点。如果噪声大,就上积分面积比。四种方法不是互斥的,组合使用效果更好。

好了,特征提取这块就聊到这儿。下一节咱们会讲怎么把这些特征组合成最终的终点判决逻辑。记住,没有万能的方法,只有最适合你工艺的方法。


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