Main Content

在数据中查找信号

此示例说明如何使用 findsignal 在数据中查找时变信号。它包括的各示例说明如何使用距离度量找到精确匹配和高度匹配的信号、如何补偿缓慢变化的偏移,以及使用动态时间规整以把采样差异考虑在内。

查找精确匹配

当您要找到信号在数值上完全一致的匹配项时,您可以使用 strfind 来执行匹配。

例如,如果我们有如下数据向量:

data = [1 4 3 2 55 2 3 1 5 2 55 2 3 1 6 4 2 55 2 3 1 6 4 2];

我们想找到以下信号的位置:

signal = [55 2 3 1];

我们可以使用 strfind 找到信号在数据中所处位置的起始索引,条件是信号和数据在数值上是完全一致的。

iStart = strfind(data,signal)
iStart = 1×3

     5    11    18

寻找匹配度最高的信号

strfind 适用于数值上完全一致的匹配。然而,当信号中可能因存在量化噪声或其他伪影而导致误差时,这种方法会失败。

例如,如果您有以下正弦波:

data = sin(2*pi*(0:25)/16);

您要找到以下信号的位置:

signal = cos(2*pi*(0:10)/16);

strfind 无法在数据中找到从第五个采样开始的正弦波:

iStart = strfind(data,signal)
iStart =

     []

strfind 无法在数据中找到该信号,因为舍入误差使得并非所有值在数值上都相等。要看到这一点,我们从匹配区域的信号中减去数据。

data(5:15) - signal
ans = 1×11
10-15 ×

         0         0         0    0.0555    0.0612    0.0555         0    0.2220         0    0.2220         0

存在 1e-15 数量级的数值差异。

要解决此问题,您可以使用 findsignal,默认情况下它会在数据中扫描该信号,并在每个匹配位置局部计算信号和数据之间的平方差之和,以寻找最小和。

要生成突出显示最佳匹配位置的信号和数据的图,您可以按如下方式调用 findsignal

findsignal(data,signal)

查找低于某阈值的最接近的匹配

默认情况下,findsignal 始终返回信号与数据最接近的匹配。要返回多个匹配项,您可以指定一个最大平方差总和的界限。

data = sin(2*pi*(0:100)/16);
signal = cos(2*pi*(0:10)/16);

findsignal(data,signal,'MaxDistance',1e-14)

findsignal 返回按接近程度进行排序的匹配项

[iStart, iStop, distance] = findsignal(data,signal,'MaxDistance',1e-14);
fprintf('iStart iStop  total squared distance\n')
iStart iStop  total squared distance
fprintf('%4i %5i     %.7g\n',[iStart; iStop; distance])
   5    15     0
  37    47     0
  69    79     0
  21    31     1.776357e-15
  53    63     1.776357e-15
  85    95     1.776357e-15

搜索存在变化偏移量的复信号轨迹

接下来的示例说明如何使用 findsignal 查找一个已知轨迹的信号。cursiveex.mat 文件包含笔尖在一张纸上描绘出 phosphorescence 一词时所记录的 x 和 y 位置。x,y 数据分别编码为复信号的实部和虚部。

load cursiveex
plot(data)
xlabel('real')
ylabel('imag')

同一执笔者描绘出字母 p 作为模板信号。

plot(signal)
title('signal')
xlabel('real')
ylabel('imag')

使用 findsignal,您可以很轻松地找到数据中的第一个 p。这是因为信号值在数据开始时对齐程度较好。

findsignal(data,signal)

但是,第二个 p 有两个特征导致 findsignal 难以识别它:它与第一个字母相比有显著而持续的偏移,并且该字母的某些部分是以不同于模板信号的速度描绘的。

如果您只对匹配字母的整体形状感兴趣,可以从信号和数据元素中减去经加窗的局部均值。这样可以减轻持续的偏移产生的影响。

为了减轻描绘字母时的速度变化的影响,您可以使用动态时间规整,它会在执行搜索时将信号或数据拉伸到一个共同的时基:

findsignal(data,signal,'TimeAlignment','dtw', ...
               'Normalization','center', ...
               'NormalizationLength',600, ...
               'MaxNumSegments',2)

查找进行时间拉伸后的功率信号

接下来的示例说明如何使用 findsignal 来查找短语语音中某发音单词的位置。

以下文件包含短语Accelerating the pace of engineering and science 的录音,还包含同一位发音人单独的 engineering 一词的录音。

load slogan
soundsc(phrase,fs)
soundsc(hotword,fs)

同一位发音人在一个句子或短语中改变单个词的发音是很常见的。此示例中的发音人以两种不同的方式读出 engineering 这个词:发音人在短语中用了大约 0.5 秒读出该单词,重音放在第二个音节 (en-GIN-eer-ing);同一发音人用了 0.75 秒单独读出该单词,重音放在第三个音节 (en-gin-EER-ing)。

为了补偿时间和音量的这些局部差异,您可以使用频谱图来报告谱功率分布随时间的变化。

首先,使用一个频率分辨率比较粗糙的频谱图。这样做是为了有意模糊声道的窄带声门脉冲,而让口腔和鼻腔的宽带共振不受干扰。这可以让您锁定一个单词中读出的元音。用频谱图来识别辅音(尤其是爆破音和摩擦音)要困难得多。以下代码用于计算频谱图

Nwindow = 64;
Nstride = 8;
Beta = 64;

Noverlap = Nwindow - Nstride;
[~,~,~,PxxPhrase] = spectrogram(phrase, kaiser(Nwindow,Beta), Noverlap);
[~,~,~,PxxHotWord] = spectrogram(hotword, kaiser(Nwindow,Beta), Noverlap);

现在您已有短语和搜索词的频谱图,您可以使用动态时间规整以将局部的单词长度差异考虑在内。同样,您可以通过结合使用功率归一化和对称 Kullback-Leibler 距离来将功率差异考虑在内。

[istart,istop] = findsignal(PxxPhrase, PxxHotWord, ...
    'Normalization','power','TimeAlignment','dtw','Metric','symmkl')
istart = 1144
istop = 1575

绘制和播放识别的单词。

findsignal(PxxPhrase, PxxHotWord, 'Normalization','power', ...
    'TimeAlignment','dtw','Metric','symmkl')

soundsc(phrase(Nstride*istart-Nwindow/2 : Nstride*istop+Nwindow/2),fs)

另请参阅