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

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:
- nRF24 Pin 1 (GND) → Arduino GND
- nRF24 Pin 2 (Vcc) → Arduino 3,3 V
- nRF24 Pin 3 (CE) → Arduino D9
- nRF24 Pin 4 (CSN) → Arduino D8
- nRF24 Pin 5 (SCK) → Arduino D13
- nRF24 Pin 6 (MOSI) → Arduino D11
- nRF24 Pin 7 (MISO) → Arduino D12
- PCA9685 SDA → Arduino A4
- PCA9685 SCL → Arduino A5
- PCA9685 Vcc → Arduino 5 V
- PCA9685 GND → Arduino GND
- PCA9685 V+ → Hochstrom-5V-Versorgung
- PCA9685 GND → Hochstrom-GND
- Finger-Servos: PCA Kanäle 0–4 (Daumen=0, Zeigefinger=1, Mittelfinger=2, Ringfinger=3, Kleiner Finger=4)
3. EMG-Sensor-Platzierung

Für brauchbare Messwerte ist die Platzierung der uECG-Geräte entscheidend. Viele Optionen sind möglich, jede erfordert angepasste Signalverarbeitung. Meine Platzierung:
- Ein Sensor auf der Rückseite des Unterarms (gegenüber dem Daumenballen) – Daumenmuskel-Signal ist dort besser sichtbar
- Alle Sensoren nah am Ellenbogen, da Muskelbäuche dort liegen
- Individuelle Anpassung nötig – Muskelpositionen variieren stark
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
- Antibabypille: Geschichte, Nutzen, Risiken und Produktion
- Drahtloses IR-Sensor-gesteuertes Roboterfahrzeug bauen
- Trinamic & Maxim Integrated veröffentlichen Referenzdesign zur Beschleunigung von End-of-Arm Tooling in Industrierobotik
- LM35-basiertes Temperatursteuerungssystem mit AT89S52 Mikrocontroller
- KI-gesteuerte Photonic Chips formen Breitbandlicht für Smart Optics
- Robotikautomatisierung straffen mit 3DGs fortschrittlicher Robotersimulationssoftware
- DIY Automatische Modellbahn-Steuerung mit Arduino – Einfach & Flexibel
- Universelle Android-Fernbedienung mit Arduino, 1Sheeld & IR-Steuerung
- Fernsteuerung eines Roboterarms per IoT: Eigenbau mit Arduino MKR1000
- Studierende bauen fortschrittliches B&R-gestütztes Robotik-Mülltrennungssystem