Main Content

Esta página se ha traducido mediante traducción automática. Haga clic aquí para ver la última versión en inglés.

Reconocimiento de gestos mediante unidades de medida inerciales

Este ejemplo muestra cómo reconocer gestos basándose en una unidad de medición inercial (IMU) portátil. El reconocimiento de gestos es un subcampo del campo general de Reconocimiento de actividad humana (HAR). En este ejemplo, se utiliza la agrupación y la distorsión dinámica del tiempo de cuaterniones para crear un algoritmo de coincidencia de plantillas para clasificar cinco gestos.

La distorsión dinámica del tiempo es un algoritmo utilizado para medir la similitud entre dos series temporales de datos. La distorsión del tiempo dinámica compara dos secuencias, alineando los puntos de datos de la primera secuencia con los puntos de datos de la segunda, ignorando la sincronización del tiempo. La distorsión dinámica del tiempo también proporciona una métrica de distancia entre dos secuencias no alineadas.

Similar a la distorsión dinámica del tiempo, la distorsión dinámica del tiempo de cuaterniones compara dos secuencias en el espacio cuaternión o rotacional [1].

La deformación temporal dinámica de cuaterniones también devuelve una distancia escalar entre dos trayectorias de orientación. Esta métrica de distancia le permite agrupar datos y encontrar una trayectoria de plantilla para cada gesto. Puede utilizar un conjunto de trayectorias de plantilla para reconocer y clasificar nuevas trayectorias.

Este enfoque de utilizar agrupación y deformación temporal dinámica de cuaterniones para generar trayectorias de plantilla es el segundo nivel de un sistema de clasificación de dos niveles descrito en [2].

Gestos y recopilación de datos.

En este ejemplo, usted construye un algoritmo que reconoce y clasifica los siguientes cinco gestos. Cada gesto comienza en la misma posición con el antebrazo derecho al nivel, paralelo al suelo.

  • up - Levante el brazo y luego regrese al nivel.

up.png

  • down - L Baje el brazo y luego regrese al nivel.

down.png

  • left - Desliza el brazo hacia la izquierda y luego regresa al centro. El antebrazo está paralelo al suelo durante todo el ejercicio.

left.png

  • right - Desliza el brazo hacia la derecha y luego regresa al centro. El antebrazo está paralelo al suelo durante todo el ejercicio.

right.png

  • twist - Gire la mano 90 grados en el sentido de las agujas del reloj y vuelva a la orientación original. El antebrazo está paralelo al suelo durante todo el ejercicio.

twist.png

Los datos de estos cinco gestos se capturan utilizando el paquete de soporte Arduino para MATLAB. Cuatro personas diferentes realizaron los cinco gestos y repitieron cada gesto de nueve a diez veces. Los datos registrados, guardados como table, contienen lecturas de acelerómetro y giroscopio, frecuencia de muestreo, el gesto que se realiza y el nombre de la persona que realiza el gesto.

Preprocesamiento de fusión de sensores

El primer paso es fusionar todas las lecturas del acelerómetro y giroscopio para producir un conjunto de series de tiempo de orientación o trayectorias. Utilizará el System object imufilter para fusionar las lecturas del acelerómetro y el giroscopio. El System object imufilter está disponible tanto en la Navigation Toolbox como en la Sensor Fusion and Tracking Toolbox. Puede utilizar un bucle parfor para acelerar el cálculo. Si tiene el Parallel Computing Toolbox, el bucle parfor se ejecutará en paralelo; de lo contrario, se ejecutará un bucle for regular de forma secuencial.

ld = load("imuGestureDataset.mat");
dataset = ld.imuGestureDataset;

dataset = dataset(randperm(size(dataset,1)), :); % Shuffle the dataset

Ntrials = size(dataset,1);
Orientation = cell(Ntrials,1);
parfor ii=1:Ntrials
    h = imufilter("SampleRate", dataset(ii,:).SampleRate);
    Orientation{ii} = h(dataset.Accelerometer{ii}, dataset.Gyroscope{ii}); 
end
Starting parallel pool (parpool) using the 'Processes' profile ...
Connected to parallel pool with 4 workers.
dataset = addvars(dataset, Orientation, NewVariableNames="Orientation");
gestures = string(unique(dataset.Gesture));  % Array of all gesture types
people = string(unique(dataset.Who));
Ngestures = numel(gestures);
Npeople = numel(people);

Fondo dinámico de deformación del tiempo del cuaternión

Deformación temporal dinámica de cuaterniones [1] compara dos series de tiempo de orientación en el espacio de cuaterniones y formula una métrica de distancia compuesta de tres partes relacionadas con la distancia, derivada y curvatura del cuaternión. En este ejemplo, solo comparará señales según la distancia del cuaternión.

La distorsión del tiempo dinámica de cuaterniones utiliza la distorsión del tiempo para calcular una alineación óptima entre dos secuencias.

A continuación se utiliza la deformación temporal dinámica de cuaterniones para comparar dos secuencias de orientaciones (cuaterniones).

Crea dos orientaciones aleatorias:

rng(20);
q = randrot(2,1);

Cree dos trayectorias diferentes conectando las dos orientaciones:

h1 = 0:0.01:1;
h2 = [zeros(1,10) (h1).^2];
traj1 = slerp(q(1),q(2),h1).';
traj2 = slerp(q(1),q(2),h2).';

Tenga en cuenta que, aunque traj1 y traj2 comienzan y terminan en las mismas orientaciones, tienen diferentes longitudes y realizan la transición a lo largo de esas orientaciones a diferentes velocidades.

A continuación, compare y encuentre la mejor alineación entre estas dos trayectorias utilizando la deformación temporal dinámica de cuaterniones.

[qdist, idx1, idx2] = helperQDTW(traj1, traj2);

La métrica de distancia entre ellos es un escalar.

qdist
qdist = 1.1474

Trazar la mejor alineación usando las variables idx1 y idx2

e1 = eulerd(traj1, "ZYX", "frame");
e2 = eulerd(traj2, "ZYX", "frame");

figure;
subplot(3,1,1);
plot(e1, "-");
legend ("Yaw (Z)", "Pitch (Y)", "Roll (X)");
ylabel("traj1")

subplot(3,1,2);
set(gca, "ColorOrderIndex", 4);
hold on;
plot(e2, "-");
legend ("Yaw (Z)", "Pitch (Y)", "Roll (X)");
ylabel("traj2")
hold off;

subplot(3,1,3);
plot(1:numel(idx1), e1(idx1,:), "-", ...
    1:numel(idx2), e2(idx2,:), "o" ); 
title("Aligned signals");
subplot(3,1,1);
title("Euler Angles for traj1 and traj2")

Figure contains 3 axes objects. Axes object 1 with title Euler Angles for traj1 and traj2, ylabel traj1 contains 3 objects of type line. These objects represent Yaw (Z), Pitch (Y), Roll (X). Axes object 2 with ylabel traj2 contains 3 objects of type line. These objects represent Yaw (Z), Pitch (Y), Roll (X). Axes object 3 with title Aligned signals contains 6 objects of type line. One or more of the lines displays its values using only markers

snapnow;

Partición de datos de entrenamiento y prueba

El conjunto de datos contiene trayectorias de cuatro sujetos de prueba. Entrenará su algoritmo con datos de tres sujetos y probará el algoritmo en el cuarto sujeto. Repite este proceso cuatro veces, alternando cada vez quién se utiliza para las pruebas y quién se utiliza para el entrenamiento. Producirá una matriz de confusión para cada ronda de pruebas para ver la precisión de la clasificación.

accuracy = zeros(1,Npeople);
for pp=1:Npeople
    testPerson = people(pp);
    trainset = dataset(dataset.Who ~= testPerson,:);
    testset = dataset(dataset.Who == testPerson,:);

Con los datos de gestos recopilados, puede utilizar la función de distorsión de tiempo dinámica de cuaterniones para calcular las distancias mutuas entre todas las grabaciones de un gesto específico.

    % Preallocate a struct of distances and compute distances
    for gg=1:Ngestures
        gest = gestures(gg);
        subdata = trainset(trainset.Gesture == gest,:);
        Nsubdata = size(subdata,1);
        D = zeros(Nsubdata);
        traj = subdata.Orientation;
        parfor ii=1:Nsubdata
            for jj=1:Nsubdata
                if jj > ii  % Only calculate triangular matrix
                    D(ii,jj) = helperQDTW(traj{ii}, traj{jj}); 
                end
            end
        end
        allgestures.(gest) = traj;
        dist.(gest) = D + D.'; % Render symmetric matrix to get all mutual distances
    end

Agrupación y plantillas

El siguiente paso es generar trayectorias de plantilla para cada gesto. La estructura dist contiene las distancias mutuas entre todos los pares de grabaciones para un gesto determinado. En esta sección, agrupa todas las trayectorias de un gesto determinado según la distancia mutua. La función helperClusterWithSplitting utiliza un enfoque de división de clústeres descrito en [2]. Todas las trayectorias se colocan inicialmente en un solo grupo. El algoritmo divide el grupo si el radio del grupo (la distancia más grande entre dos trayectorias cualesquiera en el grupo) es mayor que radiusLimit.

El proceso de división continúa de forma recursiva para cada grupo. Una vez que se encuentra una mediana para cada grupo, la trayectoria asociada con ese grupo se guarda como plantilla para ese gesto en particular. Si la relación entre el número de trayectorias en el grupo y el número total de trayectorias de un gesto determinado es menor que clusterMinPct, el grupo se descarta. Esto evita que las trayectorias atípicas afecten negativamente al proceso de clasificación. Dependiendo de la elección de radiusLimit y clusterMinPct, un gesto puede tener uno o varios grupos y, por lo tanto, puede tener una o varias plantillas.

    radiusLimit = 60;
    clusterMinPct = 0.2;
    parfor gg=1:Ngestures
        gest = gestures(gg);
        Dg = dist.(gest); %#ok<*PFBNS> 
        [gclusters, gtemplates] = helperClusterWithSplitting(Dg, radiusLimit);
        clusterSizes = cellfun(@numel, gclusters, "UniformOutput", true);
        totalSize = sum(clusterSizes);
        clusterPct = clusterSizes./totalSize;
        validClusters = clusterPct > clusterMinPct;
        tidx = gtemplates(validClusters);
        tmplt = allgestures.(gest)(tidx);
        templateTraj{gg} = tmplt;
        labels{gg} = repmat(gest, numel(tmplt),1);
    end
    templates = table(vertcat(templateTraj{:}), vertcat(labels{:}));
    templates.Properties.VariableNames = ["Orientation", "Gesture"];

La variable template se almacena como table. Cada fila contiene una trayectoria de plantilla almacenada en la variable Orientation y una etiqueta de gesto asociada almacenada en la variable Gesture . Utilizarás este conjunto de plantillas para reconocer nuevos gestos en el testset.

Sistema de clasificación

Utilizando la deformación temporal dinámica de cuaterniones, se pueden comparar los nuevos datos de gestos del conjunto de prueba con cada una de las plantillas de gestos. El sistema clasifica el nuevo gesto desconocido como la clase de plantilla que tiene la distancia de deformación temporal dinámica del cuaternión más pequeña con respecto al gesto desconocido. Si la distancia a cada plantilla supera el radiusLimit, el gesto de prueba se marca como unrecognized.

    Ntest = size(testset,1);
    Ntemplates = size(templates,1);
    testTraj = testset.Orientation;
    expected = testset.Gesture;
    actual = strings(size(expected));

    parfor ii=1:Ntest
        testdist = zeros(1,Ntemplates);
        for tt=1:Ntemplates
            testdist(tt) = helperQDTW(testTraj{ii}, templates.Orientation{tt}); 
        end
        [mind, mindidx] = min(testdist);
        if mind > radiusLimit
            actual(ii) = "unrecognized";
        else
            actual(ii) = templates.Gesture{mindidx};
        end
    end
    results.(testPerson).actual = actual;
    results.(testPerson).expected = expected;
end
    

Calcule la precisión del sistema en la identificación de gestos en el conjunto de prueba y genere una matriz de confusión.

for pp=1:Npeople   
    figure;
    act = results.(people(pp)).actual;
    exp = results.(people(pp)).expected;
    numCorrect = sum(act == exp);
    Ntest = numel(act);
    accuracy = 100 * numCorrect./Ntest;
    confusionchart([exp; "unrecognized"], [act; missing]);
    title("Test subject = " + people(pp) + newline + "Accuracy = " + accuracy + "%" );
end

MATLAB figure

MATLAB figure

MATLAB figure

MATLAB figure

snapnow;

La precisión promedio en las cuatro configuraciones de los sujetos de prueba es superior al 90%.

AverageAccuracy = mean(accuracy)
AverageAccuracy = 94

Conclusión

Al fusionar los datos de IMU con el objeto imufilter y utilizar la deformación temporal dinámica de cuaterniones para comparar la trayectoria de un gesto con un conjunto de trayectorias de plantilla, se reconocen los gestos con gran precisión. Puede utilizar la fusión de sensores junto con la deformación y agrupación dinámica del tiempo de cuaterniones para construir un sistema eficaz de reconocimiento de gestos.

Referencias

[1] B. Jablonski, "Quaternion Dynamic Time Warping", en IEEE Transactions on Signal Processing, vol. 60, núm. 3 de marzo de 2012.

[2] R. Srivastava y P. Sinha, "Caracterización de gestos y movimientos de las manos mediante la técnica de deformación dinámica del tiempo de cuaterniones", en IEEE Sensors Journal, vol. 16, núm. 5, 1 de marzo de 2016.