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 el alineamiento temporal dinámico de cuaterniones para crear un algoritmo de coincidencia de plantillas para clasificar cinco gestos.
El alineamiento temporal dinámico es un algoritmo utilizado para medir la similitud entre dos series temporales de datos. El alineamiento temporal dinámico compara dos secuencias alineando los puntos de datos de la primera secuencia con los puntos de datos de la segunda, descuidando la sincronización temporal . El alineamiento temporal dinámico también proporciona una métrica de distancia entre dos secuencias no alineadas.
Similar al alineamiento temporal dinámico, el alineamiento temporal dinámico de cuaterniones compara dos secuencias en el espacio de cuaterniones o rotacional [1].
El alineamiento temporal dinámico 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 el alineamiento temporal dinámico de cuaterniones y la agrupación 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 vuelva al nivel.
down - L
baja el brazo y luego vuelve 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
- Gira la manecilla 90 grados en el sentido de las agujas del reloj y vuelve a la orientación original. El antebrazo está paralelo al suelo durante todo el ejercicio.
Los datos de estos cinco gestos se capturan utilizando MATLAB® Support Package for Arduino® Hardware. Cuatro personas diferentes realizaron los cinco gestos y repitieron cada gesto de nueve a diez veces. Los datos registrados, guardados como table
, contienen lecturas del acelerómetro y del giroscopio, la 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á imufilter
System object ™ para fusionar las lecturas del acelerómetro y el giroscopio. El imufilter
System object está disponible tanto en Navigation Toolbox ™ como en Sensor Fusion and Tracking Toolbox ™. Puede utilizar un bucle parfor
para acelerar el cálculo. Si tiene Parallel Computing Toolbox ™, el bucle parfor
se ejecutará en paralelo; de lo contrario, un bucle for
normal se ejecutará secuencialmente.
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 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 de alineamiento temporal dinámico del cuaternión
Alineamiento temporal dinámico del cuaternión [1] compara dos series temporales de orientación en el espacio del cuaternión y formula una métrica de distancia compuesta de tres partes relacionadas con la distancia del cuaternión, la derivada y la curvatura. En este ejemplo, solo comparará señales según la distancia del cuaternión.
El alineamiento temporal dinámico de cuaterniones utiliza la distorsión del tiempo para calcular una alineación óptima entre dos secuencias.
A continuación se utiliza el alineamiento temporal dinámico 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 longitudes diferentes y realizan la transición a lo largo de esas orientaciones a ritmos diferentes.
A continuación, compare y encuentre la mejor alineación entre estas dos trayectorias utilizando el alineamiento temporal dinámico de cuaterniones.
[qdist, idx1, idx2] = helperQDTW(traj1, traj2);
La métrica de distancia entre ellos es un escalar.
qdist
qdist = 1.1474
Grafique la mejor alineación utilizando 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 alineamiento temporal dinámico 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 clúster si el radio del clúster (la distancia más grande entre dos trayectorias cualesquiera en el clúster) 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 del 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 testset
.
Sistema de clasificación
Utilizando el alineamiento temporal dinámico 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 alineamiento temporal dinámico del cuaternión más pequeña con respecto al gesto desconocido. Si la distancia a cada plantilla supera los 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
Calcular la precisión del sistema al identificar gestos en el conjunto de prueba y generar 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 datos IMU con el objeto imufilter
y usar el alineamiento temporal dinámico de cuaterniones para comparar una trayectoria de gesto con un conjunto de trayectorias de plantilla, se reconocen gestos con gran precisión. Puede utilizar la fusión de sensores junto con la deformación y agrupación de alineamiento temporal dinámico 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 movimientos y gestos de las manos mediante la técnica de alineamiento temporal dinámico de cuaterniones", en IEEE Sensors Journal, vol. 16, n.º 5, 1 de marzo de 2016.