exponenta event banner

Формирование кода распознавания речевых команд на Raspberry Pi

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

Предпосылки

  • Процессор ARM, поддерживающий расширение NEON

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

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

Поддерживаемые версии библиотек и сведения о настройке переменных среды см. в разделе Предварительные условия для глубокого обучения с помощью кодера MATLAB (MATLAB Coder).

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

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

Определите ту же частоту выборки, на которой обучалась сеть (16 кГц). Определите скорость классификации и количество входных аудиоотсчетов в кадре. Функциональный вход в сеть представляет собой спектрограмму Барка, которая соответствует 1 секунде аудиоданных. Спектрограмма Барка рассчитана для окон 25 мс с 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) для отображения результатов.

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);

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

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 (Панель звуковых инструментов) на audioFeatureExtractor объект. generateMATLABFunction объектная функция создает автономную функцию, которая выполняет извлечение эквивалентных элементов и совместима с генерацией кода.

generateMATLABFunction(afe,'extractSpeechFeatures')

Поддерживающая функция HelperSpeechCommandRecognableRasPi инкапсулирует процесс извлечения признаков и предсказания сети, продемонстрированный ранее. Чтобы извлечение признака было совместимо с генерацией кода, извлечение признака обрабатывается сгенерированным extractSpeechFeatures функция. Чтобы сеть была совместима с генерацией кода, функция поддержки использует coder.loadDeepLearningNetwork (Кодер MATLAB) для загрузки сети. Поддерживающая функция использует 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 Support Package, raspi, чтобы создать соединение с вашим Raspberry Pi. В следующем коде замените:

  • raspiname с именем вашего Малинового Пи

  • 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) для генерации кода C++ и исполняемого файла на Raspberry Pi. По умолчанию имя приложения Raspberry Pi совпадает с именем функции MATLAB.

codegen -config cfg HelperSpeechCommandRecognitionRasPi -args {hostIPAddress} -report -v
 Deploying code. This may take a few minutes. 
Location of the generated elf : /home/pi/remoteBuildDir/MATLAB_ws/R2020b/C/Users/sporwal/OneDrive_-_MathWorks/Documents/MATLAB/Examples/deeplearning_shared-ex00376115
### Using toolchain: GNU GCC Raspberry Pi
### 'C:\Users\sporwal\OneDrive - MathWorks\Documents\MATLAB\Examples\deeplearning_shared-ex00376115\codegen\exe\HelperSpeechCommandRecognitionRasPi\HelperSpeechCommandRecognitionRasPi_rtw.mk' is up to date
### Building 'HelperSpeechCommandRecognitionRasPi': make  -f HelperSpeechCommandRecognitionRasPi_rtw.mk all

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. Использовать system отправить команду на ваш Малиновый Пи.

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 получает захваченное аудио с того же порта с помощью dsp.UDPReceiver(Панель системных инструментов DSP) системный объект.

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

Создать dsp.UDPReceiver (DSP System Toolbox) системный объект для получения слуховых функций и прогнозируемого индекса речевых команд из вашего Raspberry Pi. Каждый пакет UDP, принятый от Raspberry Pi, состоит из слуховых признаков в основном порядке столбцов, за которыми следует прогнозируемый индекс речевой команды. Максимальная длина сообщения для dsp.UDPReceiver объект составляет 65507 байт. Вычислите размер буфера для размещения максимального количества пакетов 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 (процессор в цикле). Поддерживающая функция ProfireSpeechCommandRecognireRaspi эквивалентна функции HelperSpeechCommandRecognireRaspi, за исключением того, что первая возвращает индекс речевой команды и слуховую спектрограмму, в то время как вторая посылает те же параметры с помощью 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
### Target device has no native communication support. Checking connectivity configuration registrations...
 Deploying code. This may take a few minutes. 
### Target device has no native communication support. Checking connectivity configuration registrations...
### Connectivity configuration for function 'ProfileSpeechCommandRecognitionRaspi': 'Raspberry Pi'
### Using toolchain: GNU GCC Raspberry Pi
### Creating 'C:\Users\sporwal\OneDrive - MathWorks\Documents\MATLAB\Examples\deeplearning_shared-ex00376115\codegen\lib\ProfileSpeechCommandRecognitionRaspi\coderassumptions\lib\ProfileSpeechCommandRecognitionRaspi_ca.mk' ...
### Building 'ProfileSpeechCommandRecognitionRaspi_ca': make  -f ProfileSpeechCommandRecognitionRaspi_ca.mk all
### Using toolchain: GNU GCC Raspberry Pi
### Creating 'C:\Users\sporwal\OneDrive - MathWorks\Documents\MATLAB\Examples\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/R2020b/C/Users/sporwal/OneDrive_-_MathWorks/Documents/MATLAB/Examples/deeplearning_shared-ex00376115/codegen/lib/ProfileSpeechCommandRecognitionRaspi/pil
### Using toolchain: GNU GCC Raspberry Pi
### 'C:\Users\sporwal\OneDrive - MathWorks\Documents\MATLAB\Examples\deeplearning_shared-ex00376115\codegen\lib\ProfileSpeechCommandRecognitionRaspi\ProfileSpeechCommandRecognitionRaspi_rtw.mk' is up to date
### Building 'ProfileSpeechCommandRecognitionRaspi': make  -f ProfileSpeechCommandRecognitionRaspi_rtw.mk all

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:\Users\sporwal\OneDrive - MathWorks\Documents\MATLAB\Examples\deeplearning_shared-ex00376115\codegen\lib\ProfileSpeechCommandRecognitionRaspi\html\orphaned\ExecutionProfiling_0d0860c73650e0a4.html'

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