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.
down - L
Baje el brazo y luego regrese al nivel.
left
- Desliza el brazo hacia la izquierda y luego regresa al centro. El antebrazo está paralelo al suelo durante todo el ejercicio.
right
- Desliza el brazo hacia la derecha y luego regresa al centro. El antebrazo está paralelo al suelo durante todo el ejercicio.
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.
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")
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
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.