Industriële fabricage
Industrieel internet der dingen | Industriële materialen | Onderhoud en reparatie van apparatuur | Industriële programmering |
home  MfgRobots >> Industriële fabricage >  >> Manufacturing Technology >> Productieproces

Robotische handbediening met behulp van EMG

Componenten en benodigdheden

uECG-apparaat
× 3
inMoov-hand
× 1
Arduino Nano R3
× 1
Adafruit PCA9685 16-kanaals PWM-stuurprogramma
× 1
nRF24-module (algemeen)
× 1

Over dit project

Ons team heeft een lang verhaal met robothanden. We hebben een tijdje geprobeerd een betrouwbare handprothese te maken, maar voor dit project gebruik ik een goed voorbeeld van een bestaande open source hand:inMoov.

Ik zal niet ingaan op de details van handmontage - het staat goed beschreven op de projectsite en is behoorlijk ingewikkeld. Ik zal me hier concentreren op controle, want dat is helemaal nieuw :)
Bekijk ook hoe deze technologie zich in de loop van de tijd heeft ontwikkeld in het volgende project:https://www.hackster.io/the_3d6/seeing-muscles-at -work-8-channel-emg-with-leds-039d69

1. Signaalverwerking

De controle is gebaseerd op EMG - elektrische activiteit van spieren. Het EMG-signaal wordt verkregen door drie uECG-apparaten (ik weet het, het is de bedoeling dat het een ECG-monitor is, maar aangezien het is gebaseerd op een generieke ADC, kan het alle biosignalen meten, inclusief EMG). Voor EMG-verwerking heeft uECG een speciale modus waarin het 32-bin spectrumgegevens en het "spiervenster"-gemiddelde (gemiddelde spectrale intensiteit tussen 75 en 440 Hz) verzendt. Spectrumafbeeldingen zien er als volgt uit:

Hier staat de frequentie op een verticale as (op elk van de 3 plots, lage frequentie onderaan, hoog bovenaan - van 0 tot 488 Hz met stappen van ~15 Hz), de tijd staat horizontaal (oude gegevens links in het algemeen hier is ongeveer 10 seconden op het scherm). Intensiteit is gecodeerd met kleur:blauw - laag, groen - gemiddeld, geel - hoog, rood - zelfs hoger. Voor een betrouwbare gebarenherkenning is een goede pc-verwerking van deze afbeeldingen vereist. Maar voor eenvoudige activering van robothandvingers is het voldoende om gewoon de gemiddelde waarde op 3 kanalen te gebruiken - uECG levert het gemakkelijk op bepaalde pakketbytes, zodat Arduino-schets het kan ontleden. Deze waarden zien er veel eenvoudiger uit:

Rode, groene en blauwe grafieken zijn ruwe waarden van uECG-apparaten op verschillende spiergroepen wanneer ik dienovereenkomstig in duim, ring en middelvinger knijp. Voor ons oog zijn deze gevallen duidelijk anders, maar we moeten die waarden op de een of andere manier in "vingerscore" veranderen, zodat een programma waarden kan uitvoeren naar handservo's. Het probleem is dat signalen van spiergroepen "gemengd" zijn:in het 1e en 3e geval is de intensiteit van het blauwe signaal ongeveer hetzelfde - maar rood en groen zijn verschillend. In het 2e en 3e geval zijn groene seinen hetzelfde, maar blauw en rood zijn verschillend. Om ze te "ontmengen", heb ik een relatief eenvoudige formule gebruikt:

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

waarbij S0 - score voor kanaal 0, V0, V1, V2 - onbewerkte waarden voor kanalen 0, 1, 2 en a, b, c, d - coëfficiënten die ik handmatig heb aangepast (a en c waren van 0,3 tot 2,0, b en d 15 en 20 waren, zou u ze toch moeten wijzigen om ze aan te passen aan uw specifieke sensorplaatsing). Dezelfde score werd berekend voor kanaal 1 en 2. Hierna werden de grafieken bijna perfect gescheiden:

Voor dezelfde gebaren (dit keer ringvinger, middel en dan duim) zijn de signalen duidelijk en kunnen ze eenvoudig worden vertaald in servobewegingen, gewoon door te vergelijken met de drempel.

2. Schema's

Schema's zijn vrij eenvoudig, je hebt alleen een nRF24-module, PCA9685 of vergelijkbare I2C PWM-controller en een 5V-voeding met hoge ampère nodig die voldoende zou zijn om al deze servo's tegelijk te verplaatsen (dus het vereist minimaal 5A nominaal vermogen voor een stabiele werking). /P>

Lijst met aansluitingen:
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) - Arduino's D8
nRF24 pin 5 (SPI:SCK) - Arduino's D13
nRF24 pin 6 (SPI:MOSI) - Arduino's D11
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+ - hoge versterker 5V
PCA9685 GND - hoge versterker GND
Vingerservo's:naar PCA-kanalen 0-4, in mijn notatie duim - kanaal 0, wijsvinger - kanaal 1 enz.

3. Plaatsing EMG-sensoren

Om redelijke metingen te krijgen, is het belangrijk om uECG-apparaten, die spieractiviteit registreren, op de juiste plaatsen te plaatsen. Hoewel hier veel verschillende opties mogelijk zijn, vereist elk een andere benadering van signaalverwerking - dus ik deel wat ik heb gebruikt:

Het kan contra-intuïtief zijn, maar het signaal van de duimspier is beter zichtbaar aan de andere kant van de arm, dus een van de sensoren is daar geplaatst en ze zijn allemaal dicht bij de elleboog geplaatst (spieren hebben het grootste deel van hun lichaam in dat gebied , maar u wilt controleren waar de uwe zich precies bevindt - er is nogal een groot individueel verschil)

4. Coderen

Voordat u het hoofdprogramma uitvoert, moet u de unit-ID's van uw specifieke uECG-apparaten achterhalen (dit wordt gedaan door regel 101 te verwijderen en apparaten één voor één aan te zetten) en deze in de unit_ids-array (regel 37) in te vullen.

#include 
#include
#include
#include
#include
#include
#define SERVOMIN 150 // dit is het 'minimale' aantal pulsen (van 4096)
#define SERVOMAX 600 // dit is het 'maximale' aantal pulsen (van 4096)
Adafruit_PWMServoDriver pwm =Adafruit_PWMServoDriver();
int rf_cen =9; //nRF24-chip inschakelen pin
int rf_cs =8; //nRF24 CS-pin
RF24 rf(rf_cen, rf_cs);
//pipe-adres - hardgecodeerd aan uECG-zijde
uint8_t pipe_rx[8] ={0x0E, 0xE6, 0x0D, 0xA7, 0 , 0, 0, 0};
uint8_t swapbits(uint8_t a){ //uECG-pipe-adres gebruikt gewisselde bits-volgorde
// keer de bitvolgorde om in een enkele 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;
retour v;
}
long last_servo_upd =0; // tijd waarop we de servowaarden voor het laatst hebben bijgewerkt - we willen dit niet te vaak doen
byte in_pack[32]; //array voor inkomend RF-pakket
unsigned long unit_ids[3] ={4294963881, 4294943100, 28358}; //array van bekende uECG-ID's - moet worden ingevuld met uw eigen unit-ID's
int unit_vals[3] ={0, 0, 0}; //array van uECG-waarden met deze ID's
float tgt_angles[5]; // doelhoeken voor 5 vingers
float cur_angles[5]; //huidige hoeken voor 5 vingers
float angle_open =30; //hoek die overeenkomt met open vinger
float angle_closed =150; //hoek die overeenkomt met gesloten vinger
void setup() {
//nRF24 vereist relatief langzame SPI, zou waarschijnlijk ook werken op 2MHz
SPI.begin();
SPI .setBitOrder(MSBFIRST);
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
for(int x =0; x <8; x++) //nRF24 en uECG hebben een verschillende bitvolgorde voor pijpadres
pipe_rx[x] =swapbits(pipe_rx[x]);
//radioparameters configureren
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(); //luister naar uECG-gegevens
//Merk op dat uECG in de onbewerkte gegevensmodus moet worden geschakeld (door lang op de knop te drukken)
// om compatibele pakketten te verzenden, verzendt het standaard gegevens in BLE-modus
//die niet kan worden ontvangen door nRF24
Serial.begin(115200); //seriële uitvoer - erg handig voor het debuggen
pwm.begin(); //start PWM-stuurprogramma
pwm.setPWMFreq(60); // Analoge servo's draaien op ~60 Hz updates
for(int i =0; i <5; i++) //set initiële vingerposities
{
tgt_angles[i] =angle_open;
cur_angles[i] =angle_open;
}
}
void setAngle(int n, float angle){ //stuurt hoekwaarde voor gegeven kanaal
pwm.setPWM (n, 0, SERVOMIN + hoek * 0,005556 * (SERVOMAX - SERVOMIN));
}
float angle_speed =15; //hoe snel vingers zouden bewegen
zweven v0 =0, v1 =0, v2 =0; //gefilterde spieractiviteitswaarden per 3 kanalen
void loop()
{
if(rf.available())
{
rf.read(in_pack, 32 ); //verwerkingspakket
byte u1 =in_pack[3];//32-bits eenheids-ID, uniek voor elk uECG-apparaat
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); // verwijder commentaar op deze regel om een ​​lijst te maken van uw uECG-ID's
if(in_pack[7]!=32) id =0; //verkeerd pakkettype:in EMG-modus moet deze byte 32 zijn
int val =in_pack[10]; // Waarde spieractiviteit
if(val !=in_pack[11]) id =0; //waarde wordt gedupliceerd in 2 bytes omdat RF-ruis het pakket kan beschadigen, en we hebben geen CRC met nRF24
//vind welke ID overeenkomt met huidige ID en vulwaarde
for(int n =0; n <3; n++)
if(id ==unit_ids[n])
unit_vals[n] =val;
}
lange ms =millis();
if(ms - last_servo_upd> 20) //update servo's niet te vaak
{
last_servo_upd =ms;
for(int n =0; n <5; n++) / /ga door vingers, als doel en huidige hoeken niet overeenkomen - pas ze aan
{
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++) //pas hoeken toe op vingers
setAngle(n, cur_angles[n]);
//exponentiële middeling:voorkomt dat enkele pieken de vingerstatus beïnvloeden
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];
//score berekenen s van onbewerkte waarden
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));
//printscores voor debugging
Serial.print(scor0);
Serial.print(' ');
Serial.print(scor1);
Serial.print(' ');
Serial.println(scor2);
//vergelijk elke score met drempel en verander de vingerstatus dienovereenkomstig
if(scor2 <0,5) //zwak signaal - open vinger
tgt_angles[0] =angle_open;
if(scor2> 1.0) //sterk signaal - vinger sluiten
tgt_angles[0] =angle_closed;
if(scor1 <0.5)
{
tgt_angles[1] =angle_open;
tgt_angles[2] =angle_open;
}
if(scor1> 1.0)
{
tgt_angles[1 ] =angle_closed;
tgt_angles[2] =angle_closed;
}
if(scor0 <0.5)
{
tgt_angles[3] =angle_open;
tgt_angles[4] =angle_open;
}
if(scor0> 1.0)
{
tgt_angles[3] =angle_closed;
tgt_angles[4] =angle_closed;
}
}
}

5. Resultaten

Met enkele experimenten die ongeveer 2 uur duurden, kon ik een redelijk betrouwbare werking krijgen (video toont een typisch geval):

Het gedraagt ​​zich niet perfect en kan met deze verwerking alleen open en gesloten vingers herkennen (en zelfs niet elk van de 5, het detecteert slechts 3 spiergroepen:duim, wijs- en middelvinger samen, ringvinger en pink samen). Maar "AI" die het signaal analyseert, heeft hier 3 regels code en gebruikt een enkele waarde van elk kanaal. Ik denk dat er veel meer kan worden gedaan door spectrale beelden met 32 ​​bins op pc of smartphone te analyseren. Ook gebruikt deze versie slechts 3 uECG-apparaten (EMG-kanalen). Met meer kanalen zou het mogelijk moeten zijn om echt complexe patronen te herkennen - maar goed, dat is het punt van het project, om een ​​startpunt te bieden voor iedereen die geïnteresseerd is :) Handbediening is zeker niet de enige toepassing voor een dergelijk systeem.

Code

  • emg_hand_control2.ino
emg_hand_control2.inoArduino
#include #include #include #include #include #include #define SERVOMIN 150 / / dit is de 'minimale' pulslengtetelling (van 4096)#define SERVOMAX 600 // dit is de 'maximale' pulslengtetelling (van 4096)Adafruit_PWMServoDriver pwm =Adafruit_PWMServoDriver();int rf_cen =9; //nRF24-chip maakt pinint rf_cs =8 mogelijk; //nRF24 CS pinRF24 rf(rf_cen, rf_cs);//pipe-adres - hardcoded op uECG sideuint8_t pipe_rx[8] ={0x0E, 0xE6, 0x0D, 0xA7, 0, 0, 0, 0};uint8_t swapbits(uint8_t a) {//uECG-pijpadres maakt gebruik van verwisselde bits-volgorde // keer de bitvolgorde om in een enkele 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; // tijd waarop we de servo-waarden voor het laatst hebben bijgewerkt - wil dit niet te vaak doenbyte in_pack[32]; // array voor inkomende RF-packetunsigned long unit_ids [3] ={4294963881, 4294943100, 28358}; // array van bekende uECG-ID's - moet worden gevuld met uw eigen unit-IDsint unit_vals [3] ={0, 0, 0}; //array van uECG-waarden met deze IDsfloat tgt_angles [5]; // doelhoeken voor 5 fingersfloat cur_angles [5]; // huidige hoeken voor 5 fingersfloat angle_open =30; //hoek die overeenkomt met open fingerfloat angle_closed =150; //hoek die overeenkomt met gesloten fingervoid setup() {//nRF24 vereist relatief langzame SPI, zou waarschijnlijk ook werken op 2MHz SPI.begin(); SPI.setBitOrder(MSBFIRST); SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0)); for(int x =0; x <8; x++) //nRF24 en uECG hebben verschillende bitvolgorde voor pijpadres pipe_rx[x] =swapbits(pipe_rx[x]); //configureer radioparameters 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(); //luister naar uECG-gegevens //Merk op dat uECG in de onbewerkte gegevensmodus moet worden geschakeld (door lang op de knop te drukken) // om compatibele pakketten te verzenden, verzendt het standaard gegevens in BLE-modus // die niet kunnen worden ontvangen door nRF24 Serieel .begin(115200); //seriële uitvoer - erg handig voor het debuggen van pwm.begin(); //start PWM-stuurprogramma pwm.setPWMFreq (60); // Analoge servo's draaien op ~ 60 Hz-updates voor (int i =0; i <5; i ++) // stel initiële vingerposities in {tgt_angles [i] =angle_open; cur_angles[i] =angle_open; }} ongeldig setAngle (int n, zweefhoek) { // verzendt hoekwaarde voor gegeven kanaal pwm.setPWM (n, 0, SERVOMIN + hoek * 0.005556 * (SERVOMAX - SERVOMIN));} zweefhoek_snelheid =15; // hoe snel vingers zouden zweven v0 =0, v1 =0, v2 =0; // gefilterde spieractiviteitswaarden per 3 kanalenvoid loop() {if(rf.available()) {rf.read(in_pack, 32); //verwerking van pakketbyte u1 =in_pack[3];//32-bits eenheids-ID, uniek voor elke uECG-apparaatbyte 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); // verwijder commentaar op deze regel om een ​​lijst te maken van uw uECG-ID's if(in_pack[7]!=32) id =0; // verkeerd pakkettype:in EMG-modus moet deze byte 32 zijn int val =in_pack [10]; // waarde spieractiviteit if (val! =in_pack [11]) id =0; // waarde wordt gedupliceerd in 2 bytes omdat RF-ruis het pakket kan beschadigen, en we hebben geen CRC met nRF24 // zoek welke ID overeenkomt met de huidige ID en vulwaarde voor (int n =0; n <3; n++) als (id ==unit_ids[n]) unit_vals[n] =waarde; } lange ms =millis(); if (ms - last_servo_upd> 20) // update servo's niet te vaak {last_servo_upd =ms; for(int n =0; n <5; n++) // ga door de vingers, als doel en huidige hoeken niet overeenkomen - pas ze aan {if(cur_angles[n]  tgt_angles[n] + angle_speed/2) cur_angles[n] -=angle_speed; } for(int n =0; n <5; n++) // hoeken toepassen op vingers setAngle(n, cur_angles[n]); //exponentiële middeling:voorkomt dat enkele pieken de vingerstatus beïnvloeden 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]; //scores berekenen van onbewerkte waarden 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)); // print scores voor het debuggen van Serial.print (scor0); Serieel.print(' '); Seriële.print(scor1); Serieel.print(' '); Seriële.println(scor2); // vergelijk elke score met de drempel en wijzig de vingerstaten dienovereenkomstig als (scor2 <0,5) // zwak signaal - open vinger tgt_angles [0] =angle_open; if (scor2> 1.0) // sterk signaal - sluit vinger tgt_angles [0] =angle_closed; if(scor1 <0.5) { tgt_angles[1] =angle_open; tgt_angles[2] =angle_open; } if(scor1> 1.0) { tgt_angles[1] =angle_closed; tgt_angles[2] =angle_closed; } if(scor0 <0.5) { tgt_angles[3] =angle_open; tgt_angles[4] =angle_open; } if(scor0> 1.0) { tgt_angles[3] =angle_closed; tgt_angles[4] =angle_closed; } }}

Schema's

nrf24_hand_control_5jcEeCP8a3.fzz

Productieproces

  1. Birth Control Pil
  2. Maak een draadloos robotvoertuig met IR-sensoren
  3. Referentieontwerp vereenvoudigt industriële robotmotorbesturing
  4. Temperatuurgebaseerd apparaatbesturingssysteem met LM35
  5. AI gebruiken om de eigenschappen van licht te controleren | Supercontinuümgeneratie
  6. 3DG-robotsimulatiesoftware gebruiken om robotautomatisering te plannen
  7. Automatische treinbesturing
  8. Universele afstandsbediening met Arduino, 1Sheeld en Android
  9. Ioed gebruiken om een ​​robotarm op afstand te bedienen
  10. Bedien Arduino Rover met Firmata en Xbox One Controller
  11. Leerlingen bouwen gerobotiseerd afvalsorteersysteem met B&R-technologie