Manufaktur industri
Industri Internet of Things | bahan industri | Pemeliharaan dan Perbaikan Peralatan | Pemrograman industri |
home  MfgRobots >> Manufaktur industri >  >> Manufacturing Technology >> Proses manufaktur

Kontrol Tangan Robotik Menggunakan EMG

Komponen dan persediaan

perangkat uECG
× 3
inMoov hand
× 1
Arduino Nano R3
× 1
Adafruit PCA9685 16-channel PWM driver
× 1
Modul nRF24 (Generik)
× 1

Tentang proyek ini

Tim kami memiliki cerita panjang dengan tangan robot. Untuk sementara kami mencoba membuat tangan palsu yang andal, tetapi untuk proyek ini saya menggunakan contoh yang bagus dari tangan sumber terbuka yang ada:inMoov.

Saya tidak akan membahas detail perakitan tangan - ini dijelaskan dengan baik di situs proyek dan cukup rumit. Saya akan fokus pada kontrol di sini, karena itu benar-benar baru :)
Selain itu, lihat bagaimana teknologi ini berkembang dari waktu ke waktu di proyek berikutnya:https://www.hackster.io/the_3d6/seeing-muscles-at -work-8-channel-emg-with-leds-039d69

1. Pemrosesan sinyal

Kontrol didasarkan pada EMG - aktivitas listrik otot. Sinyal EMG diperoleh oleh tiga perangkat uECG (saya tahu, ini seharusnya monitor EKG, tetapi karena didasarkan pada ADC generik, ia dapat mengukur sinyal biologis apa pun - termasuk EMG). Untuk pemrosesan EMG, uECG memiliki mode khusus yang mengirimkan data spektrum 32-bin, dan rata-rata "jendela otot" (intensitas spektral rata-rata antara 75 dan 440 Hz). Gambar spektrum terlihat seperti ini:

Di sini frekuensi berada pada sumbu vertikal (pada masing-masing dari 3 plot, frekuensi rendah di bagian bawah, tinggi di bagian atas - dari 0 hingga 488 Hz dengan langkah ~15 Hz), waktu berada pada horizontal (data lama di sebelah kiri secara keseluruhan di sini adalah sekitar 10 detik di layar). Intensitas dikodekan dengan warna:biru - rendah, hijau - sedang, kuning - tinggi, merah - bahkan lebih tinggi. Untuk pengenalan gerakan yang andal, diperlukan pemrosesan PC yang tepat untuk gambar-gambar ini. Tetapi untuk aktivasi sederhana dari jari tangan robot, cukup menggunakan nilai rata-rata pada 3 saluran - uECG menyediakannya dengan mudah pada paket byte tertentu sehingga sketsa Arduino dapat menguraikannya. Nilai-nilai ini terlihat lebih sederhana:

Grafik merah, hijau, biru adalah nilai mentah dari perangkat uECG pada kelompok otot yang berbeda ketika saya meremas ibu jari, jari manis dan jari tengah secara bersamaan. Untuk mata kita, kasus-kasus ini jelas berbeda, tetapi kita perlu mengubah nilai-nilai itu menjadi "skor jari" entah bagaimana sehingga sebuah program dapat menampilkan nilai ke servos tangan. Masalahnya adalah, sinyal dari kelompok otot "bercampur":dalam kasus 1 dan 3 intensitas sinyal biru hampir sama - tetapi merah dan hijau berbeda. Dalam kasus ke-2 dan ke-3, sinyal hijau adalah sama - tetapi biru dan merah berbeda. Untuk "membatalkan" mereka, saya menggunakan rumus yang relatif sederhana:

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

di mana S0 - skor untuk saluran 0, V0, V1, V2 - nilai mentah untuk saluran 0, 1, 2, dan a, b, c, d - koefisien yang saya sesuaikan secara manual (a dan c adalah dari 0,3 hingga 2,0, b dan d adalah 15 dan 20, Anda tetap harus mengubahnya untuk menyesuaikan penempatan sensor khusus Anda). Skor yang sama dihitung untuk saluran 1 dan 2. Setelah ini, grafik menjadi hampir terpisah sempurna:

Untuk isyarat yang sama (kali ini jari manis, tengah, dan kemudian ibu jari) sinyalnya jelas dan dapat dengan mudah diterjemahkan ke dalam gerakan servo hanya dengan membandingkan dengan ambang batas.

2. Skema

Skemanya cukup sederhana, Anda hanya memerlukan modul nRF24, PCA9685 atau pengontrol PWM I2C serupa, dan catu daya 5V amp tinggi yang cukup untuk menggerakkan semua servos ini sekaligus (sehingga memerlukan setidaknya daya pengenal 5A untuk operasi yang stabil).

Daftar koneksi:
nRF24 pin 1 (GND) - GND Arduino
nRF24 pin 2 (Vcc) - 3.3v
nRF24 pin 3 Arduino (Chip Enable) - D9
nRF24 Arduino pin 4 (SPI:CS) - D8
nRF24 pin 5 Arduino (SPI:SCK) - D13
nRF24 pin 6 Arduino (SPI:MOSI) - D11
nRF24 pin 7 Arduino (SPI:MISO) - D12 Arduino
PCA9685 SDA - A4
PCA9685 SCL Arduino - A5
PCA9685 Vcc Arduino - 5v
PCA9685 GND Arduino - GND Arduino
PCA9685 V+ - amp tinggi 5V
PCA9685 GND - GND amp tinggi
Servos jari:ke saluran PCA 0-4, dalam notasi saya jempol - saluran 0, jari telunjuk - saluran 1 dll.

3. Penempatan sensor EMG

Untuk mendapatkan pembacaan yang masuk akal, penting untuk menempatkan perangkat uECG, yang merekam aktivitas otot, di tempat yang tepat. Meskipun banyak opsi berbeda dimungkinkan di sini, masing-masing memerlukan pendekatan pemrosesan sinyal yang berbeda - jadi saya membagikan apa yang telah saya gunakan:

Ini mungkin kontra-intuitif, tetapi sinyal otot ibu jari lebih baik terlihat di sisi berlawanan dari lengan, jadi salah satu sensor ditempatkan di sana, dan semuanya ditempatkan dekat dengan siku (otot memiliki sebagian besar tubuh mereka di area itu. , tetapi Anda ingin memeriksa di mana tepatnya milik Anda berada - ada perbedaan individu yang cukup besar)

4. Kode

Sebelum menjalankan program utama, Anda perlu mengetahui ID unit perangkat uECG khusus Anda (dilakukan dengan menghapus komentar pada baris 101 dan menyalakan perangkat satu per satu) dan mengisinya ke dalam unit_ids array (baris 37).

#include 
#include
#include
#include
#include
#include
#define SERVOMIN 150 // ini adalah jumlah panjang pulsa 'minimum' (dari 4096)
#define SERVOMAX 600 // ini adalah hitungan panjang pulsa 'maksimum' (dari 4096)
Adafruit_PWMServoDriver pwm =Adafruit_PWMServoDriver();
int rf_cen =9; //nRF24 chip mengaktifkan pin
int rf_cs =8; //nRF24 Pin CS
RF24 rf(rf_cen, rf_cs);
//alamat pipa - di-hardcode di sisi uECG
uint8_t pipe_rx[8] ={0x0E, 0xE6, 0x0D, 0xA7, 0 , 0, 0, 0};
uint8_t swapbits(uint8_t a){ //uECG alamat pipa menggunakan urutan bit yang ditukar
// membalik urutan bit dalam satu byte
uint8_t v =0;
jika(a &0x80) v |=0x01;
jika(a &0x40) v |=0x02;
jika(a &0x20) v |=0x04;
jika(a &0x10) v |=0x08;
jika(a &0x08) v |=0x10;
jika(a &0x04) v |=0x20;
jika(a &0x02 ) v |=0x40;
jika(a &0x01) v |=0x80;
kembali v;
}
long last_servo_upd =0; //waktu terakhir kali kita memperbarui nilai servo - tidak ingin melakukan ini terlalu sering
byte in_pack[32]; //array untuk paket RF yang masuk
unsigned long unit_ids[3] ={4294963881, 4294943100, 28358}; //array ID uECG yang diketahui - perlu diisi dengan ID unit Anda sendiri
int unit_vals[3] ={0, 0, 0}; //array nilai uECG dengan ID ini
float tgt_angles[5]; //sudut target untuk 5 jari
float cur_angles[5]; //sudut arus untuk 5 jari
float angle_open =30; //sudut yang sesuai dengan jari yang terbuka
float angle_closed =150; //angle yang sesuai dengan jari tertutup
void setup() {
//nRF24 membutuhkan SPI yang relatif lambat, mungkin akan bekerja pada 2MHz juga
SPI.begin();
SPI .setBitOrder(MSBFIRST);
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
untuk(int x =0; x <8; x++) //nRF24 dan uECG memiliki urutan bit yang berbeda untuk alamat pipa
pipe_rx[x] =swapbits(pipe_rx[x]);
//konfigurasi parameter 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(); //mendengarkan data uECG
//Perhatikan bahwa uECG harus dialihkan ke mode data mentah (melalui tekan tombol lama)
//untuk mengirim paket yang kompatibel, secara default ia mengirim data dalam mode BLE
//yang tidak dapat diterima oleh nRF24
Serial.begin(115200); //output serial - sangat berguna untuk debugging
pwm.begin(); //mulai driver PWM
pwm.setPWMreq(60); // Servo analog berjalan pada ~60 Hz update
for(int i =0; i <5; i++) //mengatur posisi jari awal
{
tgt_angles[i] =angle_open;
cur_angles[i] =angle_open;
}
}
void setAngle(int n, float angle){ //mengirimkan nilai sudut untuk saluran tertentu
pwm.setPWM (n, 0, SERVOMIN + sudut * 0,005556 * (SERVOMAX - SERVOMIN));
}
float angle_speed =15; //seberapa cepat jari akan bergerak
mengambang v0 =0, v1 =0, v2 =0; //nilai aktivitas otot yang difilter per 3 saluran
void loop()
{
if(rf.available())
{
rf.read(in_pack, 32 ); //memproses paket
byte u1 =in_pack[3];//ID unit 32-bit, unik untuk setiap perangkat 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); //batalkan komentar pada baris ini untuk membuat daftar ID uECG Anda
if(in_pack[7] !=32) id =0; //jenis paket salah:dalam mode EMG byte ini harus 32
int val =in_pack[10]; //nilai aktivitas otot
if(val !=in_pack[11]) id =0; //nilai digandakan dalam 2 byte karena noise RF dapat merusak paket, dan kami tidak memiliki CRC dengan nRF24
//temukan ID mana yang sesuai dengan ID saat ini dan isi nilai
for(int n =0; n <3; n++)
if(id ==unit_ids[n])
unit_vals[n] =val;
}
long ms =millis();
if(ms - last_servo_upd> 20) //jangan terlalu sering mengupdate servo
{
last_servo_upd =ms;
for(int n =0; n <5; n++) / /telusuri jari, jika target dan sudut saat ini tidak cocok - sesuaikan keduanya
{
if(cur_angles[n] if(cur_angles[n]> tgt_angles[n] + angle_speed/2) cur_angles[n] -=angle_speed;
}
untuk(int n =0; n <5; n++) //menerapkan sudut ke jari
setAngle(n, cur_angles[n]);
//rata-rata eksponensial:mencegah puncak tunggal memengaruhi status jari
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];
//menghitung skor s dari nilai mentah
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));
skor mengambang2 =4.0*v2*v2/((v0*1.2 + 20)*(v1*0.5 + 15));
//print skor untuk debugging
Serial.print(scor0);
Serial.print(' ');
Serial.print(scor1);
Serial.print(' ');
Serial.println(scor2);
//bandingkan setiap skor dengan ambang batas dan ubah status jari secara bersamaan
if(scor2 <0,5) //sinyal lemah - jari terbuka
tgt_angles[0] =angle_open;
if(scor2> 1.0) //sinyal kuat - tutup jari
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. Hasil

Dengan beberapa eksperimen yang memakan waktu sekitar 2 jam, saya bisa mendapatkan operasi yang cukup andal (video menunjukkan kasus biasa):

Ia berperilaku tidak sempurna dan dengan pemrosesan ini hanya dapat mengenali jari-jari yang terbuka dan tertutup (dan bahkan tidak masing-masing dari 5, ia hanya mendeteksi 3 kelompok otot:ibu jari, telunjuk dan jari tengah bersama-sama, jari manis dan jari kelingking bersama-sama). Tetapi "AI" yang menganalisis sinyal membutuhkan 3 baris kode di sini dan menggunakan satu nilai dari setiap saluran. Saya yakin lebih banyak yang bisa dilakukan dengan menganalisis gambar spektral 32-bin pada PC atau smartphone. Selain itu, versi ini hanya menggunakan 3 perangkat uECG (saluran EMG). Dengan lebih banyak saluran, seharusnya dimungkinkan untuk mengenali pola yang sangat rumit - tapi yah, itulah inti dari proyek ini, untuk memberikan beberapa titik awal bagi siapa saja yang tertarik :) Kontrol tangan jelas bukan satu-satunya aplikasi untuk sistem seperti itu.

Kode

  • emg_hand_control2.ino
emg_hand_control2.inoArduino
#include #include #include #include #include #include #define SERVOMIN 150 / / ini adalah jumlah panjang pulsa 'minimum' (dari 4096)#define SERVOMAX 600 // ini adalah jumlah panjang pulsa 'maksimum' (dari 4096)Adafruit_PWMServoDriver pwm =Adafruit_PWMServoDriver();int rf_cen =9; //nRF24 chip mengaktifkan pinint rf_cs =8; //nRF24 CS pinRF24 rf(rf_cen, rf_cs);//alamat pipa - di-hardcode pada uECG sideuint8_t pipe_rx[8] ={0x0E, 0xE6, 0x0D, 0xA7, 0, 0, 0, 0};uint8_t swapbits(uint8_t a) { // alamat pipa uECG menggunakan urutan bit yang ditukar // membalik urutan bit dalam satu byte uint8_t v =0; jika(a &0x80) v |=0x01; jika(a &0x40) v |=0x02; jika(a &0x20) v |=0x04; jika(a &0x10) v |=0x08; jika(a &0x08) v |=0x10; jika(a &0x04) v |=0x20; jika(a &0x02) v |=0x40; jika(a &0x01) v |=0x80; kembali v;}panjang last_servo_upd =0; //waktu terakhir kali kita memperbarui nilai servo - tidak ingin melakukan ini terlalu seringbyte in_pack[32]; //array untuk paket RF yang masuk unsigned long unit_ids[3] ={4294963881, 4294943100, 28358}; //array ID uECG yang diketahui - perlu diisi dengan ID unit Anda sendirisint unit_vals[3] ={0, 0, 0}; //array nilai uECG dengan IDsfloat tgt_angles[5] ini; //sudut target untuk 5 jarifloat cur_angles[5]; //sudut arus untuk 5 jarifloat angle_open =30; //angle yang sesuai dengan open fingerfloat angle_closed =150; //angle yang sesuai dengan setup fingervoid tertutup() { //nRF24 membutuhkan SPI yang relatif lambat, mungkin akan bekerja pada 2MHz juga SPI.begin(); SPI.setBitOrder(MSBFIRST); SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0)); for(int x =0; x <8; x++) //nRF24 dan uECG memiliki urutan bit yang berbeda untuk alamat pipa pipe_rx[x] =swapbits(pipe_rx[x]); //konfigurasi parameter 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(); //mendengarkan data uECG //Perhatikan bahwa uECG harus dialihkan ke mode data mentah (melalui tekan tombol lama) //untuk mengirim paket yang kompatibel, secara default ia mengirim data dalam mode BLE //yang tidak dapat diterima oleh nRF24 Serial .begin(115200); //keluaran serial - sangat berguna untuk men-debug pwm.begin(); //mulai driver PWM pwm.setPWMreq(60); // Servo analog berjalan pada ~60 Hz update untuk(int i =0; i <5; i++) //mengatur posisi jari awal { tgt_angles[i] =angle_open; cur_angles[i] =angle_open; }}void setAngle(int n, float angle){ //mengirim nilai sudut untuk saluran yang diberikan pwm.setPWM(n, 0, SERVOMIN + angle * 0,005556 * (SERVOMAX - SERVOMIN));}float angle_speed =15; //seberapa cepat jari akan bergerakfloat v0 =0, v1 =0, v2 =0; //nilai aktivitas otot yang difilter per 3 saluranvoid loop() { if(rf.available()) { rf.read(in_pack, 32); //memproses byte paket u1 =in_pack[3];//ID unit 32-bit, unik untuk setiap perangkat uECG byte u2 =in_pack[4]; byte u3 =in_pack[5]; byte u4 =in_pack[6]; id panjang yang tidak ditandatangani =(u1<<24) | (u2<<16) | (u3<<8) | u4; //Serial.println(id); //batalkan komentar pada baris ini untuk membuat daftar ID uECG Anda if(in_pack[7] !=32) id =0; //jenis paket salah:dalam mode EMG byte ini harus 32 int val =in_pack[10]; //nilai aktivitas otot if(val !=in_pack[11]) id =0; //nilai digandakan dalam 2 byte karena noise RF dapat merusak paket, dan kami tidak memiliki CRC dengan nRF24 //temukan ID mana yang sesuai dengan ID saat ini dan isi nilai untuk(int n =0; n <3; n++) if (id ==unit_ids[n]) unit_vals[n] =val; } panjang ms =milis(); if(ms - last_servo_upd> 20) //jangan terlalu sering mengupdate servo { last_servo_upd =ms; for(int n =0; n <5; n++) //menelusuri jari, jika target dan sudut saat ini tidak cocok - sesuaikan mereka { if(cur_angles[n]  tgt_angles[n] + angle_speed/2) cur_angles[n] -=angle_speed; } for(int n =0; n <5; n++) //menerapkan sudut ke jari setAngle(n, cur_angles[n]); //rata-rata eksponensial:mencegah puncak tunggal mempengaruhi keadaan jari 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]; //menghitung skor dari nilai mentah float scor0 =4.0*v0*v0/((v1*0.3 + 20)*(v2*1.3 + 15)); float skor1 =4.0*v1*v1/((v0*2.0 + 20)*(v2*2.0 + 20)); float skor2 =4.0*v2*v2/((v0*1.2 + 20)*(v1*0.5 + 15)); //cetak skor untuk debugging Serial.print(scor0); Serial.print(' '); Serial.print(scor1); Serial.print(' '); Serial.println(scor2); //bandingkan setiap skor dengan ambang batas dan ubah status jari sesuai jika(skor2 <0,5) //sinyal lemah - jari terbuka tgt_angles[0] =angle_open; if(scor2> 1.0) //sinyal kuat - tutup jari 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] =sudut_tertutup; } }}

Skema

nrf24_hand_control_5jcEeCP8a3.fzz

Proses manufaktur

  1. Pil KB
  2. Membuat Kendaraan Robot Nirkabel Menggunakan Sensor IR
  3. Desain referensi menyederhanakan kontrol motor robot industri
  4. Perangkat berbasis suhu Sistem kontrol menggunakan LM35
  5. Menggunakan AI Untuk Mengontrol Properti Cahaya | Generasi Supercontinuum
  6. Menggunakan Perangkat Lunak Simulasi Robot 3DG untuk Merencanakan Otomasi Robot
  7. Kontrol Kereta Otomatis
  8. Kontrol Jarak Jauh Universal menggunakan Arduino, 1Sheeld, dan Android
  9. Menggunakan IoT untuk Mengontrol Lengan Robot dari Jarak Jauh
  10. Siswa membangun sistem pemilah sampah robot menggunakan teknologi B&R