Arduino Roboterarm bauen: Servoauswahl, Drehmoment, Bluetooth
Arduino Roboterarm bauen: Rahmen aus MDF oder 3D-Druck, Drehmoment für MG996R berechnen, Poti- und Joystick-Steuerung, Aufnahme, PCA9685 und Bluetooth.
Ein Arduino Roboterarm ist ein Roboter im Schreibtischformat, der dank seiner servogetriebenen Gelenke einen Gegenstand greifen, anheben, schwenken und an einer anderen Stelle ablegen kann. Mit Basis, Schulter, Ellbogen, Handgelenk und Greifer lernst du in einem einzigen Projekt Mechanik, Motorauswahl, Stromversorgung und Programmierung. Und das Beste: Mit einem Arduino Uno, ein paar Servos und Teilen aus MDF oder dem 3D-Drucker baust du ihn zu Hause und lässt eine kleine Kopie eines Industrieroboters auf deinem Schreibtisch arbeiten.
In dieser Anleitung gehen wir Roboterarme mit 4 und 6 Achsen von Anfang bis Ende durch: die Wahl des Rahmenmaterials, eine Drehmomentrechnung für die Entscheidung zwischen SG90, MG90S und MG996R, die externe 5-V-Versorgung, die Steuerung mit Potis und Joysticks, das Aufnehmen und Abspielen von Bewegungen, den 16-Kanal-Servotreiber PCA9685, die Bluetooth-Steuerung per Handy und die Lösungen für die zwei häufigsten Probleme: Zittern und Resets. Wenn du nicht sicher bist, wie ein Servo funktioniert, lies zuerst Was ist ein Servomotor?.



Wie funktioniert ein Roboterarm und wie viele Achsen brauchst du?
Stell dir einen Roboterarm wie einen menschlichen Arm vor. In jedem Gelenk sitzt ein Servomotor, und der Arduino sagt jedem Servo: „Fahr auf diesen Winkel“. Dank des eingebauten Potentiometers weiß das Servo, wo seine Welle steht, und hält den Winkel auch unter Last. Indem du die Gelenkwinkel gemeinsam änderst, bringst du den Greifer an jede gewünschte Stelle. Die Zahl der unabhängigen Bewegungen eines Arms heißt Freiheitsgrade (DOF), umgangssprachlich einfach Achsen.
| Gelenk | Bewegung | Belastung | Empfohlenes Servo |
|---|---|---|---|
| Basis (Drehung) | Schwenkt den Arm nach links und rechts | Hebt nichts, trägt aber den ganzen Arm | MG996R + Kugellager |
| Schulter | Hebt den Arm nach vorn und hinten | Am stärksten belastetes Gelenk | MG996R oder Servo der 20-kg·cm-Klasse |
| Ellbogen | Beugt den Unterarm | Mittel bis hoch | MG996R |
| Handgelenk (auf und ab) | Stellt den Winkel des Greifers ein | Niedrig | MG90S |
| Handgelenk-Drehung | Dreht den Greifer um seine Achse | Sehr niedrig | MG90S oder SG90 |
| Greifer | Greift und lässt den Gegenstand los | Klemmkraft | SG90 oder MG90S |
Die meisten „6-Achs-Roboterarm“-Bausätze haben 6 Servos; da der Greifer nicht als Achse zählt, sind das technisch gesehen ein Arm mit 5 Freiheitsgraden plus Greifer. Für deinen ersten Arm ist ein Design mit 4 Achsen (Basis, Schulter, Ellbogen, Greifer) der beste Einstieg: 4 Servos, 4 Potis und ein Arduino Uno reichen, die Verkabelung bleibt übersichtlich und Drehmomentprobleme sind selten. Handgelenke helfen, wenn der Greifer waagerecht bleiben oder Gegenstände aus verschiedenen Winkeln greifen soll; ab 5–6 Servos ist ein PCA9685-Treiber fast unverzichtbar.
Materialliste
| Teil | Arm mit 4 Achsen | Arm mit 6 Servos | Hinweis |
|---|---|---|---|
| Steuerung | Arduino Uno oder Nano | Arduino Uno, Nano oder ESP32 | Die zusätzlichen Eingänge A6 und A7 des Nano erlauben mehr Potis |
| Servomotoren | 4× SG90 oder MG90S | 3× MG996R + 3× MG90S | Prüfe deine Wahl mit der Drehmomentrechnung unten |
| Servotreiber | Nicht nötig | PCA9685-Modul mit 16 Kanälen | Nur 2 Signalleitungen über I2C |
| Netzteil | 5-V-Netzteil mit 3 A | 5–6 V, mindestens 5 A | Servos nie aus dem Arduino versorgen |
| Kondensator | 470–1000 µF, 16 V | 1000–2200 µF, 16 V | An der Servoversorgung, auf die Polung achten |
| Bedienelemente | 4× Poti 10 kΩ | 2× Joystick-Modul oder HC-05 | Du kannst auch beides kombinieren |
| Taster | 2 Stück (Aufnahme, Abspielen) | Die Joystick-Taster reichen | Mit INPUT_PULLUP kein Widerstand nötig |
| Mechanik | Teile aus 3 mm MDF oder 3D-Druck | 4 mm MDF, 3D-Druck oder Aluminium-Halterungen | Kugellager 608, M3-Schrauben und Sicherungsmuttern |
Mechanik: MDF oder 3D-Druck?
| Material | Vorteile | Nachteile | Geeignet für |
|---|---|---|---|
| 3–4 mm MDF (gelasert) | Günstig, leicht, leicht zu schrauben und zu kleben | Feuchtigkeitsempfindlich, Schraublöcher weiten sich mit der Zeit | Erste Arme im MeArm-Stil, Schulprojekte |
| 3 mm Acryl | Sieht edel aus, unempfindlich gegen Feuchtigkeit | Spröde; kann beim Anziehen von Schrauben reißen | Ausstellungs- und Vitrinenprojekte |
| 3D-Druck (PLA / PETG) | Komplexe Teile, passgenaue Servoaufnahmen | Lange Druckzeit; PLA verformt sich bei Wärme und Dauerlast | Eigene Konstruktionen; PETG für tragende Teile |
| Aluminium-Bausatz | Sehr stabil, fertige Teile passend zum MG996R | Schwer und teuer, belastet die Schulter stärker | Arme mit 6 Servos für schwerere Gegenstände |
| Karton, Eisstiele | Fast kostenlos, schneller Prototyp | Nicht haltbar, Gelenke bekommen Spiel | Maße testen, erste Versuche mit Kindern |
- Halte den Arm kurz und leicht: Die Last an der Schulter wächst proportional zum Abstand. Ein Arm mit 24 statt 30 cm senkt das Schultermoment um etwa ein Fünftel.
- Setz schwere Servos nah an die Basis: Das Ellbogenservo auf Schulterhöhe zu verlegen und den Ellbogen über eine Schubstange (Parallelgestänge) anzutreiben, ist ein cleverer Trick von Armen wie dem MeArm; die Schulter muss dieses Servo dann nicht mehr anheben.
- Nutze ein Lager in der Basis: Häng nicht das ganze Gewicht des Arms an die Welle des Basisservos. Ein Kugellager 608 (8 mm Innen-, 22 mm Außendurchmesser, 7 mm breit; das klassische Skateboardlager) oder ein Drehtellerlager trägt die Last, das Servo dreht nur.
- Lagere Gelenke beidseitig: Während das Servo das Gelenk auf der Hornseite hält, setz auf der Gegenseite einen Drehpunkt aus M3-Schraube und Sicherungsmutter. So nimmt die Servowelle keine Biegelast auf und das Getriebe hält viel länger.
- Verwende Metallhörner: Montiere auf jedes MG996R ein rundes Metallhorn mit 25 Zähnen (25T) und sichere es mit der Mittelschraube auf der Welle. Kunststoffhörner rutschen an stark belasteten Gelenken durch und verlieren ihre Verzahnung.
- Gib den Greiferfingern Gummi: Ein dünner Gummistreifen auf den Fingerspitzen hält Gegenstände rutschfest und mit weniger Klemmkraft.
Servoauswahl: SG90, MG90S oder MG996R?
| Modell | Drehmoment | Geschwindigkeit (pro 60°) | Gewicht | Getriebe | Blockierstrom | Einsatz im Roboterarm |
|---|---|---|---|---|---|---|
| SG90 | 1,8 kg·cm (4,8 V) | 0,10 s (4,8 V) | 9 g | Kunststoff | Etwa 0,6–0,75 A | Greifer, leichtes Handgelenk, kleiner Arm mit 4 Achsen |
| MG90S | 1,8 kg·cm (4,8 V), 2,2 kg·cm (6 V) | 0,10 s (4,8 V) | 13,4 g | Metall | Etwa 0,7–1 A | Handgelenk, Handgelenk-Drehung, Greifer |
| MG996R | 9,4 kg·cm (4,8 V), 11 kg·cm (6 V) | 0,17 s (4,8 V) | 55 g | Metall | 2,5 A (6 V) | Basis, Schulter, Ellbogen |
| DS3218 | 19 kg·cm (5 V), 21,5 kg·cm (6,8 V) | 0,16 s (5 V) | 60 g | Metall | Etwa 2–3 A | Schulter langer oder schwerer Arme |
Der Drehmomentwert in der Tabelle ist das Blockiermoment (Stall Torque): die größte Kraft, die das Servo aufbringt, bevor seine Welle stehen bleibt, kein Wert für den Dauerbetrieb. Je näher ein Servo dieser Grenze kommt, desto mehr steigt sein Strom Richtung Blockierstrom, es wird heiß und beginnt zu zittern. Die Faustregel: Das Drehmoment, das ein Gelenk braucht, sollte höchstens die Hälfte des Blockiermoments betragen. Auch die Spannung zählt: Das MG996R liefert bei 4,8 V 9,4 kg·cm und bei 6 V 11 kg·cm, dasselbe Servo wird also allein durch die Versorgungsspannung etwa 17 % stärker. Darum sind 6 V bei Armen mit MG996R sinnvoll, für SG90 und MG90S sind 6 V aber die Obergrenze. Anschluss und Grenzen des MG996R findest du auch im Beitrag MG996R Servomotor.

Drehmoment berechnen: Schafft das Schulterservo diesen Arm?
Das Drehmoment, das ein Servo stemmen muss, ist die Summe aus Masse jedes Teils mal waagerechtem Abstand zur Gelenkachse: Drehmoment (kg·cm) = Σ Masse (kg) × Abstand (cm). Der ungünstigste Fall ist der waagerecht ausgestreckte Arm, weil dann alle Abstände maximal sind; zeigt der Arm senkrecht nach oben, sinkt dieser Hebelarm fast auf null. Zur Umrechnung in Newtonmeter multiplizierst du mit 0,098 (1 kg·cm ≈ 0,098 N·m). Als Beispiel berechnen wir die Schulterlast eines Tischarms mit 12 cm von Schulter bis Ellbogen, 12 cm von Ellbogen bis Handgelenk, 6 cm vom Handgelenk bis zur Greiferspitze und einem MG996R im Ellbogen; der Greifer hält einen Gegenstand von 50 g:
| Teil | Masse | Abstand zur Schulterachse | Drehmoment |
|---|---|---|---|
| Oberarmteile (MDF und Schrauben) | 40 g | 6 cm (Schwerpunkt) | 0,04 × 6 = 0,24 kg·cm |
| Ellbogenservo (MG996R) | 55 g | 12 cm | 0,055 × 12 = 0,66 kg·cm |
| Unterarmteile | 30 g | 18 cm | 0,03 × 18 = 0,54 kg·cm |
| Handgelenkservo + Greifer (MG90S, SG90 und Teile) | 50 g | 25 cm | 0,05 × 25 = 1,25 kg·cm |
| Gehaltener Gegenstand | 50 g | 30 cm | 0,05 × 30 = 1,50 kg·cm |
| Summe | 225 g | — | 4,19 kg·cm (≈ 0,41 N·m) |
Im Stillstand braucht dieser Arm an der Schulter 4,19 kg·cm. Um die Zusatzlast beim Beschleunigen und Abbremsen abzudecken und das Servo weit weg vom Blockieren zu halten, multipliziere mit 2: 8,4 kg·cm. Die Schulter braucht also mindestens ein MG996R (9,4 kg·cm bei 4,8 V, 11 kg·cm bei 6 V); die 1,8–2,2 kg·cm eines SG90 oder MG90S bewegen diesen Arm keinen Millimeter. Rechnest du dasselbe für den Ellbogen, ergeben Unterarm (0,03 × 6), Handgelenk mit Greifer (0,05 × 13) und Gegenstand (0,05 × 18) zusammen 1,73 kg·cm, mit Sicherheitsfaktor 3,5 kg·cm; auch der Ellbogen braucht ein MG996R. Am Handgelenk bleiben nur Greifer und Gegenstand, die Last liegt bei etwa 0,4 kg·cm und ein MG90S reicht locker. Dieselbe Rechnung verrät dir auch den schwersten Gegenstand, den der Arm tragen kann: Erhöhe die Masse, rechne die Summe neu und bleib unter dem halben Servomoment.
Stromversorgung: Warum Servos nicht am Arduino hängen dürfen
Die meisten Probleme mit Roboterarmen kommen von der Stromversorgung. Ein MG996R zieht in Bewegung 0,5–0,9 A und blockiert bis zu 2,5 A; selbst ein kleines SG90 kann blockiert über 0,6 A ziehen. Hängt ein Arduino Uno am USB, stehen am 5V-Pin insgesamt etwa 500 mA zur Verfügung, die USB-Grenze, und einen Teil davon braucht das Board selbst. Fahren drei große Servos gleichzeitig an, bricht die Spannung ein, der Mikrocontroller startet neu und der Arm springt in die Startposition. Die Lösung: Versorge die Servos aus einer eigenen 5–6-V-Quelle und verbinde alle GNDs miteinander; den Arduino kannst du weiter über USB oder sein eigenes Netzteil betreiben. Versorgungsoptionen erklärt der Beitrag Arduino Stromversorgung, und wie lange ein Arm mit Akku durchhält, schätzt du mit dem Akkulaufzeit-Rechner.
| Armtyp | Geschätzter Strom | Empfohlene Quelle |
|---|---|---|
| Kleiner Arm mit 4× SG90 oder MG90S | 0,5–1 A in Bewegung, 2–3 A Spitze | 5-V-Netzteil mit 3 A oder LM2596-Modul (max. 3 A, ohne Kühlkörper etwa 2 A) |
| Arm mit 6 Servos: 3× MG996R + 3× MG90S | 2–3 A in Bewegung, 8–10 A wenn alle blockieren | Schaltnetzteil 5–6 V mit mindestens 5 A (10 A für Reserve) |
| Mobiler Arm | Je nach Arm | 2S-LiPo (7,4 V) + Step-down-Modul (UBEC) mit mindestens 5 A, auf 6 V eingestellt |
| Ungeeignete Quellen | — | 9-V-Block (liefert zu wenig Strom), der 5V-Pin des Arduino, 4 AA-Alkalibatterien bei einem Arm mit MG996R |
Schaltung für den Arm mit 4 Achsen
Die Tabelle zeigt die Verdrahtung des Poti-gesteuerten Arms mit 4 Achsen für Arduino Uno und Raspberry Pi Pico; die Codes in diesem Beitrag nutzen genau diese Pins. Am Uno kann die Servo-Bibliothek ein Servo an jedem Digitalpin betreiben, sie belegt aber Timer1, deshalb funktioniert analogWrite an den Pins 9 und 10 nicht, solange Servos angeschlossen sind. Die Pinbelegung deines Boards siehst du im Werkzeug Pinout.
| Verbindung | Arduino Uno | Raspberry Pi Pico |
|---|---|---|
| Signal Basisservo (orange Leitung) | D3 | GP10 |
| Signal Schulterservo | D5 | GP11 |
| Signal Ellbogenservo | D6 | GP12 |
| Signal Greiferservo | D9 | GP13 |
| Poti 1–3 Schleifer (Basis, Schulter, Ellbogen) | A0, A1, A2 | GP26, GP27, GP28 |
| Poti 4 Schleifer (Greifer) | A3 | Keins; Greifer über Taster an GP14 |
| Äußere Poti-Anschlüsse | 5V und GND | 3V3 und GND |
| Taster Aufnahme und Abspielen (andere Seite an GND) | D7 und D8 | — |
| Servo rote Leitung (+) | Externes 5–6 V Plus | Externes 5–6 V Plus |
| Servo braune Leitung (−) | Minus der externen Quelle + Arduino GND | Minus der externen Quelle + Pico GND |

Vor dem Zusammenbau kalibrieren: Servos zentrieren
- Lade den Code unten hoch, bevor du die Hörner montierst; alle vier Servos fahren nacheinander auf 90 Grad und bleiben dort stehen.
- Halte den Arm in einer neutralen Pose (zum Beispiel Oberarm senkrecht, Unterarm waagerecht, Greifer halb offen), steck jedes Horn auf den Zahn, der dieser Pose am nächsten kommt, und sichere es mit der Mittelschraube.
- Setz im Code CENTER_ONLY auf false, wähle mit TEST ein Gelenk und dreh das Poti an A0 langsam.
- Beobachte den Winkel im seriellen Monitor und notiere den kleinsten und größten Winkel, den das Gelenk erreicht, ohne anzustoßen oder Kabel zu spannen.
- Leg den geschlossenen Winkel des Greifers auf den Punkt, an dem sich die Finger gerade berühren; ein Servo, das weiter zudrücken will, bleibt blockiert und wird heiß.
- Trag die ermittelten Werte in die Arrays MIN_A und MAX_A der folgenden Sketche ein.
1#include <Servo.h>
2
3const byte SERVO_PIN[4] = {3, 5, 6, 9}; // taban, omuz, dirsek, kıskaç / base, shoulder, elbow, gripper
4const byte TEST = 1; // denenecek servo (0–3) / servo under test (0–3)
5const bool CENTER_ONLY = true; // true: hepsi 90°'de bekler / true: all wait at 90°
6
7Servo servo[4];
8
9void setup() {
10 Serial.begin(9600);
11 for (byte i = 0; i < 4; i++) {
12 servo[i].write(90); // önce açıyı ver / set the angle first
13 servo[i].attach(SERVO_PIN[i]); // sonra bağla: zıplamaz / then attach: no jump
14 delay(300); // akım darbelerini ayır / spread the current surges
15 }
16}
17
18void loop() {
19 if (CENTER_ONLY) return; // korna takarken burada kal / stay here while fitting horns
20 int angle = map(analogRead(A0), 0, 1023, 0, 180); // A0'daki pot / pot on A0
21 servo[TEST].write(angle);
22 Serial.print(F("Servo "));
23 Serial.print(TEST);
24 Serial.print(F(" aci / angle: "));
25 Serial.println(angle); // sınırları buradan oku / read the limits here
26 delay(50);
27}Ein kleines, aber wichtiges Detail im Code: Weil servo.write(90) vor attach() aufgerufen wird, bekommt das Servo schon mit dem ersten Impuls den gewünschten Winkel. Drehst du die Reihenfolge um, schickt die Bibliothek zuerst ihren Standardimpuls von 1500 µs (etwa 90°); ist dein Startwinkel nicht 90, springt der Arm beim Einschalten erst in die Mitte und dann zum Ziel. Die Servos einzeln mit 300 ms Abstand zu starten, verteilt außerdem den Einschaltstromstoß.
Roboterarm mit Potis steuern
Die intuitivste Steuerung ist ein Potentiometer pro Gelenk: Wo die Potiachse steht, dorthin fährt auch das Gelenk, du machst also eine Positionsregelung. Baust du die Potis in ein kleines Modell des Arms ein (Karton oder 3D-Druck in gleichen Proportionen), kopiert der große Arm die Bewegung eins zu eins, wenn du das Modell von Hand bewegst. Analoge Messwerte schwanken allerdings naturgemäß um ein paar Stufen, und dieses Schwanken zeigt sich am Servo als Zittern; dagegen hat der Code zwei Maßnahmen.
1#include <Servo.h>
2
3const byte SERVO_PIN[4] = {3, 5, 6, 9}; // taban, omuz, dirsek, kıskaç / base, shoulder, elbow, gripper
4const byte POT_PIN[4] = {A0, A1, A2, A3}; // her eklem için bir pot / one pot per joint
5const byte MIN_A[4] = {0, 15, 20, 40}; // kalibrasyonda bulduğun alt sınırlar / lower limits from calibration
6const byte MAX_A[4] = {180, 165, 160, 110}; // üst sınırlar / upper limits
7
8Servo servo[4];
9float smooth[4]; // süzülmüş pot değeri / filtered pot value
10int lastAngle[4]; // son yazılan açı / last written angle
11
12void setup() {
13 for (byte i = 0; i < 4; i++) {
14 smooth[i] = analogRead(POT_PIN[i]);
15 lastAngle[i] = map((int)smooth[i], 0, 1023, MIN_A[i], MAX_A[i]);
16 servo[i].write(lastAngle[i]); // önce açı, sonra attach / angle first, then attach
17 servo[i].attach(SERVO_PIN[i]);
18 delay(300); // servolar sırayla uyansın / wake the servos one by one
19 }
20}
21
22void loop() {
23 for (byte i = 0; i < 4; i++) {
24 smooth[i] += (analogRead(POT_PIN[i]) - smooth[i]) * 0.2; // yumuşatma filtresi / low-pass filter
25 int angle = map((int)smooth[i], 0, 1023, MIN_A[i], MAX_A[i]);
26 if (abs(angle - lastAngle[i]) >= 2) { // ölü bant: küçük gürültüyü yok say / deadband: ignore small noise
27 servo[i].write(angle);
28 lastAngle[i] = angle;
29 }
30 }
31 delay(15);
32}Das Array smooth ist ein einfacher Tiefpassfilter: Pro Durchlauf fließen nur 20 % des neuen Messwerts ein, Sprünge werden geglättet und der Arm reagiert trotzdem schnell. Mit 0,1 statt 0,2 wird die Bewegung weicher, aber träger. Die zweite Maßnahme ist ein Totband: Weicht der neue Winkel nicht um mindestens 2 Grad vom zuletzt geschriebenen ab, bekommt das Servo keinen Befehl, und das Zittern im Stillstand hört auf. Da map() den Poti-Bereich direkt auf die sicheren Grenzen jedes Gelenks umrechnet, läuft das Gelenk selbst bei voll aufgedrehtem Poti nicht in den mechanischen Anschlag.
Bewegungen aufnehmen und abspielen
Was einen Roboterarm wirklich zum Roboter macht, ist, dass er eine vorgeführte Bewegung selbstständig wiederholt. Das Prinzip ist einfach: Du bringst den Arm mit den Potis in eine Pose und drückst den Aufnahmetaster, der Arduino speichert die Winkel aller vier Gelenke. Nach ein paar Posen drückst du auf Abspielen, und der Arm fährt sie der Reihe nach ab, mit weichen Übergängen dazwischen. Die Posen landen im 1024 Byte großen EEPROM des Arduino Uno und überstehen daher das Ausschalten; jede Pose belegt 4 Byte, eine Aufnahme mit 100 Posen braucht also nur 401 Byte.
1#include <Servo.h>
2#include <EEPROM.h>
3
4const byte SERVO_PIN[4] = {3, 5, 6, 9}; // taban, omuz, dirsek, kıskaç / base, shoulder, elbow, gripper
5const byte POT_PIN[4] = {A0, A1, A2, A3};
6const byte MIN_A[4] = {0, 15, 20, 40}; // güvenli sınırlar / safe limits
7const byte MAX_A[4] = {180, 165, 160, 110};
8const byte BTN_REC = 7, BTN_PLAY = 8, LED = 13; // butonların diğer ucu GND'de / buttons go to GND
9const byte MAX_STEPS = 100; // 100 adım x 4 bayt = 400 bayt / 100 steps x 4 bytes
10
11Servo servo[4];
12float smooth[4]; // süzülmüş pot değeri / filtered pot value
13byte pos[4]; // servoların şu anki açısı / current servo angles
14byte stepCount; // kayıtlı adım sayısı / number of saved steps
15
16bool pressed(byte pin) { // basılıyken true / true while pressed
17 return digitalRead(pin) == LOW;
18}
19
20byte potAngle(byte i) {
21 smooth[i] += (analogRead(POT_PIN[i]) - smooth[i]) * 0.2; // yumuşatma / smoothing
22 return map((int)smooth[i], 0, 1023, MIN_A[i], MAX_A[i]);
23}
24
25void blink(byte n) {
26 for (byte k = 0; k < n; k++) {
27 digitalWrite(LED, HIGH); delay(80);
28 digitalWrite(LED, LOW); delay(80);
29 }
30}
31
32// Tüm eklemleri aynı anda hedefe götürür / moves all joints to the target together
33void moveTo(const byte target[4], byte msPerDegree) {
34 byte start[4];
35 int steps = 0;
36 for (byte i = 0; i < 4; i++) {
37 start[i] = pos[i];
38 int d = abs((int)target[i] - (int)start[i]);
39 if (d > steps) steps = d; // en uzun hareket / longest move
40 }
41 for (int s = 1; s <= steps; s++) {
42 for (byte i = 0; i < 4; i++) {
43 pos[i] = start[i] + (long)((int)target[i] - (int)start[i]) * s / steps; // ara açı / in-between angle
44 servo[i].write(pos[i]);
45 }
46 delay(msPerDegree);
47 }
48}
49
50void saveStep() {
51 if (stepCount >= MAX_STEPS) { blink(3); return; } // hafıza dolu / memory full
52 for (byte i = 0; i < 4; i++) EEPROM.update(1 + stepCount * 4 + i, pos[i]);
53 stepCount++;
54 EEPROM.update(0, stepCount); // 0. bayt adım sayısını tutar / byte 0 holds the step count
55 blink(1);
56}
57
58void playBack() {
59 byte target[4];
60 for (byte s = 0; s < stepCount; s++) {
61 for (byte i = 0; i < 4; i++) target[i] = EEPROM.read(1 + s * 4 + i);
62 moveTo(target, 12); // derece başına 12 ms / 12 ms per degree
63 delay(400); // her pozda kısa bekleme / short pause at each pose
64 if (pressed(BTN_PLAY)) break; // oynatırken basınca durur / press again to stop
65 }
66}
67
68void syncToPots() { // potların konumuna yumuşakça dön / ease back to the pots
69 byte target[4];
70 for (byte i = 0; i < 4; i++) {
71 smooth[i] = analogRead(POT_PIN[i]);
72 target[i] = map((int)smooth[i], 0, 1023, MIN_A[i], MAX_A[i]);
73 }
74 moveTo(target, 12);
75}
76
77void setup() {
78 pinMode(BTN_REC, INPUT_PULLUP);
79 pinMode(BTN_PLAY, INPUT_PULLUP);
80 pinMode(LED, OUTPUT);
81 stepCount = EEPROM.read(0);
82 if (stepCount > MAX_STEPS) stepCount = 0; // boş EEPROM 255 okur / a blank EEPROM reads 255
83 for (byte i = 0; i < 4; i++) {
84 smooth[i] = analogRead(POT_PIN[i]);
85 pos[i] = potAngle(i);
86 servo[i].write(pos[i]); // önce açı, sonra attach / angle first, then attach
87 servo[i].attach(SERVO_PIN[i]);
88 delay(300);
89 }
90}
91
92void loop() {
93 // Kayıt: kısa bas = pozu kaydet, 2 sn basılı tut = hepsini sil / Record: short press = save pose, hold 2 s = erase all
94 if (pressed(BTN_REC)) {
95 unsigned long t0 = millis();
96 while (pressed(BTN_REC) && millis() - t0 < 2000) {}
97 if (millis() - t0 >= 2000) {
98 stepCount = 0;
99 EEPROM.update(0, 0);
100 blink(5);
101 } else {
102 saveStep();
103 }
104 while (pressed(BTN_REC)) {} // bırakılmasını bekle / wait for release
105 delay(50); // buton arkı / debounce
106 }
107
108 // Oynat: kaydedilen pozları sırayla tekrarla / play: repeat the saved poses in order
109 if (pressed(BTN_PLAY) && stepCount > 0) {
110 while (pressed(BTN_PLAY)) {}
111 delay(50);
112 digitalWrite(LED, HIGH);
113 playBack();
114 while (pressed(BTN_PLAY)) {}
115 syncToPots();
116 digitalWrite(LED, LOW);
117 }
118
119 // Normal mod: kolu potlarla sür / normal mode: drive the arm with the pots
120 for (byte i = 0; i < 4; i++) {
121 byte a = potAngle(i);
122 if (abs((int)a - (int)pos[i]) >= 2) {
123 pos[i] = a;
124 servo[i].write(a);
125 }
126 }
127 delay(15);
128}| Aktion | Was du tust | Was der Arm tut |
|---|---|---|
| Pose speichern | Aufnahmetaster (D7) kurz drücken | Die LED blinkt einmal, die Pose wird ins EEPROM geschrieben |
| Aufnahmen löschen | Aufnahmetaster 2 Sekunden gedrückt halten | Die LED blinkt fünfmal, die Schrittzahl wird zurückgesetzt |
| Abspielen | Abspieltaster (D8) drücken und loslassen | Die LED leuchtet, die Posen laufen der Reihe nach mit weichen Übergängen ab |
| Abspielen stoppen | Während des Abspielens den Abspieltaster gedrückt halten | Der Arm beendet die aktuelle Pose und stoppt |
| Zurück in den Normalbetrieb | Nach dem Abspielen nichts tun | Der Arm fährt langsam dorthin, wo die Potis stehen, die LED geht aus |
Die Funktion moveTo() ist das Herzstück des Sketches: Sie sucht das Gelenk mit dem längsten Weg, teilt die Bewegung in entsprechend viele Schritte und bewegt bei jedem Schritt alle Gelenke anteilig weiter. So kommen alle Gelenke gleichzeitig am Ziel an, und der Arm bewegt sich flüssig statt ruckartig Gelenk für Gelenk. 12 ms pro Grad bedeuten, dass eine Bewegung um 90 Grad etwa 1,1 Sekunden dauert. Für schnelleres Abspielen kannst du den Wert senken, doch selbst ohne Last dreht ein MG996R 60 Grad in 0,17 Sekunden, also etwa 3 ms pro Grad; darunter kommen die Servos den Befehlen nicht hinterher und die Bewegung wird ruckelig.
Servotreiber PCA9685: 6 Servos und ein Signal ohne Zittern
Sobald du auf 5–6 Servos gehst oder Zusatzmodule wie Bluetooth einbaust, macht dir ein PCA9685-Modul das Leben viel leichter. Die Platine ist ein PWM-Generator mit 16 Kanälen und 12 Bit Auflösung; sie spricht über nur zwei Leitungen per I2C mit dem Arduino und erzeugt die Servoimpulse mit eigenem Takt, unabhängig vom Prozessor. Die Standardadresse ist 0x40; über die Lötbrücken A0–A5 änderst du die Adresse und kannst bis zu 62 Platinen, also 992 Servos, verketten. Bei 50 Hz wird die Periode von 20 ms in 4096 Schritte geteilt, ein Schritt dauert also etwa 4,9 µs; der Bereich 544–2400 µs umfasst rund 380 Schritte, das ergibt eine Auflösung von ungefähr einem halben Grad.
| PCA9685-Pin | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| GND | GND | GND | GND |
| VCC (nur Chipversorgung) | 5V | 3V3 | 3V3 |
| SDA | A4 | GPIO 21 | GP4 |
| SCL | A5 | GPIO 22 | GP5 |
| Klemme V+ (Servoversorgung) | Extern 5–6 V | Extern 5–6 V | Extern 5–6 V |
| OE | Frei lassen | Frei lassen | Frei lassen |
| Servostecker 0–5 | Basis, Schulter, Ellbogen, Handgelenk, Handgelenk-Drehung, Greifer | Gleiche Reihenfolge | Gleiche Reihenfolge |
Roboterarm mit Joysticks steuern
Ein Joystick funktioniert anders als ein Poti: Losgelassen springt er in die Mitte zurück. Koppelst du den Winkel direkt an die Joystickposition, schnellt das Gelenk beim Loslassen auf 90 Grad. Der richtige Weg ist eine Geschwindigkeitssteuerung: Je weiter du den Joystick drückst, desto schneller dreht das Gelenk, und lässt du los, bleibt es stehen. Im Code unten steuern zwei Joysticks vier Gelenke (linker Joystick Basis und Schulter, rechter Joystick Ellbogen und Handgelenk); der Taster des linken Joysticks öffnet und schließt den Greifer, der des rechten schickt den Arm in die Ausgangspose. Schließ VRx und VRy des linken Joysticks an A0 und A1 an, VRy und VRx des rechten an A2 und A3 und die SW-Pins an D2 und D4; versorgen kannst du die Joysticks über den 5V-Pin des Arduino. Zum Kompilieren installierst du im Bibliotheksverwalter die Adafruit PWM Servo Driver Library und bestätigst die Abhängigkeit Adafruit BusIO.

1#include <Wire.h>
2#include <Adafruit_PWMServoDriver.h>
3
4Adafruit_PWMServoDriver pca = Adafruit_PWMServoDriver(0x40); // varsayılan adres / default address
5
6// Kanal: 0 taban, 1 omuz, 2 dirsek, 3 bilek, 4 bilek dönüşü, 5 kıskaç / Channel: 0 base, 1 shoulder, 2 elbow, 3 wrist, 4 wrist roll, 5 gripper
7const byte N = 6;
8const float MIN_A[N] = {0, 15, 20, 10, 0, 40}; // güvenli sınırlar / safe limits
9const float MAX_A[N] = {180, 165, 160, 170, 180, 110};
10const float HOME[N] = {90, 90, 90, 90, 90, 40}; // başlangıç pozu, kıskaç açık / home pose, gripper open
11const byte JOY[4] = {A0, A1, A2, A3}; // J1 X, J1 Y, J2 Y, J2 X → kanal / channel 0, 1, 2, 3
12const int8_t DIR[4] = {1, -1, 1, 1}; // ters giden ekseni -1 yap / set -1 to invert an axis
13const byte BTN_GRIP = 2, BTN_HOME = 4; // joystick SW pinleri / joystick SW pins
14const float MAX_SPEED = 90; // en yüksek hız, derece/sn / top speed, degrees/s
15const float DT = 0.02; // 20 ms döngü / 20 ms loop
16const int DEADZONE = 60; // ortadaki ölü bölge / dead zone around center
17
18float target[N], current[N];
19bool gripClosed = false;
20
21void writeServo(byte ch, float a) {
22 // Servo.h ile aynı 544–2400 µs aralığı / same 544–2400 µs range as Servo.h
23 pca.writeMicroseconds(ch, (uint16_t)(544 + a * (2400 - 544) / 180.0));
24}
25
26bool newPress(byte pin, byte k) { // her basışta bir kez true / true once per press
27 static bool prev[2] = {HIGH, HIGH};
28 bool now = digitalRead(pin);
29 bool edge = (prev[k] == HIGH && now == LOW);
30 prev[k] = now;
31 return edge;
32}
33
34void setup() {
35 pinMode(BTN_GRIP, INPUT_PULLUP);
36 pinMode(BTN_HOME, INPUT_PULLUP);
37 pca.begin();
38 pca.setOscillatorFrequency(27000000); // çoğu modülde ~27 MHz / ~27 MHz on most boards
39 pca.setPWMFreq(50); // servolar için 50 Hz / 50 Hz for servos
40 Wire.setClock(400000); // hızlı I2C / fast I2C
41 for (byte i = 0; i < N; i++) {
42 target[i] = current[i] = HOME[i];
43 writeServo(i, HOME[i]);
44 delay(300); // servolar sırayla uyansın / wake servos one by one
45 }
46}
47
48void loop() {
49 static unsigned long last = 0;
50 if (millis() - last < 20) return; // 50 Hz güncelleme / 50 Hz update
51 last = millis();
52
53 // Joystick hızı belirler, konumu değil / the joystick sets speed, not position
54 for (byte i = 0; i < 4; i++) {
55 int v = analogRead(JOY[i]) - 512;
56 if (abs(v) > DEADZONE) {
57 target[i] += DIR[i] * (v / 512.0) * MAX_SPEED * DT;
58 target[i] = constrain(target[i], MIN_A[i], MAX_A[i]);
59 }
60 }
61 if (newPress(BTN_GRIP, 0)) { // kıskaç aç/kapa / toggle gripper
62 gripClosed = !gripClosed;
63 target[5] = gripClosed ? MAX_A[5] : MIN_A[5];
64 }
65 if (newPress(BTN_HOME, 1)) { // başlangıç pozuna dön / go to home pose
66 for (byte i = 0; i < N; i++) target[i] = HOME[i];
67 gripClosed = false;
68 }
69
70 // Her eklem hedefe hız sınırıyla gider / each joint follows its target with a speed limit
71 for (byte i = 0; i < N; i++) {
72 float d = constrain(target[i] - current[i], -MAX_SPEED * DT, MAX_SPEED * DT);
73 if (d != 0) {
74 current[i] += d;
75 writeServo(i, current[i]);
76 }
77 }
78}DEADZONE verhindert, dass kleine Abweichungen wie 490–530 in der Mittelstellung den Arm von selbst wandern lassen. MAX_SPEED legt die Geschwindigkeit in Grad pro Sekunde fest, wenn der Joystick voll ausgelenkt ist; 90 Grad pro Sekunde sind für die ersten Versuche sicher. Läuft ein Gelenk entgegen dem Joystick, tausch keine Kabel, sondern setz den passenden Wert im Array DIR auf -1. Der Code führt für jedes Gelenk ein Ziel (target) und eine tatsächliche Position (current); da sich die Position dem Ziel mit begrenzter Geschwindigkeit nähert, gleitet der Arm selbst beim Druck auf den Ausgangspose-Taster ruhig zurück, statt zu springen. Die Handgelenk-Drehung bleibt in diesem Sketch fest auf 90 Grad; du steuerst sie mit dem Befehl R im Bluetooth-Abschnitt. Die Details dieser nicht blockierenden Zeitsteuerung erklärt millis() statt delay().
Bluetooth-Steuerung per Handy (HC-05)
Das Modul HC-05 baut eine drahtlose serielle Verbindung zwischen Handy und Arduino auf. Sein TXD-Pin kommt an D10 des Arduino, der RXD-Pin über einen Spannungsteiler an D11: D11 → 1 kΩ → RXD → 2 kΩ → GND. Der Teiler senkt das 5-V-Signal des Arduino auf die etwa 3,3 V, die das HC-05 erwartet. Die Standard-Datenrate beträgt 9600 Baud, die Kopplungs-PIN meist 1234. Das Handy schickt einfache Textbefehle: Ein Buchstabe wählt das Gelenk, die folgende Zahl gibt den Winkel an, ein Zeilenende oder Semikolon schließt den Befehl ab.

| Befehl | Bedeutung | Beispiel |
|---|---|---|
| B + Winkel | Basis | B90 |
| S + Winkel | Schulter | S120 |
| E + Winkel | Ellbogen | E45 |
| W + Winkel | Handgelenk (auf und ab) | W100 |
| R + Winkel | Handgelenk-Drehung | R0 |
| G + Winkel | Greifer | G110 (schließen), G40 (öffnen) |
| H | Zurück in die Ausgangspose | H |
| K / P / X / C | Pose speichern / in Schleife abspielen / stoppen / Posen löschen | K |
1#include <Wire.h>
2#include <SoftwareSerial.h>
3#include <Adafruit_PWMServoDriver.h>
4
5SoftwareSerial bt(10, 11); // D10 ← HC-05 TXD, D11 → bölücü / divider → HC-05 RXD
6Adafruit_PWMServoDriver pca = Adafruit_PWMServoDriver(0x40);
7
8const byte N = 6;
9const char NAME[N] = {'B', 'S', 'E', 'W', 'R', 'G'}; // taban, omuz, dirsek, bilek, dönüş, kıskaç / base, shoulder, elbow, wrist, roll, gripper
10const byte MIN_A[N] = {0, 15, 20, 10, 0, 40}; // güvenli sınırlar / safe limits
11const byte MAX_A[N] = {180, 165, 160, 170, 180, 110};
12const byte HOME[N] = {90, 90, 90, 90, 90, 40};
13const byte MAX_POSES = 30;
14const float SPEED = 1.5; // 20 ms'de en fazla 1,5° (75°/sn) / max 1.5° per 20 ms (75°/s)
15
16float current[N];
17byte target[N];
18byte poses[MAX_POSES][N]; // kaydedilen pozlar (RAM) / recorded poses (RAM)
19byte poseCount = 0, playIndex = 0;
20bool playing = false;
21char buf[12];
22byte len = 0;
23
24void writeServo(byte ch, float a) {
25 pca.writeMicroseconds(ch, (uint16_t)(544 + a * (2400 - 544) / 180.0)); // 544–2400 µs
26}
27
28void handle(char *cmd) { // ör. / e.g. "S120", "H", "K", "P"
29 char c = toupper(cmd[0]);
30 if (c == 'H') { // başlangıç pozu / home pose
31 for (byte i = 0; i < N; i++) target[i] = HOME[i];
32 playing = false;
33 } else if (c == 'K') { // pozu kaydet / save pose
34 if (poseCount < MAX_POSES) {
35 for (byte i = 0; i < N; i++) poses[poseCount][i] = target[i];
36 poseCount++;
37 }
38 bt.print(F("Poz / pose: "));
39 bt.println(poseCount);
40 } else if (c == 'P') { // döngüyle oynat / play in a loop
41 if (poseCount > 0) { playing = true; playIndex = 0; }
42 } else if (c == 'X') { // durdur / stop
43 playing = false;
44 } else if (c == 'C') { // kayıtları sil / clear poses
45 poseCount = 0;
46 playing = false;
47 } else {
48 for (byte i = 0; i < N; i++) {
49 if (c == NAME[i]) {
50 target[i] = constrain(atoi(cmd + 1), MIN_A[i], MAX_A[i]);
51 playing = false; // elle komut oynatmayı keser / a manual command stops playback
52 }
53 }
54 }
55}
56
57bool arrived() {
58 for (byte i = 0; i < N; i++)
59 if (fabs(target[i] - current[i]) > 0.5) return false;
60 return true;
61}
62
63void setup() {
64 bt.begin(9600); // HC-05 varsayılan hızı / HC-05 default baud
65 pca.begin();
66 pca.setOscillatorFrequency(27000000);
67 pca.setPWMFreq(50);
68 for (byte i = 0; i < N; i++) {
69 target[i] = HOME[i];
70 current[i] = HOME[i];
71 writeServo(i, HOME[i]);
72 delay(300); // servolar sırayla uyansın / wake servos one by one
73 }
74}
75
76void loop() {
77 // Komutları satır satır topla / collect commands line by line
78 while (bt.available()) {
79 char ch = bt.read();
80 if (ch == '\n' || ch == '\r' || ch == ';') {
81 if (len > 0) { buf[len] = '\0'; handle(buf); len = 0; }
82 } else if (len < sizeof(buf) - 1) {
83 buf[len++] = ch;
84 }
85 }
86
87 static unsigned long last = 0, arrivedAt = 0;
88 if (millis() - last < 20) return; // 50 Hz güncelleme / 50 Hz update
89 last = millis();
90
91 if (playing) {
92 if (!arrived()) arrivedAt = millis(); // hareket sürüyor / still moving
93 else if (millis() - arrivedAt > 300) { // her pozda 300 ms bekle / hold each pose 300 ms
94 for (byte i = 0; i < N; i++) target[i] = poses[playIndex][i];
95 playIndex = (playIndex + 1) % poseCount; // sona gelince başa dön / wrap around
96 arrivedAt = millis();
97 }
98 }
99
100 for (byte i = 0; i < N; i++) {
101 float d = constrain(target[i] - current[i], -SPEED, SPEED); // hız sınırı / speed limit
102 if (d != 0) {
103 current[i] += d;
104 writeServo(i, current[i]);
105 }
106 }
107}In diesem Sketch hängen die Servos am PCA9685 statt direkt am Arduino, und das ist Absicht. SoftwareSerial sperrt beim Empfang jedes Bytes die Interrupts für etwa 1 ms; würden die Servos mit der Servo-Bibliothek an Arduino-Pins laufen, verlängerten sich in dieser Zeit ihre Impulse und der Arm würde bei jedem Befehl zucken. Weil der PCA9685 die Impulse selbst erzeugt, bleibt das Signal stabil, egal wie viel über Bluetooth los ist. Aufnehmen und Abspielen geht jetzt auch vom Handy aus: Bring den Arm mit den Schiebereglern in eine Pose und sende K; nach ein paar Posen wiederholt der Arm sie mit P in einer Schleife, X stoppt ihn. In dieser Version liegen die Posen im RAM und gehen beim Ausschalten verloren; sollen sie bleiben, ergänze die EEPROM-Methode aus dem vorigen Sketch.
Kabelloser Roboterarm mit dem ESP32
Da der ESP32 klassisches Bluetooth eingebaut hat, brauchst du kein HC-05: Mit der Bibliothek BluetoothSerial erscheint das Board auf dem Handy als „BlueGrays-RobotKol“ und versteht dieselben Befehle. Schließ den PCA9685 an GPIO 21 (SDA) und GPIO 22 (SCL) an und versorge VCC mit 3V3; die Servosignale haben dann 3,3-V-Pegel, was die allermeisten Servos problemlos erkennen. BluetoothSerial läuft nur auf dem klassischen ESP32 (ESP32-WROOM-32, DevKit); ESP32-S2, S3 und C3 unterstützen kein klassisches Bluetooth.
1#include <Wire.h>
2#include <BluetoothSerial.h>
3#include <Adafruit_PWMServoDriver.h>
4
5BluetoothSerial bt; // klasik Bluetooth SPP / classic Bluetooth SPP
6Adafruit_PWMServoDriver pca = Adafruit_PWMServoDriver(0x40);
7
8const int N = 6;
9const char NAME[N] = {'B', 'S', 'E', 'W', 'R', 'G'}; // taban, omuz, dirsek, bilek, dönüş, kıskaç / base, shoulder, elbow, wrist, roll, gripper
10const int MIN_A[N] = {0, 15, 20, 10, 0, 40};
11const int MAX_A[N] = {180, 165, 160, 170, 180, 110};
12const int HOME[N] = {90, 90, 90, 90, 90, 40};
13float current[N], target[N];
14String line;
15
16void writeServo(int ch, float a) {
17 pca.writeMicroseconds(ch, (uint16_t)(544 + a * (2400 - 544) / 180.0)); // 544–2400 µs
18}
19
20void handle(String cmd) { // ör. / e.g. "E45", "H"
21 cmd.trim();
22 if (cmd.length() == 0) return;
23 char c = toupper(cmd[0]);
24 if (c == 'H') {
25 for (int i = 0; i < N; i++) target[i] = HOME[i];
26 return;
27 }
28 int value = cmd.substring(1).toInt();
29 for (int i = 0; i < N; i++)
30 if (c == NAME[i]) target[i] = constrain(value, MIN_A[i], MAX_A[i]);
31}
32
33void setup() {
34 Serial.begin(115200);
35 bt.begin("BlueGrays-RobotKol"); // telefonda görünen ad / name shown on the phone
36 Wire.begin(21, 22); // SDA = GPIO 21, SCL = GPIO 22
37 pca.begin();
38 pca.setOscillatorFrequency(27000000);
39 pca.setPWMFreq(50);
40 for (int i = 0; i < N; i++) {
41 current[i] = target[i] = HOME[i];
42 writeServo(i, HOME[i]);
43 delay(300);
44 }
45 Serial.println("Bluetooth hazir / ready");
46}
47
48void loop() {
49 while (bt.available()) {
50 char ch = bt.read();
51 if (ch == '\n' || ch == ';') { handle(line); line = ""; }
52 else if (line.length() < 12) line += ch;
53 }
54
55 static unsigned long last = 0;
56 if (millis() - last < 20) return; // 50 Hz güncelleme / 50 Hz update
57 last = millis();
58 for (int i = 0; i < N; i++) {
59 float d = constrain(target[i] - current[i], -1.5f, 1.5f); // yumuşak hareket / smooth motion
60 if (d != 0) {
61 current[i] += d;
62 writeServo(i, current[i]);
63 }
64 }
65}Roboterarm mit dem Raspberry Pi Pico (MicroPython)
Für Servos am Raspberry Pi Pico brauchst du keine zusätzliche Bibliothek: Stell die PWM auf 50 Hz ein und gib die Impulsbreite mit duty_ns() in Nanosekunden an. Mit 16 PWM-Kanälen kann der Pico 6 Servos direkt ansteuern; Pinpaare im selben Slice teilen sich die Frequenz, da aber alle Servos 50 Hz nutzen, ist das kein Problem. Die Grenze liegt auf der Analogseite: Der Pico hat nur drei nutzbare ADC-Pins (GP26, GP27, GP28). Deshalb steuern im Code unten Potis drei Gelenke, und der Greifer öffnet und schließt per Taster an GP14. Da der Pico mit 3,3 V arbeitet, kommen die Potis an 3V3; die Servos versorgst du weiterhin aus einer externen 5-V-Quelle.
1from machine import Pin, PWM, ADC
2import time
3
4# Servo pinleri: taban, omuz, dirsek, kıskaç / servo pins: base, shoulder, elbow, gripper
5servos = [PWM(Pin(p)) for p in (10, 11, 12, 13)]
6for s in servos:
7 s.freq(50) # 50 Hz servo sinyali / 50 Hz servo signal
8
9pots = [ADC(26), ADC(27), ADC(28)] # Pico'da 3 ADC pini var / the Pico has 3 ADC pins
10button = Pin(14, Pin.IN, Pin.PULL_UP) # kıskaç butonu GND'ye / gripper button to GND
11
12MIN_A = (0, 15, 20, 40) # güvenli sınırlar / safe limits
13MAX_A = (180, 165, 160, 110)
14
15def write(i, angle):
16 angle = max(MIN_A[i], min(MAX_A[i], angle))
17 servos[i].duty_ns(544000 + int(angle * 10311)) # 544–2400 µs aralığı / 544–2400 µs range
18
19smooth = [p.read_u16() for p in pots]
20grip_closed = False
21last_btn = 1
22write(3, MIN_A[3]) # kıskaç açık başlar / gripper starts open
23
24while True:
25 for i in range(3):
26 smooth[i] += (pots[i].read_u16() - smooth[i]) * 0.2 # yumuşatma / smoothing
27 write(i, MIN_A[i] + (MAX_A[i] - MIN_A[i]) * smooth[i] / 65535)
28 b = button.value()
29 if b == 0 and last_btn == 1: # yeni basış / new press
30 grip_closed = not grip_closed
31 write(3, MAX_A[3] if grip_closed else MIN_A[3])
32 last_btn = b
33 time.sleep_ms(20) # 50 Hz döngü / 50 Hz loopDer nächste Schritt: inverse Kinematik
Bisher haben wir jedem Gelenk seinen Winkel einzeln vorgegeben; das nennt man Vorwärtskinematik. Die inverse Kinematik macht das Gegenteil: Du gibst den Punkt (x, y) an, den der Greifer erreichen soll, und der Code berechnet die nötigen Gelenkwinkel. Bei einem Arm aus zwei Gliedern mit der Länge L1 (Schulter bis Ellbogen) und L2 (Ellbogen bis Handgelenk) berechnest du zuerst D = (x² + y² − L1² − L2²) / (2·L1·L2). Der Ellbogenwinkel ist θ2 = acos(D), der Schulterwinkel θ1 = atan2(y, x) + atan2(L2·sin θ2, L1 + L2·cos θ2) (Lösung mit Ellbogen oben). Mit L1 = L2 = 12 cm ergibt sich zum Beispiel für einen Punkt 15 cm vor und 5 cm über der Schulter D ≈ −0,13, θ2 ≈ 97,6° und θ1 ≈ 67,2°: Der Oberarm zeigt 67 Grad über die Waagerechte, der Unterarm knickt relativ dazu um 97,6 Grad nach unten ab. Ist der Betrag von D größer als 1, liegt der Punkt außerhalb der Reichweite. Der Basiswinkel wird getrennt aus der Position des Ziels in der Draufsicht berechnet. Beim Umrechnen in Servowinkel musst du Einbaurichtung und Mittelstellung jedes Servos berücksichtigen.
Zittern und Resets: häufige Fehler und Lösungen
| Symptom | Wahrscheinliche Ursache | Lösung |
|---|---|---|
| Servos zittern und brummen, obwohl der Arm steht | Rauschen in den Poti-Werten oder schwache Versorgung | Glättungsfilter und 2°-Totband einbauen, externes Netzteil und 1000-µF-Kondensator nutzen |
| Der Arduino startet neu, wenn sich der Arm bewegt, das HC-05 verliert die Verbindung | Der Anlaufstrom der Servos lässt die Spannung einbrechen | Servos aus einer eigenen 5–6-V-Quelle versorgen, GNDs verbinden, Servos beim Start nacheinander einschalten |
| Servos zucken bei jedem Bluetooth-Befehl | SoftwareSerial-Interrupts stören die Impulse der Servo-Bibliothek | Servos an einen PCA9685 hängen oder das eingebaute Bluetooth des ESP32 nutzen |
| Beim Einschalten schnellt der Arm irgendwohin | Servos kennen ihre letzte Position nicht und fahren beim ersten Befehl mit voller Geschwindigkeit | Arm vor dem Ausschalten in die Ausgangspose fahren, write() vor attach() aufrufen |
| Die Schulter hebt den Arm nicht oder sackt langsam ab | Zu wenig Drehmoment | Drehmoment berechnen; MG996R, kürzeren Arm, Gegengewicht oder Feder verwenden |
| Ein Gelenk bleibt am Ende hängen und wird heiß | Das Servo drückt gegen einen mechanischen Anschlag | Die Grenzen MIN_A und MAX_A aus der Kalibrierung in den Code eintragen |
| Das Greiferservo wird heiß, während es etwas hält | Der Greifer will weiter schließen, das Servo bleibt blockiert | Geschlossenen Winkel auf den Punkt setzen, an dem der Gegenstand gerade berührt wird, Gummi an die Finger |
| Der PCA9685 ist angeschlossen, aber kein Servo bewegt sich | Keine Spannung an V+ oder keine gemeinsame Masse | 5–6 V an V+ anlegen, I2C-Adresse (0x40) prüfen |
| Ein Gelenk rutscht unter Last durch | Kunststoffhorn ausgerissen oder Mittelschraube fehlt | Metallhorn verwenden und mit der Mittelschraube auf der Welle sichern |
Projektideen mit dem Roboterarm
- Farbsortierer: Lies die Farbe von Würfeln mit einem Farbsensor TCS3200 und lass den Arm jede Farbe in ihre eigene Kiste legen.
- Gestensteuerung: Mit einem MPU6050 am Handschuh neigt sich der Roboterarm in dieselbe Richtung wie dein Handgelenk.
- Funkfernbedienung: Schließ zwei Joysticks an einen zweiten Arduino an und sende die Befehle mit einem nRF24L01; so bekommst du eine Fernbedienung ganz ohne Handy.
- Steuerung im Browser: Erstelle mit einem ESP32 Schieberegler auf einer Webseite und steuere den Arm von jedem Gerät im selben WLAN; ein ESP32-WebSocket hält die Verzögerung minimal.
- Greifen vom Förderband: Bau mit einem Schrittmotor ein kleines Förderband; sobald ein TCRT5000 einen Gegenstand erkennt, nimmt der Arm ihn vom Band.
- Wettbewerb und Schulprojekt: Ein Arm mit Aufnahme und Wiedergabe ist unter den Ideen für Wettbewerbsprojekte eine starke Wahl, weil er Mechanik, Elektronik und Software zusammen zeigt.
Ein Arduino Roboterarm bringt dir Mechanik, Drehmomentrechnung, Stromversorgung und das Programmieren weicher Bewegungen gleichzeitig bei und wächst mit jeder Stunde, die du hineinsteckst. Fang mit einem Arm mit 4 Servos und Potis an, ergänze Aufnahme und Wiedergabe und steig dann mit PCA9685 und Bluetooth auf 6 Servos um. In der BlueGrays-App findest du Projekte wie die Servosteuerung per Poti und Joystick, die Bluetooth-Steuerung per Handy und eine Choreografie mit 32 Servos, jeweils mit Schaltplan und Schritt-für-Schritt-Code, sodass du jedes Gelenk deines Arms zuerst einzeln testen kannst.
Häufige Fragen
Welches Servo eignet sich für einen Arduino Roboterarm?
Für tragende Gelenke wie Basis, Schulter und Ellbogen ist das MG996R mit Metallgetriebe und 9–11 kg·cm Drehmoment eine gute Wahl. Für leichte Gelenke wie Handgelenk und Greifer reichen MG90S oder SG90. Kleine Arme nur mit SG90 funktionieren, wenn sie kurz und leicht bleiben, heben aber nur Gegenstände von einigen zehn Gramm.
Welches Netzteil braucht ein Roboterarm?
Für einen kleinen Arm mit 4 SG90 oder MG90S reicht ein 5-V-Netzteil mit 3 A. Für einen Arm mit 6 Servos, davon 3 MG996R und 3 kleine, empfiehlt sich ein Schaltnetzteil mit 5–6 V und mindestens 5 A, mit Reserve 10 A. Vergiss nicht, den Minuspol des Netzteils mit dem GND des Arduino zu verbinden.
Wie viele Servos kann ein Arduino ansteuern?
Die Servo-Bibliothek steuert am Arduino Uno bis zu 12 und am Mega bis zu 48 Servos. Die eigentliche Grenze ist aber der Strom: Alle Servos sollten aus einer externen Quelle versorgt werden. Ein PCA9685-Modul steuert über nur zwei I2C-Pins 16 Servos und lässt sich auf bis zu 62 Module erweitern.
Warum zittern die Servos meines Roboterarms?
Die häufigsten Ursachen sind eine zu schwache Versorgung, eine fehlende gemeinsame Masse und Rauschen in den Poti-Werten. Nutze ein externes Netzteil mit 1000-µF-Kondensator an der Servoschiene und setz im Code einen Glättungsfilter und ein Totband ein. Laufen SoftwareSerial und die Servo-Bibliothek zusammen, beseitigt der Umzug der Servos auf einen PCA9685 das Zittern vollständig.
Wie viel kann ein Roboterarm mit SG90 heben?
Das Blockiermoment des SG90 liegt bei etwa 1,8 kg·cm; für den sicheren Betrieb plane mit der Hälfte, also 0,9 kg·cm. Da Arm und Servos mit ihrem Eigengewicht schon einen Teil davon verbrauchen, trägt ein etwa 15 cm langer SG90-Arm in der Praxis nur leichte Gegenstände von 20–30 g, etwa einen Flaschendeckel oder einen kleinen Schaumstoffwürfel.
Wie steuere ich einen Roboterarm mit dem Handy?
Du schließt ein HC-05-Bluetooth-Modul an den Arduino an und schickst mit einem Bluetooth-Terminal oder einer Controller-App mit Schiebereglern Textbefehle wie „S120“. Mit einem ESP32 brauchst du dank eingebautem Bluetooth kein Zusatzmodul. Das HC-05 funktioniert nicht mit dem iPhone; dafür brauchst du ein BLE-Modul oder ESP32 BLE.
Projekte, die du danach bauen kannst
Alle Projekte ansehen
Servomotorbetrieb mit Potentiometer
Während du am Knopf des Potentiometers drehst, dreht sich der Arm des Servomotors einfach mit: Das eine Ende des Knopfes bedeutet null Grad, das andere eine halbe Umdrehung. Der Arduino liest die Stel

Servo-Steuerung mit Joystick
Steuere zwei Motoren mit einem Joystick, genau wie beim Schwenken einer Überwachungskamera. Drückst du den Hebel nach links oder rechts, dreht sich der untere Servomotor langsam, nach oben oder unten,

Verwendung von Servomotoren
Der Arm eines kleinen Servomotors dreht sich nacheinander in drei Stellungen: ganz an den Anfang, genau in die Mitte und ganz ans Ende, mit jeweils zwei Sekunden Pause. Der Arduino sagt dem Motor, bei

ESP32-Servomotor (Steuerung über Potentiometer)
In diesem Projekt steuern wir einen SG90-Servomotor mit einem Potentiometer. Wir stellen den Servowinkel ein, indem wir den vom Potentiometer gelesenen Analogwert auf den Bereich von 0 bis 180 Grad abbilden. Die ESP32Servo-Bibliothek erzeugt Servosignale an geeigneten GPIO-Pins des ESP32, die als Ausgang verwendet werden können; in diesem Projekt wird GPIO 18 verwendet. Dies ist eine grundlegende Steuerungsmethode für Roboterarme, Kamera-Gimbals und automatische Türmechanismen.

LED per Bluetooth mit dem Handy steuern
Eine kleine Probe dafür, das Licht in deinem Zimmer vom Handy aus ein- und auszuschalten. Das Bluetooth-Modul leitet die Buchstaben, die du vom Handy schickst, an den Arduino weiter; ein Buchstabe sch

Bluetooth-gesteuertes Auto mit L298N
Mach dein Handy zur Fernbedienung und steuere dein eigenes Roboterauto. Der Arduino empfängt über ein Bluetooth-Modul die Befehle einer Fernsteuer-App auf dem Handy und treibt die Räder über einen Mot

Servo-Kreis-Choreografie - Animierte Muster mit 32 Servos
Ein visuelles Animationsprojekt mit 32 kreisförmig angeordneten Servomotoren auf einem Arduino Mega. Die Servos werden in einem einzigen Array verwaltet und an die Pins 22-53 des Mega angeschlossen, um vier verschiedene Bewegungsabläufe darzustellen: eine chaotische Vibration, bei der sich alle Servos in zufälligen Winkeln bewegen, ein synchroner Sweep, bei dem sich alle gleichzeitig bewegen, eine sanft rotierende Welle um einen Kreis und schließlich eine Kompassnadel, die nach links und rechts
Weitere Artikel
BlueGrays-App
Diese Schaltung Schritt für Schritt auf dem Handy nachbauen
Schaltplan, Code und Teileliste auf einem Bildschirm. Über 200 Arduino-, ESP32- und Pico-Projekte zum Nachbauen.
Mit dem Handy scannen
Richte die Kamera auf den Code, dann öffnet sich der App-Store deines Handys.
oder im Store öffnen

