Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Manufacturing Technology >> Proceso de manufactura

Control manual robótico mediante EMG

Componentes y suministros

dispositivo uECG
× 3
inMoov hand
× 1
Arduino Nano R3
× 1
Controlador PWM de 16 canales Adafruit PCA9685
× 1
Módulo nRF24 (genérico)
× 1

Acerca de este proyecto

Nuestro equipo tiene una larga historia con manos robóticas. Durante un tiempo intentamos hacer una mano protésica confiable, pero para este proyecto estoy usando un buen ejemplo de mano de código abierto existente:inMoov.

No entraré en detalles sobre el montaje manual; está bien descrito en el sitio del proyecto y es bastante complicado. Me enfocaré en el control aquí, ya que eso es completamente nuevo :)
Además, vea cómo esta tecnología evolucionó con el tiempo en el próximo proyecto:https://www.hackster.io/the_3d6/seeing-muscles-at -trabajo-emg-de-8-canales-con-leds-039d69

1. Procesamiento de señales

El control se basa en EMG:actividad eléctrica de los músculos. La señal EMG se obtiene mediante tres dispositivos uECG (lo sé, se supone que es un monitor de ECG, pero como se basa en un ADC genérico, puede medir cualquier bioseñal, incluida la EMG). Para el procesamiento de EMG, uECG tiene un modo especial en el que envía datos de espectro de 32 intervalos y un promedio de "ventana muscular" (intensidad espectral promedio entre 75 y 440 Hz). Las imágenes de espectro se ven así:

Aquí la frecuencia está en un eje vertical (en cada uno de los 3 gráficos, baja frecuencia en la parte inferior, alta en la parte superior, de 0 a 488 Hz con pasos de ~ 15 Hz), el tiempo está en una horizontal (datos antiguos a la izquierda en general aquí es de unos 10 segundos en la pantalla). La intensidad se codifica con el color:azul - bajo, verde - medio, amarillo - alto, rojo - incluso más alto. Para un reconocimiento de gestos confiable, se requiere un procesamiento de PC adecuado de estas imágenes. Pero para una activación simple de los dedos de la mano robótica, basta con usar el valor promedio en 3 canales:uECG lo proporciona convenientemente en ciertos bytes de paquetes para que Arduino Sketch pueda analizarlo. Estos valores parecen mucho más simples:

Los gráficos rojos, verdes y azules son valores brutos de dispositivos uECG en diferentes grupos de músculos cuando aprieto los dedos pulgar, anular y medio de manera correspondiente. Para nuestro ojo, estos casos son claramente diferentes, pero necesitamos convertir esos valores en "puntaje de dedo" de alguna manera para que un programa pueda enviar valores a los servos manuales. El problema es que las señales de los grupos de músculos están "mezcladas":en el primer y tercer caso, la intensidad de la señal azul es aproximadamente la misma, pero el rojo y el verde son diferentes. En el segundo y tercer caso, las señales verdes son las mismas, pero el azul y el rojo son diferentes. Para "desmezclarlos", utilicé una fórmula relativamente simple:

S0 =V0 ^ 2 / ((V1 * a0 + b0) (V2 * c0 + d0))

donde S0 - puntuación para el canal 0, V0, V1, V2 - valores brutos para los canales 0, 1, 2 y a, b, c, d - coeficientes que ajusté manualmente (ayc fueron de 0.3 a 2.0, by d eran 15 y 20, tendría que cambiarlos para ajustarlos a la ubicación de su sensor en particular). Se calculó la misma puntuación para los canales 1 y 2. Después de esto, los gráficos se separaron casi perfectamente:

Para los mismos gestos (esta vez el dedo anular, el medio y luego el pulgar), las señales son claras y se pueden traducir fácilmente en movimientos de servo simplemente comparándolo con el umbral.

2. Esquemas

Schematics es bastante simple, solo necesita el módulo nRF24, PCA9685 o un controlador I2C PWM similar y una fuente de alimentación de alto amperaje de 5 V que sería suficiente para mover todos estos servos a la vez (por lo que requiere al menos 5 A de potencia nominal para un funcionamiento estable).

Lista de conexiones:
nRF24 pin 1 (GND) - Arduino's GND
nRF24 pin 2 (Vcc) - Arduino's 3.3v
nRF24 pin 3 (Chip Enable) - Arduino's D9
nRF24 pin 4 (SPI:CS) - D8 de Arduino
nRF24 pin 5 (SPI:SCK) - D13 de Arduino
nRF24 pin 6 (SPI:MOSI) - D11 de Arduino
nRF24 pin 7 (SPI:MISO) - Arduino's D12
PCA9685 SDA - Arduino's A4
PCA9685 SCL - Arduino's A5
PCA9685 Vcc - Arduino's 5v
PCA9685 GND - Arduino's GND
PCA9685 V + - alto amperaje 5V
PCA9685 GND - GND de alto amperaje
Servos de dedo:a los canales PCA 0-4, en mi notación pulgar - canal 0, dedo índice - canal 1, etc.

3. Ubicación de los sensores EMG

Para obtener lecturas razonables, es importante colocar los dispositivos uECG, que registran la actividad muscular, en los lugares correctos. Si bien aquí son posibles muchas opciones diferentes, cada una requiere un enfoque de procesamiento de señal diferente, por lo que estoy compartiendo lo que he usado:

Puede ser contrario a la intuición, pero la señal del músculo del pulgar es mejor visible en el lado opuesto del brazo, por lo que uno de los sensores se coloca allí, y todos se colocan cerca del codo (los músculos tienen la mayor parte de su cuerpo en esa área). , pero desea comprobar dónde se encuentran exactamente los suyos; hay una gran diferencia individual)

4. Código

Antes de ejecutar el programa principal, deberá averiguar los ID de unidad de sus dispositivos uECG en particular (se hace descomentando la línea 101 y encendiendo los dispositivos uno por uno) y completarlos en la matriz unit_ids (línea 37).

  #include  
#include
#include
#include
#include
#include
#define SERVOMIN 150 // este es el conteo de longitud de pulso 'mínimo' (de 4096)
#define SERVOMAX 600 // este es el conteo de longitud de pulso 'máximo' (de 4096)
Adafruit_PWMServoDriver pwm =Adafruit_PWMServoDriver ();
int rf_cen =9; // pin de habilitación del chip nRF24
int rf_cs =8; // clavija nRF24 CS
RF24 rf (rf_cen, rf_cs);
// dirección de tubería - codificada en el lado de uECG
uint8_t pipe_rx [8] ={0x0E, 0xE6, 0x0D, 0xA7, 0 , 0, 0, 0};
uint8_t swapbits (uint8_t a) {// la dirección de la tubería uECG usa el orden de los bits intercambiados
// invierte el orden de los bits en un solo byte
uint8_t v =0;
if (a &0x80) v | =0x01;
if (a &0x40) v | =0x02;
if (a &0x20) v | =0x04;
if (a &0x10) v | =0x08;
if (a &0x08) v | =0x10;
if (a &0x04) v | =0x20;
if (a &0x02 ) v | =0x40;
if (a &0x01) v | =0x80;
return v;
}
long last_servo_upd =0; // hora de la última actualización de los valores de los servos; no quiero hacer esto con demasiada frecuencia
byte in_pack [32]; // matriz para el paquete RF entrante
unsigned long unit_ids [3] ={4294963881, 4294943100, 28358}; // conjunto de ID de uECG conocidos:es necesario completarlo con sus propios ID de unidad
int unit_vals [3] ={0, 0, 0}; // matriz de valores de uECG con estos ID
float tgt_angles [5]; // ángulos de destino para 5 dedos
float cur_angles [5]; // ángulos actuales para 5 dedos
float angle_open =30; // ángulo que corresponde al dedo abierto
float angle_closed =150; // ángulo que corresponde al dedo cerrado
void setup () {
// nRF24 requiere SPI relativamente lento, probablemente también funcionaría a 2MHz
SPI.begin ();
SPI .setBitOrder (MSBFIRST);
SPI.beginTransaction (SPISettings (1000000, MSBFIRST, SPI_MODE0));
for (int x =0; x <8; x ++) // nRF24 y uECG tienen un orden de bits diferente para la dirección de la tubería
pipe_rx [x] =swapbits (pipe_rx [x]);
// configurar los parámetros de radio
rf.begin ();
rf.setDataRate (RF24_1MBPS);
rf.setAddressWidth (4);
rf.setChannel (22);
rf.setRetries (0, 0);
rf.setAutoAck (0);
rf.disableDynamicPayloads ();
rf.setPayloadSize (32);
rf.openReadingPipe (0, pipe_rx);
rf.setCRCLength (RF24_CRC_DISABLED);
rf.disableCRC ();
rf.startListening (); // escucha los datos de uECG
// Tenga en cuenta que uECG debe cambiarse al modo de datos sin procesar (presionando un botón largo)
// para enviar paquetes compatibles, por defecto envía datos en modo BLE
// que no puede ser recibido por nRF24
Serial.begin (115200); // salida en serie - muy útil para depurar
pwm.begin (); // iniciar el controlador PWM
pwm.setPWMFreq (60); // Los servos analógicos funcionan a ~ 60 Hz actualizaciones
para (int i =0; i <5; i ++) // establece las posiciones iniciales de los dedos
{
tgt_angles [i] =angle_open;
cur_angles [i] =angle_open;
}
}
void setAngle (int n, float angle) {// envía el valor del ángulo para el canal dado
pwm.setPWM (n, 0, SERVOMIN + ángulo * 0.005556 * (SERVOMAX - SERVOMIN));
}
float angle_speed =15; // qué tan rápido se moverían los dedos
float v0 =0, v1 =0, v2 =0; // valores de actividad muscular filtrados por 3 canales
void loop ()
{
if (rf.available ())
{
rf.read (in_pack, 32 ); // procesando paquete
byte u1 =in_pack [3]; // ID de unidad de 32 bits, único para cada dispositivo uECG
byte u2 =in_pack [4];
byte u3 =in_pack [ 5];
byte u4 =in_pack [6];
unsigned long id =(u1 <<24) | (u2 <<16) | (u3 <<8) | u4;
//Serial.println(id); // Descomente esta línea para hacer una lista de sus ID de uECG
if (in_pack [7]! =32) id =0; // tipo de paquete incorrecto:en modo EMG este byte debe ser 32
int val =in_pack [10]; // valor de la actividad muscular
if (val! =in_pack [11]) id =0; // el valor está duplicado en 2 bytes porque el ruido de RF puede dañar el paquete, y no tenemos CRC con nRF24
// encuentra qué ID corresponde a la ID actual y el valor de relleno
para (int n =0; n <3; n ++)
if (id ==unit_ids [n])
unit_vals [n] =val;
}
long ms =millis ();
if (ms - last_servo_upd> 20) // no actualice los servos con demasiada frecuencia
{
last_servo_upd =ms;
for (int n =0; n <5; n ++) / / pasar por los dedos, si los ángulos objetivo y actual no coinciden - ajústelos
{
if (cur_angles [n] if (cur_angles [n]> tgt_angles [n] + angle_speed / 2) cur_angles [n] - =angle_speed;
}
for (int n =0; n <5; n ++) // aplica ángulos a los dedos
setAngle (n, cur_angles [n]);
// promediado exponencial:evita que los picos individuales afecten el estado del dedo
v0 =v0 * 0.7 + 0.3 * (float ) unit_vals [0];
v1 =v1 * 0.7 + 0.3 * (float) unit_vals [1];
v2 =v2 * 0.7 + 0.3 * (float) unit_vals [2];
// puntuación de cálculo s de valores brutos
float scor0 =4.0 * v0 * v0 / ((v1 * 0.3 + 20) * (v2 * 1.3 + 15));
float scor1 =4.0 * v1 * v1 / (( v0 * 2.0 + 20) * (v2 * 2.0 + 20));
float scor2 =4.0 * v2 * v2 / ((v0 * 1.2 + 20) * (v1 * 0.5 + 15));
// imprime puntuaciones para depuración
Serial.print (scor0);
Serial.print ('');
Serial.print (scor1);
Serial.print (' ');
Serial.println (scor2);
// compara cada puntaje con el umbral y cambia los estados de los dedos en consecuencia
if (scor2 <0.5) // señal débil - dedo abierto
tgt_angles [0] =angle_open;
if (scor2> 1.0) // señal fuerte - cerrar el dedo
tgt_angles [0] =angle_closed;
if (scor1 <0.5)
{
tgt_angles [1] =ángulo_open;
tgt_angles [2] =angle_open;
}
if (scor1> 1.0)
{
tgt_angles [1 ] =ángulo_cerrado;
tgt_angles [2] =ángulo_cierre;
}
if (scor0 <0.5)
{
tgt_angles [3] =ángulo_open;
tgt_angles [4] =ángulo_open;
}
if (scor0> 1.0)
{
tgt_angles [3] =ángulo_closed;
tgt_angles [4] =ángulo_cierre;
}
}
}

5. Resultados

Con algunos experimentos que tomaron aproximadamente 2 horas, pude obtener un funcionamiento bastante confiable (el video muestra un caso típico):

No se comporta perfectamente y con este procesamiento solo puede reconocer los dedos abiertos y cerrados (y ni siquiera cada uno de los 5, detecta solo 3 grupos musculares:pulgar, índice y medio juntos, anular y meñique juntos). Pero "AI" que analiza la señal toma 3 líneas de código aquí y usa un solo valor de cada canal. Creo que se podría hacer mucho más analizando imágenes espectrales de 32 contenedores en una PC o un teléfono inteligente. Además, esta versión utiliza solo 3 dispositivos uECG (canales EMG). Con más canales, debería ser posible reconocer patrones realmente complejos, pero bueno, ese es el objetivo del proyecto, proporcionar un punto de partida para cualquiera que esté interesado :) El control manual definitivamente no es la única aplicación para dicho sistema.

Código

  • emg_hand_control2.ino
emg_hand_control2.ino Arduino
 #include  #include  #include  #include  #include  #include  #define SERVOMIN 150 / / este es el recuento de longitud de pulso 'mínimo' (de 4096) #define SERVOMAX 600 // este es el recuento de longitud de pulso 'máximo' (de 4096) Adafruit_PWMServoDriver pwm =Adafruit_PWMServoDriver (); int rf_cen =9; // chip nRF24 habilita pinint rf_cs =8; // nRF24 CS pinRF24 rf (rf_cen, rf_cs); // dirección de tubería - codificada en uECG sideuint8_t pipe_rx [8] ={0x0E, 0xE6, 0x0D, 0xA7, 0, 0, 0, 0}; uint8_t swapbits (uint8_t a) {// La dirección de la tubería uECG usa el orden de bits intercambiados // invierte el orden de los bits en un solo byte uint8_t v =0; si (a &0x80) v | =0x01; si (a &0x40) v | =0x02; si (a &0x20) v | =0x04; si (a &0x10) v | =0x08; si (a &0x08) v | =0x10; si (a &0x04) v | =0x20; si (a &0x02) v | =0x40; si (a &0x01) v | =0x80; return v;} long last_servo_upd =0; // hora en la que actualizamos los valores de los servos por última vez - no quiero hacer esto con demasiada frecuenciabyte in_pack [32]; // matriz para paquetes RF entrantes sin firmar long unit_ids [3] ={4294963881, 4294943100, 28358}; // matriz de ID de uECG conocidos:debe completarla con su propia ID de unidad sint unit_vals [3] ={0, 0, 0}; // matriz de valores de uECG con estos IDsfloat tgt_angles [5]; // ángulos objetivo para 5 fingerfloat cur_angles [5]; // ángulos actuales para 5 dedos flotante angle_open =30; // ángulo que corresponde al flotador abierto angle_closed =150; // ángulo que corresponde a la configuración de vacío de dedos cerrado () {// nRF24 requiere SPI relativamente lento, probablemente también funcionaría a 2MHz SPI.begin (); SPI.setBitOrder (MSBFIRST); SPI.beginTransaction (SPISettings (1000000, MSBFIRST, SPI_MODE0)); for (int x =0; x <8; x ++) // nRF24 y uECG tienen un orden de bits diferente para la dirección de tubería pipe_rx [x] =swapbits (pipe_rx [x]); // configurar los parámetros de radio rf.begin (); rf.setDataRate (RF24_1MBPS); rf.setAddressWidth (4); rf.setChannel (22); rf.setRetries (0, 0); rf.setAutoAck (0); rf.disableDynamicPayloads (); rf.setPayloadSize (32); rf.openReadingPipe (0, pipe_rx); rf.setCRCLength (RF24_CRC_DISABLED); rf.disableCRC (); rf.startListening (); // escuche los datos de uECG // Tenga en cuenta que uECG debe cambiarse al modo de datos sin procesar (presionando un botón largo) // para enviar paquetes compatibles, por defecto envía datos en modo BLE // que no pueden ser recibidos por nRF24 Serial .comienzo (115200); // salida en serie - muy útil para depurar pwm.begin (); // iniciar el controlador PWM pwm.setPWMFreq (60); // Los servos analógicos funcionan con actualizaciones de ~ 60 Hz para (int i =0; i <5; i ++) // establece las posiciones iniciales de los dedos {tgt_angles [i] =angle_open; cur_angles [i] =ángulo_open; }} void setAngle (int n, float angle) {// envía el valor del ángulo para el canal dado pwm.setPWM (n, 0, SERVOMIN + angle * 0.005556 * (SERVOMAX - SERVOMIN));} float angle_speed =15; // qué tan rápido se moverían los dedos flotando v0 =0, v1 =0, v2 =0; // valores de actividad muscular filtrados por 3 canales void loop () {if (rf.available ()) {rf.read (in_pack, 32); // procesando el byte del paquete u1 =in_pack [3]; // ID de unidad de 32 bits, único para cada byte de dispositivo uECG u2 =in_pack [4]; byte u3 =in_pack [5]; byte u4 =in_pack [6]; id largo sin firmar =(u1 <<24) | (u2 <<16) | (u3 <<8) | u4; //Serial.println(id); // Descomente esta línea para hacer una lista de sus ID de uECG if (in_pack [7]! =32) id =0; // tipo de paquete incorrecto:en modo EMG este byte debe ser 32 int val =in_pack [10]; // valor de la actividad muscular if (val! =in_pack [11]) id =0; // el valor está duplicado en 2 bytes porque el ruido de RF puede dañar el paquete, y no tenemos CRC con nRF24 // encontrar qué ID corresponde a la ID actual y llenar el valor para (int n =0; n <3; n ++) si (id ==unit_ids [n]) unit_vals [n] =val; } ms largo =milis (); if (ms - last_servo_upd> 20) // no actualice los servos con demasiada frecuencia {last_servo_upd =ms; for (int n =0; n <5; n ++) // pasar por los dedos, si los ángulos objetivo y actual no coinciden, ajústelos {if (cur_angles [n]  tgt_angles [n] + angle_speed / 2) cur_angles [n] - =ángulo_speed; } for (int n =0; n <5; n ++) // aplica ángulos a los dedos setAngle (n, cur_angles [n]); // Promedio exponencial:evita que los picos individuales afecten al estado del dedo v0 =v0 * 0.7 + 0.3 * (flotante) unit_vals [0]; v1 =v1 * 0,7 + 0,3 * (flotante) unit_vals [1]; v2 =v2 * 0,7 + 0,3 * (flotante) unit_vals [2]; // Calcular puntuaciones a partir de valores brutos float scor0 =4.0 * v0 * v0 / ((v1 * 0.3 + 20) * (v2 * 1.3 + 15)); flotar scor1 =4.0 * v1 * v1 / ((v0 * 2.0 + 20) * (v2 * 2.0 + 20)); flotar scor2 =4.0 * v2 * v2 / ((v0 * 1.2 + 20) * (v1 * 0.5 + 15)); // imprime puntuaciones para depurar Serial.print (scor0); Serial.print (''); Serial.print (scor1); Serial.print (''); Serial.println (scor2); // compara cada puntuación con el umbral y cambia los estados de los dedos en consecuencia if (scor2 <0.5) // señal débil - dedo abierto tgt_angles [0] =angle_open; if (scor2> 1.0) // señal fuerte - dedo cerrado tgt_angles [0] =angle_closed; if (scor1 <0.5) {tgt_angles [1] =angle_open; tgt_angles [2] =ángulo_open; } if (scor1> 1.0) {tgt_angles [1] =angle_closed; tgt_angles [2] =ángulo_cierre; } if (scor0 <0.5) {tgt_angles [3] =angle_open; tgt_angles [4] =ángulo_open; } if (scor0> 1.0) {tgt_angles [3] =angle_closed; tgt_angles [4] =ángulo_cierre; }}} 

Esquemas

nrf24_hand_control_5jcEeCP8a3.fzz

Proceso de manufactura

  1. Píldora anticonceptiva
  2. Haga un vehículo robótico inalámbrico usando sensores de infrarrojos
  3. El diseño de referencia simplifica el control de motores robóticos industriales
  4. Sistema de control de dispositivo basado en temperatura usando LM35
  5. Uso de IA para controlar las propiedades de la luz | Generación de supercontinuo
  6. Uso del software de simulación de robots 3DG para planificar la automatización robótica
  7. Control automático de trenes
  8. Control remoto universal usando Arduino, 1Sheeld y Android
  9. Uso de IoT para controlar de forma remota un brazo robótico
  10. Controla Arduino Rover usando Firmata y el controlador Xbox One
  11. Los estudiantes construyen un sistema robótico de clasificación de basura usando tecnología B&R