В этом примере показано, как использовать 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])53 63 0 69 79 0 85 95 0 5 15 1.776357e-15 21 31 1.776357e-15 37 47 1.776357e-15
В следующем примере показано, как использовать findsignal найти сигнал, отслеживающий известную траекторию. Файл «cursiveex.mat» содержит запись x- и y- положения кончика пера, так как он прослеживал слово «фосфоресценция» на листке бумаги. Данные x, y кодируются как действительная и мнимая составляющие комплексного сигнала соответственно.
load cursiveex plot(data) xlabel('real') ylabel('imag')

Тот же писатель отследил букву «p» как шаблонный сигнал.
plot(signal) title('signal') xlabel('real') ylabel('imag')

Вы можете найти первый «p» в данных довольно легко с помощью findsignal. Это происходит потому, что значения сигнальной линии достаточно хорошо подаются в начале данных.
findsignal(data,signal)

Однако вторая «p» имеет две характеристики, затрудняющие findsignal для идентификации: Он имеет значительное, но постоянное смещение от первой буквы, и части буквы были нарисованы с другой скоростью, чем шаблон сигнала.
Если вы заинтересованы в том, чтобы просто сопоставить общую форму буквы, вы можете вычесть окошечное локальное среднее как из сигнала, так и из элемента данных. Это позволяет смягчить эффект постоянных сдвигов.
Чтобы смягчить влияние изменяющихся скоростей, с которыми рисуются буквы, можно использовать динамическое искажение времени, которое растягивает либо сигнал, либо данные на общую базу времени при выполнении поиска:
findsignal(data,signal,'TimeAlignment','dtw', ... 'Normalization','center', ... 'NormalizationLength',600, ... 'MaxNumSegments',2)

В следующем примере показано, как использовать findsignal чтобы найти местоположение произнесенного слова во фразе.
Следующий файл содержит аудиозапись фразы: «Ускорение темпов инженерии и науки» и отдельную аудиозапись «инженерии», произнесённой тем же оратором.
load slogan
soundsc(phrase,fs)
soundsc(hotword,fs)Один и тот же оратор обычно варьирует произношение отдельных произносимых слов в предложении или фразе. Говорящий в этом примере произносил «инженерный» двумя разными способами: Говорящему потребовалось примерно 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);
Теперь, когда у вас есть спектрограмма фразы и поискового слова, вы можете использовать динамическое искажение времени, чтобы учесть локальные изменения длины слова. Аналогично, можно учитывать вариации мощности, используя нормализацию мощности в сочетании с симметричным расстоянием Куллбэка-Лейблера.
[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)