Речевая генерация кода распознавания команды на Raspberry Pi

В этом примере показано, как развернуть извлечение признаков и сверточную нейронную сеть (CNN) для речевого распознавания команды к Raspberry Pi™. Чтобы сгенерировать извлечение признаков и сетевой код, вы используете MATLAB Coder, Пакет поддержки MATLAB для Оборудования Raspberry Pi и Библиотеку ARM® Compute. В этом примере сгенерированный код является исполняемым файлом на вашем Raspberry Pi, который вызван скриптом MATLAB, который отображает предсказанную речевую команду наряду с и слуховой спектрограммой сигнала. Взаимодействие между скриптом MATLAB и исполняемым файлом на вашем Raspberry Pi обработано с помощью пользовательского дейтаграммного протокола (UDP). Для получения дополнительной информации о предварительной обработке аудио и сетевом обучении, смотрите Распознание речевых команд с использованием глубокого обучения (Audio Toolbox).

Необходимые условия

  • Процессор ARM, который поддерживает расширение NEON

  • ARM Вычисляет версию 19.05 Библиотеки (на целевом оборудовании ARM)

  • Переменные окружения для компиляторов и библиотек

Для поддерживаемых версий библиотек и для получения информации о подготовке переменных окружения, смотрите Необходимые условия для Глубокого обучения для MATLAB Coder (MATLAB Coder).

Потоковая передача демонстрации в MATLAB

Используйте те же параметры для трубопровода извлечения признаков и классификации, как разработано в Распознании речевых команд с использованием глубокого обучения (Audio Toolbox).

Задайте ту же частоту дискретизации, сеть была обучена на (16 кГц). Задайте уровень классификации и количество входа аудиосэмплов на систему координат. Вход функции к сети является спектрограммой Коры, которая соответствует 1 секунде аудиоданных. Спектрограмма Коры вычисляется для 25 MS Windows с транзитными участками на 10 мс. Вычислите количество отдельных спектров в каждой спектрограмме.

fs = 16000;
classificationRate = 20;
samplesPerCapture = fs/classificationRate;

segmentDuration = 1;
segmentSamples = round(segmentDuration*fs);

frameDuration = 0.025;
frameSamples = round(frameDuration*fs);

hopDuration = 0.010;
hopSamples = round(hopDuration*fs);

numSpectrumPerSpectrogram = floor((segmentSamples-frameSamples)/hopSamples) + 1;

Создайте audioFeatureExtractor Объект (Audio Toolbox) извлечь спектрограммы Коры с 50 полосами без нормализации окна. Вычислите число элементов в каждой спектрограмме.

afe = audioFeatureExtractor( ...
    'SampleRate',fs, ...
    'FFTLength',512, ...
    'Window',hann(frameSamples,'periodic'), ...
    'OverlapLength',frameSamples - hopSamples, ...
    'barkSpectrum',true);

numBands = 50;
setExtractorParams(afe,'barkSpectrum','NumBands',numBands,'WindowNormalization',false);

numElementsPerSpectrogram = numSpectrumPerSpectrogram*numBands;

Загрузите предварительно обученный CNN и метки.

load('commandNet.mat')
labels = trainedNet.Layers(end).Classes;
NumLabels = numel(labels);
BackGroundIdx = find(labels == 'background'); 

Задайте буферы и пороги решения, чтобы отправить предсказания сети процесса.

probBuffer = single(zeros([NumLabels,classificationRate/2]));
YBuffer = single(NumLabels * ones(1, classificationRate/2)); 

countThreshold = ceil(classificationRate*0.2);
probThreshold = single(0.7);

Создайте audioDeviceReader Объект (Audio Toolbox) считать аудио из вашего устройства. Создайте dsp.AsyncBuffer Объект (DSP System Toolbox) буферизовать аудио во фрагменты.

adr = audioDeviceReader('SampleRate',fs,'SamplesPerFrame',samplesPerCapture,'OutputDataType','single');
audioBuffer = dsp.AsyncBuffer(fs);

Создайте dsp.MatrixViewer Объект (DSP System Toolbox) и timescope Объект (DSP System Toolbox) отобразить результаты.

matrixViewer = dsp.MatrixViewer("ColorBarLabel","Power per band (dB/Band)",...
    "XLabel","Frames",...
    "YLabel","Bark Bands", ...
    "Position",[400 100 600 250], ...
    "ColorLimits",[-4 2.6445], ...
    "AxisOrigin","Lower left corner", ...
    "Name","Speech Command Recognition using Deep Learning");

timeScope = timescope("SampleRate",fs, ...
    "YLimits",[-1 1], ...
    "Position",[400 380 600 250], ...
    "Name","Speech Command Recognition Using Deep Learning", ...
    "TimeSpanSource","Property", ...
    "TimeSpan",1, ...
    "BufferLength",fs, ...
    "YLabel","Amplitude", ...
    "ShowGrid",true);

Покажите осциллограф времени и матричное средство просмотра. Обнаружьте команды, пока и осциллограф времени и матричное средство просмотра открыты или пока ограничение по времени не достигнуто. Чтобы остановить живое обнаружение перед, ограничение по времени достигнуто, закройте окно scope времени или матричное окно средства просмотра.

show(timeScope)
show(matrixViewer)

timeLimit = 10;

tic
while isVisible(timeScope) && isVisible(matrixViewer) && toc < timeLimit
    % Capture audio
    x = adr();
    write(audioBuffer,x);
    y = read(audioBuffer,fs,fs-samplesPerCapture);
    
    % Compute auditory features
    features = extract(afe,y);
    auditoryFeatures = log10(features + 1e-6);
    
    % Perform prediction
    probs = predict(trainedNet, auditoryFeatures);      
    [~, YPredicted] = max(probs);
    
    % Perform statistical post processing
    YBuffer = [YBuffer(2:end),YPredicted];
    probBuffer = [probBuffer(:,2:end),probs(:)];

    [YModeIdx, count] = mode(YBuffer);
    maxProb = max(probBuffer(YModeIdx,:));

    if YModeIdx == single(BackGroundIdx) || single(count) < countThreshold || maxProb < probThreshold
        speechCommandIdx = BackGroundIdx;
    else
        speechCommandIdx = YModeIdx;
    end
    
    % Update plots
    matrixViewer(auditoryFeatures');
    timeScope(x);

    if (speechCommandIdx == BackGroundIdx)
        timeScope.Title = ' ';
    else
        timeScope.Title = char(labels(speechCommandIdx));
    end
    drawnow limitrate 
end   

Скройте осциллографы.

hide(matrixViewer)
hide(timeScope)

Подготовьте код MATLAB к развертыванию

Чтобы создать функцию, чтобы выполнить извлечение признаков, совместимое с генерацией кода, вызовите generateMATLABFunction (Audio Toolbox) на audioFeatureExtractor объект. generateMATLABFunction объектная функция создает автономную функцию, которая выполняет эквивалентное извлечение признаков и совместима с генерацией кода.

generateMATLABFunction(afe,'extractSpeechFeatures')

HelperSpeechCommandRecognitionRasPi поддерживание функции инкапсулирует извлечение признаков и сетевой процесс предсказания, продемонстрированный ранее. Так, чтобы извлечение признаков было совместимо с генерацией кода, извлечение признаков обработано сгенерированным extractSpeechFeatures функция. Так, чтобы сеть была совместима с генерацией кода, функция поддержки использует coder.loadDeepLearningNetwork (MATLAB Coder) функция, чтобы загрузить сеть. Функция поддержки использует dsp.UDPReceiver (DSP System Toolbox) системный объект, чтобы отправить слуховую спектрограмму и индекс, соответствующий предсказанной речевой команде от Raspberry Pi до MATLAB. Функция поддержки использует dsp.UDPReceiver (DSP System Toolbox) системный объект, чтобы получить аудио, записанное вашим микрофоном в MATLAB.

Сгенерируйте исполняемый файл на Raspberry Pi

Замените hostIPAddress с адресом вашей машины. Ваш Raspberry Pi отправляет слуховые спектрограммы и предсказанную речевую команду к этому IP-адресу.

hostIPAddress = coder.Constant('172.18.230.30');

Создайте объект настройки генерации кода сгенерировать исполняемую программу. Задайте выходной язык как C++.

cfg = coder.config('exe');
cfg.TargetLang = 'C++';

Создайте объект настройки для генерации кода глубокого обучения с ARM, вычисляют библиотеку, которая находится на вашем Raspberry Pi. Задайте архитектуру Raspberry Pi и присоедините объект настройки глубокого обучения к объекту настройки генерации кода.

dlcfg = coder.DeepLearningConfig('arm-compute');
dlcfg.ArmArchitecture = 'armv7';
dlcfg.ArmComputeVersion = '19.05';
cfg.DeepLearningConfig = dlcfg;

Используйте функцию Пакета поддержки Raspberry Pi, raspi, создать связь с вашим Raspberry Pi. В следующем коде, замене:

  • raspiname с именем вашего Raspberry Pi

  • пи с вашим именем пользователя

  • password с вашим паролем

r = raspi('raspiname','pi','password');

Создайте coder.hardware Объект (MATLAB Coder) для Raspberry Pi и присоединения это к объекту настройки генерации кода.

hw = coder.hardware('Raspberry Pi');
cfg.Hardware = hw;

Задайте папку сборки на Raspberry Pi.

buildDir = '~/remoteBuildDir';
cfg.Hardware.BuildDir = buildDir;

Используйте автоматический сгенерированный C++ основной файл для генерации независимого исполняемого файла.

cfg.GenerateExampleMain = 'GenerateCodeAndCompile';

Вызовите codegen (MATLAB Coder), чтобы сгенерировать Код С++ и исполняемый файл на вашем Raspberry Pi. По умолчанию имя приложения Raspberry Pi совпадает с функцией MATLAB.

codegen -config cfg HelperSpeechCommandRecognitionRasPi -args {hostIPAddress} -report -v
 Deploying code. This may take a few minutes. 
### Compiling function(s) HelperSpeechCommandRecognitionRasPi ...
------------------------------------------------------------------------
Location of the generated elf : /home/pi/remoteBuildDir/MATLAB_ws/R2021b/C/ExampleMatlab/ExampleManager/sporwal.Bdoc21b.j1648568/deeplearning_shared-ex00376115
### Using toolchain: GNU GCC Embedded Linux
### 'C:\ExampleMatlab\ExampleManager\sporwal.Bdoc21b.j1648568\deeplearning_shared-ex00376115\codegen\exe\HelperSpeechCommandRecognitionRasPi\HelperSpeechCommandRecognitionRasPi_rtw.mk' is up to date
### Building 'HelperSpeechCommandRecognitionRasPi': make  -f HelperSpeechCommandRecognitionRasPi_rtw.mk all

------------------------------------------------------------------------
### Generating compilation report ...
Warning: Function 'HelperSpeechCommandRecognitionRasPi' does not terminate due to an infinite
loop.

Warning in ==> HelperSpeechCommandRecognitionRasPi Line: 86 Column: 1
Code generation successful (with warnings): View report

Инициализируйте приложение на Raspberry Pi

Создайте команду, чтобы открыть HelperSpeechCommandRasPi application on Raspberry PiИспользование системы отправить команду в ваш Raspberry Pi.

applicationName = 'HelperSpeechCommandRecognitionRasPi';

applicationDirPaths = raspi.utils.getRemoteBuildDirectory('applicationName',applicationName);
targetDirPath = applicationDirPaths{1}.directory;

exeName = strcat(applicationName,'.elf');
command = ['cd ' targetDirPath '; ./' exeName ' &> 1 &'];

system(r,command);

Создайте dsp.UDPReceiver (DSP System Toolbox) системный объект, чтобы отправить аудио, записанное в MATLAB к вашему Raspberry Pi. Обновите targetIPAddress для вашего Raspberry Pi. Raspberry Pi получает записанное аудио от того же порта с помощью dsp.UDPReceiver (DSP System Toolbox) системный объект.

targetIPAddress = '172.18.228.24';
UDPSend = dsp.UDPSender('RemoteIPPort',26000,'RemoteIPAddress',targetIPAddress); 

Создайте dsp.UDPReceiver (DSP System Toolbox) системный объект, чтобы получить слуховые функции и предсказанную речевую команду индексирует от вашего Raspberry Pi. Каждый пакет UDP, полученный от Raspberry Pi, состоит из слуховых функций в порядке развертывания по столбцам, сопровождаемом предсказанным речевым индексом команды. Максимальная длина сообщения для dsp.UDPReceiver объект составляет 65 507 байтов. Вычислите buffer size, чтобы вместить максимальное количество пакетов UDP.

sizeOfFloatInBytes = 4;
maxUDPMessageLength = floor(65507/sizeOfFloatInBytes);
samplesPerPacket = 1 + numElementsPerSpectrogram; 
numPackets = floor(maxUDPMessageLength/samplesPerPacket);
bufferSize = numPackets*samplesPerPacket*sizeOfFloatInBytes;

UDPReceive = dsp.UDPReceiver("LocalIPPort",21000, ...  
    "MessageDataType","single", ...
    "MaximumMessageLength",samplesPerPacket, ...
    "ReceiveBufferSize",bufferSize);

Уменьшайте инициализацию наверху путем передачи кадра нулей к исполняемому файлу, работающему Raspberry Pi.

UDPSend(zeros(samplesPerCapture,1,"single"));

Выполните речевое распознавание команды Используя развернутый код

Обнаружьте команды, пока и осциллограф времени и матричное средство просмотра открыты или пока ограничение по времени не достигнуто. Чтобы остановить живое обнаружение перед, ограничение по времени достигнуто, закройте осциллограф времени или матричное окно средства просмотра.

show(timeScope)
show(matrixViewer)

timeLimit = 20;

tic
while isVisible(timeScope) && isVisible(matrixViewer) && toc < timeLimit
    % Capture audio and send that to RasPi
    x = adr();
    UDPSend(x);
    
    % Receive data packet from RasPi
    udpRec = UDPReceive();
    
    if ~isempty(udpRec)
        % Extract predicted index, the last sample of received UDP packet
        speechCommandIdx = udpRec(end); 
        
        % Extract auditory spectrogram
        spec = reshape(udpRec(1:numElementsPerSpectrogram), [numBands, numSpectrumPerSpectrogram]);
        
        % Display time domain signal and auditory spectrogram    
        timeScope(x)
        matrixViewer(spec)
        
        if speechCommandIdx == BackGroundIdx
            timeScope.Title = ' ';
        else
            timeScope.Title = char(labels(speechCommandIdx));
        end
        
        drawnow limitrate 
    end
end

hide(matrixViewer)
hide(timeScope)

Чтобы остановить исполняемый файл на вашем Raspberry Pi, используйте stopExecutable. Выпустите объекты UDP.

stopExecutable(codertarget.raspi.raspberrypi,exeName)

release(UDPSend)
release(UDPReceive)

Профилируйте Используя рабочий процесс PIL

Можно измерить время выполнения, взятое Raspberry Pi с помощью PIL (процессор в цикле) рабочий процесс. ProfileSpeechCommandRecognitionRaspi поддерживание функции является эквивалентом функции HelperSpeechCommandRecognitionRaspi, за исключением того, что первый возвращает речевой индекс команды и слуховую спектрограмму, в то время как последний отправляет те же параметры с помощью UDP. Время, потраченное вызовами UDP, меньше 1 мс, который относительно мал по сравнению с полным временем выполнения.

Создайте объект настройки PIL.

cfg = coder.config('lib','ecoder',true);
cfg.VerificationMode = 'PIL';

Установите ARM, вычисляют библиотеку и архитектуру.

dlcfg = coder.DeepLearningConfig('arm-compute');
cfg.DeepLearningConfig = dlcfg ;
cfg.DeepLearningConfig.ArmArchitecture = 'armv7';
cfg.DeepLearningConfig.ArmComputeVersion = '19.05';

Настройте связь со своим целевым компьютером.

if (~exist('r','var'))
  r = raspi('raspiname','pi','password');
end
hw = coder.hardware('Raspberry Pi');
cfg.Hardware = hw;

Установите каталог сборки и выходной язык.

buildDir = '~/remoteBuildDir';
cfg.Hardware.BuildDir = buildDir;
cfg.TargetLang = 'C++';

Позвольте профилировать и затем сгенерируйте код PIL. Файл MEX под названием ProfileSpeechCommandRecognition_pil сгенерирован в вашей текущей папке.

cfg.CodeExecutionProfiling = true;
codegen -config cfg ProfileSpeechCommandRecognitionRaspi -args {rand(samplesPerCapture, 1, 'single')} -report -v
 Deploying code. This may take a few minutes. 
### Compiling function(s) ProfileSpeechCommandRecognitionRaspi ...
### Connectivity configuration for function 'ProfileSpeechCommandRecognitionRaspi': 'Raspberry Pi'
### Using toolchain: GNU GCC Embedded Linux
### Creating 'C:\ExampleMatlab\ExampleManager\sporwal.Bdoc21b.j1648568\deeplearning_shared-ex00376115\codegen\lib\ProfileSpeechCommandRecognitionRaspi\coderassumptions\lib\ProfileSpeechCommandRecognitionRaspi_ca.mk' ...
### Building 'ProfileSpeechCommandRecognitionRaspi_ca': make  -f ProfileSpeechCommandRecognitionRaspi_ca.mk all
### Using toolchain: GNU GCC Embedded Linux
### Creating 'C:\ExampleMatlab\ExampleManager\sporwal.Bdoc21b.j1648568\deeplearning_shared-ex00376115\codegen\lib\ProfileSpeechCommandRecognitionRaspi\pil\ProfileSpeechCommandRecognitionRaspi_rtw.mk' ...
### Building 'ProfileSpeechCommandRecognitionRaspi': make  -f ProfileSpeechCommandRecognitionRaspi_rtw.mk all
Location of the generated elf : /home/pi/remoteBuildDir/MATLAB_ws/R2021b/C/ExampleMatlab/ExampleManager/sporwal.Bdoc21b.j1648568/deeplearning_shared-ex00376115/codegen/lib/ProfileSpeechCommandRecognitionRaspi/pil
------------------------------------------------------------------------
### Using toolchain: GNU GCC Embedded Linux
### 'C:\ExampleMatlab\ExampleManager\sporwal.Bdoc21b.j1648568\deeplearning_shared-ex00376115\codegen\lib\ProfileSpeechCommandRecognitionRaspi\ProfileSpeechCommandRecognitionRaspi_rtw.mk' is up to date
### Building 'ProfileSpeechCommandRecognitionRaspi': make  -f ProfileSpeechCommandRecognitionRaspi_rtw.mk all

------------------------------------------------------------------------
### Generating compilation report ...
Code generation successful: View report

Оцените время выполнения Raspberry Pi

Вызовите сгенерированную функцию PIL многократно, чтобы получить среднее время выполнения.

testDur = 50e-3;
numCalls = 100;

for k = 1:numCalls
    x = pinknoise(fs*testDur,'single');
    [speechCommandIdx, auditoryFeatures] = ProfileSpeechCommandRecognitionRaspi_pil(x);
end
### Starting application: 'codegen\lib\ProfileSpeechCommandRecognitionRaspi\pil\ProfileSpeechCommandRecognitionRaspi.elf'
    To terminate execution: clear ProfileSpeechCommandRecognitionRaspi_pil
### Launching application ProfileSpeechCommandRecognitionRaspi.elf...
    Execution profiling data is available for viewing. Open Simulation Data Inspector.
    Execution profiling report available after termination.

Отключите выполнение PIL.

clear ProfileSpeechCommandRecognitionRaspi_pil 
### Host application produced the following standard output (stdout) and standard error (stderr) messages:

    Execution profiling report: report(getCoderExecutionProfile('ProfileSpeechCommandRecognitionRaspi'))

Сгенерируйте профиль выполнения, сообщают, чтобы оценить время выполнения.

executionProfile = getCoderExecutionProfile('ProfileSpeechCommandRecognitionRaspi');
report(executionProfile, ...
       'Units','Seconds', ...
       'ScaleFactor','1e-03', ...
       'NumericFormat','%0.4f')  
ans = 
'C:\ExampleMatlab\ExampleManager\sporwal.Bdoc21b.j1648568\deeplearning_shared-ex00376115\codegen\lib\ProfileSpeechCommandRecognitionRaspi\html\orphaned\ExecutionProfiling_4f9ceee464b795df.html'

Максимальное время выполнения потрачено ProfileSpeechCommandRecognitionRaspi функция является почти дважды средним временем выполнения. Можно заметить, что время выполнения максимально для первого вызова функции PIL, и это происходит из-за инициализации, происходящей в первом вызове. Среднее время выполнения составляет приблизительно 20 мс, который является ниже бюджета на 50 мс (аудио время получения). Уровень измерен на версии Модели B Raspberry Pi 4 1.1.