Ручная классификация жестов Используя радарные сигналы и глубокое обучение

В этом примере показано, как классифицировать ультраширокополосный (UWB) импульсные радарные данные сигнала с помощью нескольких - вход, сверточная нейронная сеть (CNN) одно выхода.

Введение

Основанные на перемещении данные сигнала, полученные с помощью датчиков, как импульсные радары UWB, содержат шаблоны, характерные для различных жестов. Сопоставление данных о движении с перемещением приносит пользу нескольким путям работы. Например, ручное распознавание жеста важно для бесконтактного человеко-машинного взаимодействия. Этот пример стремится использовать решение для глубокого обучения автоматизировать извлечение признаков от шаблонов в ручном наборе данных жеста и обеспечить метку для каждой выборки сигнала.

UWB-жесты являются общедоступным набором данных динамических ручных жестов [1]. Это содержит в общей сложности 9 600 выборок, собранных от 8 различных волонтеров - людей. Чтобы получить каждую запись, ревизоры поместили отдельный импульсный радар UWB слева, верхнюю часть и правые стороны их экспериментальной настройки, приводящей к 3 полученным радарным матрицам данных сигнала. Волонтеры выполнили ручные жесты из словаря жеста, состоящего из 12 динамических движений рук:

  1. лево-правильный сильный удар (сильный удар L-R)

  2. оставленный праву сильный удар (сильный удар R-L)

  3. вниз сильный удар (сильный удар U-D)

  4. вниз сильный удар (сильный удар D-U)

  5. диагональ, оставленная право вниз, сильно ударяет (сильный удар Diag-LR-UD)

  6. диагональ, оставленная право вниз, сильно ударяет (сильный удар Diag-LR-DU)

  7. диагональное право, оставленное вниз, сильно ударяет (Сильный удар Diag-RL-UD)

  8. диагональное право, оставленное вниз, сильно ударяет (Сильный удар Diag-RL-DU)

  9. по часовой стрелке вращение

  10. против часовой стрелки вращение

  11. внутрь продвиньте

  12. пустой жест

Когда каждое ручное движение жеста получено 3 независимыми импульсными радарами UWB, мы будем использовать архитектуру CNN, которая принимает 3 сигнала как отдельные входные параметры. Модель CNN извлечет информацию о функции из каждого сигнала прежде, чем объединить его, чтобы сделать итоговое предсказание метки жеста. По сути, несколько - вход, CNN одно выхода будет использовать минимально предварительно обработанные радарные матрицы данных сигнала, чтобы классифицировать различные жесты.

Загрузите данные

Каждая радарная матрица данных сигнала помечена как ручной жест, который сгенерировал ее. 8 различных волонтеров - людей выполнили 12 отдельных ручных жестов для в общей сложности 96 испытаний, сохраненных в 96 MAT-файлах. Каждый MAT-файл содержит 3 радарных матрицы данных, соответствуя этим 3 радарам, используемым в экспериментальной настройке. Их называют Left, Top, и Right. Файлы доступны в следующем местоположении:

https://ssd.mathworks.com/supportfiles/SPT/data/uwb-gestures.zip

Загрузите файлы данных в свою директорию MATLAB Examples.

datasetZipFolder = matlab.internal.examples.downloadSupportFile("SPT","data/uwb-gestures.zip");
datasetFolder = erase(datasetZipFolder,".zip");
if ~exist(datasetFolder,"dir")
    downloadLocation = fileparts(datasetZipFolder);
    unzip(datasetZipFolder,downloadLocation);
end

Можно также принять решение загрузить отдельный файл, который включает предварительно обученную сеть, misoNet, сохраненный в MAT-файле под названием pretrainedNetwork.mat. Это доступно в следующем местоположении:

https://ssd.mathworks.com/supportfiles/SPT/data/uwb-gestures-network.zip

Можно пропустить учебные шаги и использовать предварительно обученную сеть для классификации установкой doTraining к false. Если doTraining установлен в false, предварительно обученная сеть будет загружена позже в этом примере. Если вы хотите обучить сеть, когда пример запускается, убедитесь, что установили doTraining к true.

doTraining = true;

Исследуйте данные

Создайте datastore сигнала, чтобы получить доступ к данным в файлах. Задайте имена переменной сигнала, которые вы хотите считать из каждого файла с помощью SignalVariableNames параметр. Этот пример принимает, что набор данных хранился в вашей директории MATLAB Examples под папкой uwb-жестов. Если дело обстоит не так, изменение путь к данным в datasetFolder переменная.

sds = signalDatastore(datasetFolder,...
                      "IncludeSubfolders",true,...
                      "SignalVariableNames",["Left","Top","Right"],...
                      "FileExtensions",".mat",...
                      "ReadOutputOrientation","row")
sds = 
  signalDatastore with properties:

                       Files:{
                             ' .../R2021b/supportfiles/SPT/data/uwb-gestures/HV_01/HV_01_G10_RawData.mat';
                             ' .../R2021b/supportfiles/SPT/data/uwb-gestures/HV_01/HV_01_G11_RawData.mat';
                             ' .../R2021b/supportfiles/SPT/data/uwb-gestures/HV_01/HV_01_G12_RawData.mat'
                              ... and 93 more
                             }
                     Folders: {'/home/ejoshi/Documents/MATLAB/Examples/R2021b/supportfiles/SPT/data/uwb-gestures'}
    AlternateFileSystemRoots: [0×0 string]
                    ReadSize: 1
         SignalVariableNames: ["Left"    "Top"    "Right"]
       ReadOutputOrientation: "row"

Datastore возвращает трехэлементный массив ячеек, содержащий радарные матрицы сигнала для левых, верхней части и правильных радаров, в том порядке.

preview(sds)
ans=1×3 cell array
    {9000×189 double}    {9000×189 double}    {9000×189 double}

Строки и столбцы в каждой радарной матрице сигнала представляют длительность ручного (медленно-разового) жеста и расстояние руки от (быстро-разового) радара, соответственно. Во время сбора данных ревизоры записали предмет, повторяющий конкретный ручной жест в течение 450 секунд, соответствуя 9 000 (медленно-разовых) строк. В 90 медленно-разовых системах координат существует 1 полное движение жеста. По сути, каждая радарная матрица сигнала содержит 100 полных ручных движений жеста samples.The, область значений каждого радара UWB составляет 1,2 метра, соответствуя 189 быстро-разовым интервалам.

slowTimeFrames = 90;
recordedTimePerSample = 4.5;
radarRange = 1.2;

Чтобы визуализировать ручное движение жеста, задайте радарное местоположение UWB, жест и выборку жеста (между 1 и 100).

radarToPlot = 1;
gestureToPlot = "_G1_";
gestureSample = 50;
 

Получите радарную матрицу сигнала для выбранного ручного жеста и радарного местоположения.

sdssubset = subset(sds,contains(sds.Files,gestureToPlot));
radarDataMatrix = read(sdssubset);
radarDataMatrix = radarDataMatrix{radarToPlot};

Используйте normalize преобразовать данные сигнала жеста, чтобы расположиться между 0 и 1 и использовать imagesc визуализировать ручную выборку движения жеста.

normalizedRadarData = normalize(radarDataMatrix,2,"range",[0 1]);
imagesc([0 radarRange],...
        [0 recordedTimePerSample],...
        normalizedRadarData(slowTimeFrames*(gestureSample-1)+1:slowTimeFrames*gestureSample,:),...
        [0 1]);
set(gca,"YDir","normal")
title("Raw Signal")
xlabel("Distance of Hand from the Radar (m)")
ylabel("Duration of Hand Gesture (s)")

Как вы видите, это затрудняет, чтобы различить шаблон движения.

Необработанный сигнал содержит экологические отражения от частей тела или другого подарка статических объектов в радаре "s область значений. Эти нежелательные отражения известны как "помеху" и могут быть удалены с помощью импульсного компенсатора, который выполняет экспоненциальное скользящее среднее. Передаточная функция для этой операции

H(z)=1-z-11-αz-1

таким образом, что α значение 0α1 это управляет объемом усреднения [2]. Используйте filter с коэффициентами числителя и содействующим набором знаменателя как [1 - 1] и [1 - 0.9], соответственно, чтобы удалить помеху из необработанного сигнала.

clutterRemovedSignal = filter([1 -1],[1 -0.9],radarDataMatrix,[],1);

Визуализируйте удаленный с помехой сигнал видеть различие.

normalizedClutterRemovedSignal = normalize(clutterRemovedSignal,2,"range",[0 1]);
imagesc([0 radarRange],...
        [0 recordedTimePerSample],...
        normalizedClutterRemovedSignal(slowTimeFrames*(gestureSample-1)+1:slowTimeFrames*gestureSample,:),...
        [0 1]);
set(gca,"YDir","normal")
title("Clutter-Removed Signal")
xlabel("Distance of Hand from the Radar (m)")
ylabel("Duration of Hand Gesture (s)")

Обратите внимание на то, что шаблон движения намного больше отображается теперь. Например, если вы примете решение визуализировать лево-правильный сильный удар с точки зрения левого радара, вы будете видеть, что расстояние руки от радара увеличивается по длительности ручного жеста.

Подготовка данных для обучения

Имена MAT-файла содержат коды жеста (G1, G2..., G12) соответствие меткам для каждой радарной матрицы сигнала. Преобразуйте эти коды в метки в рамках словаря жеста, с помощью категориального массива.

[~,filenames] = fileparts(sds.Files);
gestureLabels = extract(filenames,"G"+digitsPattern);
gestureCodes = ["G1","G2","G3","G4",...
                "G5","G6","G7","G8",...
                "G9","G10","G11","G12"];
gestureVocabulary = ["L-R swipe",       "R-L swipe",       "U-D swipe",       "D-U swipe",...
                     "Diag-LR-UD swipe","Diag-LR-DU swipe","Diag-RL-UD swipe","Diag-RL-DU swipe",...
                     "clockwise",       "counterclockwise","inward push",     "empty"];
gestureLabels = categorical(gestureLabels,gestureCodes,gestureVocabulary);

Соберите метки в datastore массивов.

labelDs = arrayDatastore(gestureLabels,"OutputType","cell");

Объедините datastore сигнала и datastore массивов, чтобы получить один datastore, который содержит данные сигнала от каждого радара и категориальной метки. Переставьте получившийся datastore, чтобы рандомизировать порядок, в котором он хранит MAT-файлы.

allDataDs = combine(sds,labelDs);
allDataDs = shuffle(allDataDs);
preview(allDataDs)
ans=1×4 cell array
    {9000×189 double}    {9000×189 double}    {9000×189 double}    {[Diag-LR-UD swipe]}

Функция преобразования позволяет функцию помощника, processData, быть примененным к данным, когда это читается datastore. processData выполняет нормализацию и фильтрацию, которая была описана в вышеупомянутом разделе, чтобы стандартизировать данные и удалить помеху. Кроме того, это делит радарную матрицу сигнала на отдельные ручные выборки движения жеста.

allDataDs = transform(allDataDs,@processData);
preview(allDataDs)
ans=8×4 cell array
    {90×189 double}    {90×189 double}    {90×189 double}    {[Diag-LR-UD swipe]}
    {90×189 double}    {90×189 double}    {90×189 double}    {[Diag-LR-UD swipe]}
    {90×189 double}    {90×189 double}    {90×189 double}    {[Diag-LR-UD swipe]}
    {90×189 double}    {90×189 double}    {90×189 double}    {[Diag-LR-UD swipe]}
    {90×189 double}    {90×189 double}    {90×189 double}    {[Diag-LR-UD swipe]}
    {90×189 double}    {90×189 double}    {90×189 double}    {[Diag-LR-UD swipe]}
    {90×189 double}    {90×189 double}    {90×189 double}    {[Diag-LR-UD swipe]}
    {90×189 double}    {90×189 double}    {90×189 double}    {[Diag-LR-UD swipe]}

Обучение нейронной сети является итеративным. В каждой итерации datastore считывает данные из файлов и преобразовывает данные прежде, чем обновить сетевые коэффициенты. Поскольку данные считываются из отдельных выборок, данные должны будут быть считаны в память, прежде чем быть переставленным и вставлены в другой datastore для обучения.

Поскольку целый обучающий набор данных умещается в памяти, возможно преобразовать данные параллельно, если Parallel Computing Toolbox доступен, и затем соберите его в рабочую область. Используйте readall с UseParallel отметьте набор к истине, чтобы использовать параллельный пул, чтобы считать все данные сигнала и метки в рабочую область. Если совпадения данных в память о вашем компьютере, импортируя данные в рабочую область включают более быстрое обучение, потому что данные считаны и преобразованы только однажды. Обратите внимание на то, что, если данные не умещаются в памяти, вы должны, чтобы передать datastore в учебную функцию, и преобразования выполняются в каждую учебную эпоху.

allData = readall(allDataDs,"UseParallel",true);
Starting parallel pool (parpool) using the 'local' profile ...
Connected to the parallel pool (number of workers: 8).

Метки возвращены как последний столбец в allData. Используйте countlabels получить пропорции значений метки в наборе данных. Обратите внимание на то, что жесты сбалансированы и хорошо представлены через набор данных.

countlabels(allData(:,4))
ans=12×3 table
         Label          Count    Percent
    ________________    _____    _______

    D-U swipe            793     8.2664 
    Diag-LR-DU swipe     800     8.3394 
    Diag-LR-UD swipe     800     8.3394 
    Diag-RL-DU swipe     800     8.3394 
    Diag-RL-UD swipe     800     8.3394 
    L-R swipe            800     8.3394 
    R-L swipe            800     8.3394 
    U-D swipe            800     8.3394 
    clockwise            800     8.3394 
    counterclockwise     800     8.3394 
    empty                800     8.3394 
    inward push          800     8.3394 

Разделите данные случайным образом на наборы обучения и валидации, убеждаясь на потом оставить данные о тестировании. В этом примере обучение, валидация и тестирующие разделения составят 70%, 15% и 15%, соответственно. Используйте splitlabels разделять данные в обучение, валидацию и тестирующие наборы, которые обеспечивают те же пропорции метки как исходный набор данных. Задайте randomized опция, чтобы переставить данные случайным образом через три набора.

idxs = splitlabels(allData(:,4),[0.7 0.15],"randomized");
trainIdx = idxs{1}; valIdx = idxs{2}; testIdx = idxs{3};

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

trainData = allData(trainIdx(randperm(length(trainIdx))),:);
valData = allData(valIdx(randperm(length(valIdx))),:);
trainDataDs = arrayDatastore(trainData,"OutputType","same");
valDataDs = arrayDatastore(valData,"OutputType","same");

Подготовьте сеть к обучению

Задайте сетевую архитектуру перед обучением. Поскольку каждое ручное движение жеста получено 3 независимыми импульсными радарами UWB, мы будем использовать архитектуру CNN, которая принимает 3 сигнала как отдельные входные параметры. Результаты, достигнутые после обучения, это предложило несколько - вход, CNN одно выхода, значительно лучше, чем достигнутые с альтернативным одно входом, CNN одно выхода, вход которого является 90 x 189 x 3 стека радарных матриц данных.

repeatBranch содержит операции, которые будут выполняться отдельно на трех радарных матрицах сигнала данных. Модель CNN должна объединить извлеченную информацию о функции от каждого сигнала сделать итоговое предсказание метки жеста. mainBranch содержит операции, которые конкатенируют 3 repeatBranch выходные параметры и оценочные метки. Задайте imageInputLayer из размера 90 x 189, чтобы принять ручные выборки движения жеста. Задайте additionLayer с количеством входного набора к 3, чтобы собрать выходные параметры 3 ветвей и передать их в раздел классификации модели. Задайте fullyConnectedLayer с выходным размером 12, один для каждого из ручных жестов. Добавьте softmaxLayer и classificationLayer выводить предполагаемые метки.

repeatBranch = [
    imageInputLayer([90 189 1],"Normalization", "none")

    convolution2dLayer(3,8,"Padding",1)
    batchNormalizationLayer
    reluLayer   
    maxPooling2dLayer(2,"Stride",2)

    convolution2dLayer(3,16,"Padding",1)
    batchNormalizationLayer
    reluLayer
    maxPooling2dLayer(2,"Stride",2)

    convolution2dLayer(3,32,"Padding",1)
    batchNormalizationLayer
    reluLayer
    maxPooling2dLayer(2,"Stride",2)

    convolution2dLayer(3,64,"Padding",1)
    batchNormalizationLayer
    reluLayer
    maxPooling2dLayer(2,"Stride",2)];

mainBranch = [
    additionLayer(3)
    fullyConnectedLayer(12)
    softmaxLayer
    classificationLayer];

Задайте layerGraph к которому repeatBranch добавляется 3 раза и mainBranch добавляется однажды. Соедините выходные параметры итогового maxPooling2dLayer в каждом repeatBranch к входным параметрам additionLayer.

misoCNN = layerGraph();
misoCNN = addLayers(misoCNN, repeatBranch);
misoCNN = addLayers(misoCNN, repeatBranch);
misoCNN = addLayers(misoCNN, repeatBranch);
misoCNN = addLayers(misoCNN, mainBranch);
misoCNN = connectLayers(misoCNN, "maxpool_4", "addition/in1");
misoCNN = connectLayers(misoCNN, "maxpool_8", "addition/in2");
misoCNN = connectLayers(misoCNN, "maxpool_12", "addition/in3");

Визуализируйте несколько - вход, CNN одно выхода.

plot(misoCNN);

Выберите опции для учебного процесса, которые гарантируют хорошую производительность сети. Убедитесь, что задали valDataDs как ValidationData, так, чтобы это использовалось для валидации во время обучения. Обратитесь к trainingOptions (Deep Learning Toolbox) документация для описания каждого параметра.

options = trainingOptions("adam", ...
    "InitialLearnRate",1e-3, ...
    "MaxEpochs",3,... 
    "MiniBatchSize",32, ...
    "ValidationData",valDataDs,...
    "ValidationFrequency",40,...
    "Verbose",false,...
    "Plots","training-progress");

Обучение сети

Используйте trainNetwork команда, чтобы обучить CNN.

if doTraining == true
    misoNet = trainNetwork(trainDataDs,misoCNN,options);
else
    pretrainedNetworkZipFolder = matlab.internal.examples.downloadSupportFile("SPT","data/uwb-gestures-network.zip");
    pretrainedNetworkFolder = erase(pretrainedNetworkZipFolder,".zip");
    if ~exist(pretrainedNetworkFolder,"dir")
        downloadLocation = fileparts(pretrainedNetworkZipFolder);
        unzip(pretrainedNetworkZipFolder,downloadLocation);
    end
    load(fullfile(pretrainedNetworkFolder,"pretrainedNetwork.mat"),"misoNet");
end

Классифицируйте данные о тестировании

Классифицируйте данные о тестировании с помощью обученного CNN и classify команда.

testData = allData(testIdx,:);
testData = {cat(4,testData{:,1}),cat(4,testData{:,2}),cat(4,testData{:,3}),cat(1,testData{:,4})};
actualLabels = testData{4};
predictedLabels = classify(misoNet,testData{1},testData{2},testData{3});
accuracy = mean(predictedLabels==actualLabels);
fprintf("Accuracy on the test set is %0.2f%%",100*accuracy)
Accuracy on the test set is 96.04%

Визуализируйте эффективность классификации с помощью матрицы беспорядка.

confusionchart(predictedLabels,actualLabels);

Самый большой беспорядок между против часовой стрелки и по часовой стрелке перемещения и входящее нажатие и пустые перемещения.

Исследуйте сетевые предсказания

Можно получить баллы из итоговых макс. объединяющих слоев в каждой входной ветви, чтобы получить лучшее понимание как данные из каждого радара, внесенного итоговому сетевому доверию. Функция помощника, getActivationData, возвращает softmax-нормированные баллы (вероятности для членства в классе) и индексы, соответствующие 3 самым высоким баллам.

gestureToPlot = "L-R swipe";
gestureToPlotIndices = находят (соответствия (строка (actualLabels), gestureToPlot));
gestureSelection = randsample (gestureToPlotIndices, 1);
actualLabel = actualLabels (gestureSelection);
predictedLabel = predictedLabels (gestureSelection);
allLabels = категории (actualLabels);

[leftScores, leftClassIds] = getActivationData (misoNet, данные тестирования, gestureSelection,"maxpool_4");
[topScores, topClassIds] = getActivationData (misoNet, данные тестирования, gestureSelection,"maxpool_8");
[rightScores, rightClassIds] = getActivationData (misoNet, данные тестирования, gestureSelection,"maxpool_12");

Используйте функцию помощника plotActivationData чтобы визуализировать данные из каждого радара и наложить метки, соответствующие 3 самым высоким баллам после, операции в каждой входной ветви завершаются.

t = tiledlayout(1,3);
plotActivationData(testData, allLabels, leftScores, leftClassIds,...
    gestureSelection, [0 radarRange],[0 recordedTimePerSample], "Left");
plotActivationData(testData, allLabels, topScores, topClassIds,...
    gestureSelection, [0 radarRange],[0 recordedTimePerSample], "Top");
plotActivationData(testData, allLabels, rightScores, rightClassIds,...
    gestureSelection, [0 radarRange],[0 recordedTimePerSample], "Right");
title(t,["Actual Label : "+string(actualLabel);"Predicted Label : "+string(predictedLabel)],"FontSize",12);
xlabel(t,"Distance of Hand from the Radar (m)","FontSize",11)
ylabel(t,"Duration of Hand Gesture (s)","FontSize",11)

Заключение

В этом примере вы изучили, как использовать несколько - вход CNN, чтобы извлечь информацию из 3 независимых радарных матриц данных и классифицировать ручные перемещения жеста. Несколько - входная архитектура позволила нам использовать в своих интересах данные, сгенерированные несколькими датчиками, записывающими то же событие.

Функции помощника

function dataOut = processData(dataIn)
    label = dataIn(end);
    dataIn(end) = [];
    dataOut = cellfun(@(x) filter([1 -1],[1 -0.9],x,[],1),dataIn,"UniformOutput",false);
    dataOut = cellfun(@(x) normalize(x,2,"range",[0 1]),dataOut,"UniformOutput",false);
    dataOut = cat(1,dataOut{:});
    dataOut = mat2cell(dataOut,90*ones(1,size(dataOut,1)/90));
    dataOut = reshape(dataOut,[],3);
    dataOut(:,4) = label;
end

function [scores, classIds] = getActivationData(net, testData, index, layer)
    activation = activations(net,testData{1}(:,:,index),testData{2}(:,:,index),testData{3}(:,:,index),layer,"OutputAs","columns");
    fcWeights = net.Layers(end-2).Weights;
    fcBias = net.Layers(end-2).Bias;
    scores = fcWeights*activation + fcBias;
    scores = exp(scores)/sum(exp(scores));  
    [~,classIds] = maxk(scores,3);
end

function plotActivationData(testData, labels, scores, ids, sampleIdx, xlimits, ylimits, plotTitle)
    if plotTitle == "Left"
        gesture = 1;
    elseif plotTitle == "Top"
        gesture = 2;
    elseif plotTitle == "Right"
        gesture = 3;
    end
    nexttile;
    imagesc(xlimits,ylimits,testData{gesture}(:,:,sampleIdx),[0 1])
    text(0.3,4,plotTitle,"Color","red","FontWeight","bold","HorizontalAlignment","center")
    set(gca,"YDir","normal")
    title(string(labels(ids)) + ": " + string(round(scores(ids),3)),"FontSize",8);
end

Ссылки

[1] Ахмед, S., Ван, D., Парк, J. и др. UWB-жесты, общедоступный набор данных динамических ручных жестов, полученных с помощью импульсных радарных датчиков. Научные Данные 8, 102 (2021). https://doi.org/10.1038/s41597-021-00876-0

[2] Lazaro A, Girbau D, Виллэрино Р. Текникс для подавления помехи в присутствии движений тела во время обнаружения дыхательного действия через радары UWB. Датчики (Базель, Швейцария). 2 014 февралей; 14 (2):2595-2618. DOI: 10.3390/s140202595.

Смотрите также

| (Signal Processing Toolbox) | (Signal Processing Toolbox) |