Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Manufacturing Technology >> Herstellungsprozess

Präzise EMG-gesteuerte Roboterhand: InMoov-Integration & Bauanleitung

Komponenten und Materialien

Komponente Menge
uECG-Gerät (für EMG-Messung) 3
InMoov-Hand (Open-Source-Roboterhand) 1
Arduino Nano R3 1
Adafruit PCA9685 16-Kanal PWM-Treiber 1
nRF24L01+ Modul (Generic) 1

Projektbeschreibung

Unser Team arbeitet seit längerem an Roboterhänden. Zunächst versuchten wir, eine zuverlässige Prothesenhand zu entwickeln – für dieses Projekt nutze ich jedoch eine bewährte Open-Source-Hand: die InMoov. Der mechanische Zusammenbau ist auf der Projektseite ausführlich dokumentiert und recht komplex. Hier konzentriere ich mich auf die Steuerung, die komplett neu ist.

Zur technologischen Weiterentwicklung siehe auch: „Seeing Muscles at Work – 8-Kanal-EMG mit LEDs“.

1. Signalverarbeitung

Die Steuerung basiert auf EMG (elektrische Muskelaktivität). Das EMG-Signal wird von drei uECG-Geräten erfasst (ursprünglich für EKG gedacht, aber dank generischem ADC für beliebige Biosignale geeignet). Für EMG gibt es einen speziellen Modus: Das Gerät sendet 32-Bin-Spektraldaten und einen „Muskel-Fenster“-Mittelwert (mittlere spektrale Intensität zwischen 75 und 440 Hz).

Spektralbilder zeigen Frequenz (vertikal, 0–488 Hz, ~15 Hz Schritte) über Zeit (horizontal, ~10 s). Intensität farbcodiert: blau=niedrig, grün=mittel, gelb=hoch, rot=sehr hoch.

Für zuverlässige Gestenerkennung wäre PC-seitige Verarbeitung der Spektralbilder nötig. Für einfache Fingeraktivierung reicht der gemittelte Wert der 3 Kanäle, den uECG komfortabel in bestimmten Paket-Bytes bereitstellt. Diese Rohwerte sind einfacher zu interpretieren.

Entmischung der Signale: Signale der Muskelgruppen sind „vermischt“. Um sie zu trennen, nutze ich eine Formel:

S0 = V0² / ((V1 × a0 + b0) × (V2 × c0 + d0))

Dabei sind S0 der Score für Kanal 0, V0–V2 die Rohwerte der Kanäle 0–2, a/b/c/d manuell angepasste Koeffizienten (a,c: 0,3–2,0; b,d: 15,20 – an eigene Sensorplatzierung anpassen). Entsprechende Scores für Kanal 1 und 2. Nach Entmischung sind die Signale klar getrennt und lassen sich per Schwellenwertvergleich in Servobewegungen übersetzen.

2. Schaltplan

Präzise EMG-gesteuerte Roboterhand: InMoov-Integration & Bauanleitung

Der Schaltplan ist simpel: nRF24-Modul, PCA9685 (oder ähnlicher I2C-PWM-Controller), leistungsstarke 5 V Versorgung (mind. 5 A für stabilen Betrieb aller Servos gleichzeitig).

Verbindungen:

3. EMG-Sensor-Platzierung

Präzise EMG-gesteuerte Roboterhand: InMoov-Integration & Bauanleitung

Für brauchbare Messwerte ist die Platzierung der uECG-Geräte entscheidend. Viele Optionen sind möglich, jede erfordert angepasste Signalverarbeitung. Meine Platzierung:

4. Code

Vor dem Hauptprogramm müssen die Unit-IDs der uECG-Geräte ermittelt werden (Zeile 101 auskommentieren, Geräte einzeln einschalten) und in das unit_ids-Array (Zeile 37) eingetragen werden.

#include <SPI.h>
#include <RF24.h>
#include <RF24_config.h>
#include <nRF24L01.h>
#include <Adafruit_PWMServoDriver.h>
#include <Wire.h>

#define SERVOMIN 150
#define SERVOMAX 600

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
int rf_cen = 9;
int rf_cs = 8;
RF24 rf(rf_cen, rf_cs);

uint8_t pipe_rx[8] = {0x0E, 0xE6, 0x0D, 0xA7, 0, 0, 0, 0};

uint8_t swapbits(uint8_t a) {
  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;
byte in_pack[32];
unsigned long unit_ids[3] = {4294963881, 4294943100, 28358}; // Eigene IDs eintragen!
int unit_vals[3] = {0, 0, 0};
float tgt_angles[5];
float cur_angles[5];
float angle_open = 30;
float angle_closed = 150;

void setup() {
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
  for (int x = 0; x < 8; x++) pipe_rx[x] = swapbits(pipe_rx[x]);

  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();

  Serial.begin(115200);
  pwm.begin();
  pwm.setPWMFreq(60);
  for (int i = 0; i < 5; i++) {
    tgt_angles[i] = angle_open;
    cur_angles[i] = angle_open;
  }
}

void setAngle(int n, float angle) {
  pwm.setPWM(n, 0, SERVOMIN + angle * 0.005556 * (SERVOMAX - SERVOMIN));
}

float angle_speed = 15;
float v0 = 0, v1 = 0, v2 = 0;

void loop() {
  if (rf.available()) {
    rf.read(in_pack, 32);
    byte u1 = in_pack[3], u2 = in_pack[4], u3 = in_pack[5], u4 = in_pack[6];
    unsigned long id = (u1 << 24) | (u2 << 16) | (u3 << 8) | u4;
    // Serial.println(id); // Zum Auslesen eigener IDs auskommentieren
    if (in_pack[7] != 32) id = 0;
    int val = in_pack[10];
    if (val != in_pack[11]) id = 0;
    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) {
    last_servo_upd = ms;
    for (int n = 0; n < 5; n++) {
      if (cur_angles[n] < tgt_angles[n] - angle_speed/2) cur_angles[n] += angle_speed;
      if (cur_angles[n] > tgt_angles[n] + angle_speed/2) cur_angles[n] -= angle_speed;
    }
    for (int n = 0; n < 5; n++) setAngle(n, cur_angles[n]);

    // Exponentielle Glättung
    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-Berechnung
    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));

    Serial.print(scor0); Serial.print(' '); Serial.print(scor1); Serial.print(' '); Serial.println(scor2);

    // Schwellenwert-Logik
    if (scor2 < 0.5) tgt_angles[0] = angle_open;
    if (scor2 > 1.0) 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. Ergebnisse

Nach ca. 2 Stunden Experimentieren erreichte ich recht zuverlässigen Betrieb (Video zeigt typischen Fall). Das System erkennt geöffnete/geschlossene Finger, allerdings nur 3 Muskelgruppen: Daumen, Zeige+Mittelfinger zusammen, Ring+Kleiner Finger zusammen. Die „KI“ zur Signalanalyse besteht hier aus 3 Codezeilen und nutzt je Kanal einen Einzelwert. Durch Analyse der 32-Bin-Spektralbilder auf PC/Smartphone wäre weit mehr möglich. Mit mehr EMG-Kanälen ließen sich komplexe Muster erkennen – aber genau das ist der Projektzweck: Einstiegspunkt für Interessierte bieten. Handsteuerung ist keineswegs die einzige Anwendung.

Schaltplan-Datei

nrf24_hand_control_5jcEeCP8a3.fzz (Fritzing-Datei)

Herstellungsprozess

  1. Antibabypille: Geschichte, Nutzen, Risiken und Produktion
  2. Drahtloses IR-Sensor-gesteuertes Roboterfahrzeug bauen
  3. Trinamic & Maxim Integrated veröffentlichen Referenzdesign zur Beschleunigung von End-of-Arm Tooling in Industrierobotik
  4. LM35-basiertes Temperatursteuerungssystem mit AT89S52 Mikrocontroller
  5. KI-gesteuerte Photonic Chips formen Breitbandlicht für Smart Optics
  6. Robotikautomatisierung straffen mit 3DGs fortschrittlicher Robotersimulationssoftware
  7. DIY Automatische Modellbahn-Steuerung mit Arduino – Einfach & Flexibel
  8. Universelle Android-Fernbedienung mit Arduino, 1Sheeld & IR-Steuerung
  9. Fernsteuerung eines Roboterarms per IoT: Eigenbau mit Arduino MKR1000
  10. Studierende bauen fortschrittliches B&R-gestütztes Robotik-Mülltrennungssystem