Viele Servos mit Arduino steuern

Will man viele Servos mit Arduino steuern, stößt man schnell an die Grenze des Boards. Die USB-Stromversorgung reicht kaum für mehr als einen Servomotor. Um trotzdem viele Servos zu versorgen, gibt es einen Servomotor-Treiber. Diese Erweiterungsplatine kann bis zu 16 Servos steuern und verfügt darüber hinaus über eine Anschlussmöglichkeit für eine externe Stromversorgung. Entwickelt wurde sie von Adafruit und hört auf den schönen Namen PCA9685.

 Adafruit PCA9685

Dieser Servomotor-Treiber wird per I2C-Schnittstelle angesprochen und belegt dadurch nur zwei Arduino-Pins. Zum Betreiben gibt es eine eigene Arduino-Programmbibliothek, die die Nutzung extrem einfach macht.

Spannend am Servomotor-Treiber PCA9685 ist, dass er über Adress-Pins verfügt, die die Nutzung von bis zu 62 Servomotor-Treibern ermöglicht. Das führt zu einer Gesamtanzahl von gleichzeitig steuerbaren Servos von 992!! (die nötige Stromversorgung vorausgesetzt)

Viele Servos mit Arduino steuern - PCA9685

Installation der Adafruit PWM Programmbibliothek

Klicke in der Arduino-Software auf Sketch>Bibliothek einbinden>Bibliothek verwalten. Suche im Textfeld nach Adafruit PWM und installiere die Adafruit PWM Servo Driver Library in der aktuellen Version.

Code – Viele Servos mit Arduino steuern

In diesem Beispiel wird jeder Servo (von 0 bis 15) einmal hin und her bewegt. Den Original-Code findest du in der Arduino-Software unter Datei>Beispiele>Adafruit PWM Servo Driver Library>test.

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver myServos = Adafruit_PWMServoDriver();

#define SERVOMIN  150
#define SERVOMAX  600

uint8_t servonum = 0;
uint8_t numberOfServos = 16;

void setup() {
  myServos.begin();
  myServos.setPWMFreq(60);
  delay(10);
}

void loop() {
  for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++){
    myServos.setPWM(servonum, 0, pulselen);
  }
  delay(500);

  for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--){
    myServos.setPWM(servonum, 0, pulselen);
  }
  delay(500);

  servonum ++;
  if (servonum > numberOfServos-1) servonum = 0;
}

Wie funktioniert dieses Programm?

Da das PCA9685 I2C für den Datenaustausch verwendet, muss die Wire.h-Bibliothek eingebunden werden. Wire war der Vorgänger von Arduino und die Bibliothek beinhaltet noch eine Reihe nützlicher Tools. Dazu die weiter oben erwähnte Bibliothek für das Modul selbst. Das passiert am Anfang des Programmes:

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Nun wird ein Adafruit_PWMServoDriver-Objekt angelegt. Über dessen Namen myServos können wir es dann ansprechen. Im Originalcode heißt diese Variable übrigens pwm, was ich als Variablennamen etwas unglücklick finde. Falls du also den Original-Code verwendest, nicht wundern.

Adafruit_PWMServoDriver myServos = Adafruit_PWMServoDriver();

Jetzt werden noch ein paar Konfigurationen getroffen. Der maximale und minimale Ausschlag der Servos wird als Konstante definiert:

#define SERVOMIN  150
#define SERVOMAX  600

Hier könnte fast genau so gut stehen:

int servoMin = 150;
int servoMax = 600;

Da man diese Werte aber im Laufenden Programm lieber nicht verändert, macht es Sinn, die Konstantendeklaration (oben) zu nutzen.

Die Hilfsvariable servonum wird verwendet, um später im Loop eine Zählervariable zur Verfügung zu haben. Sie zählt pro Durchlauf des Loops von 0 bis numberOfServos-1.

uint8_t servonum = 0;
uint8_t numberOfServos = 16;

Springen wir kurz an das Ende des Loops, um uns das genauer anzusehen.

  servonum ++;
  if (servonum > numberOfServos-1) servonum = 0;

Die Variable servonum wird also so lange wiederholt, bis sie größer als numberOfServos-1 ist. Sobald sie größer ist, wird sie auf 0 zurück gesetzt.

Nun steht uns im Loop also eine Variable zur Verfügung, die von 0 bis 15 durchläuft. Das ist praktisch, denn im Beispiel wollen wir ja 16 Servos – und zwar einen nach dem anderen – bewegen.

Das wird durch zwei for-Schleifen realisiert. Die eine zählt von SERVOMIN bis SERVOMAX hoch,

  for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++){
    myServos.setPWM(servonum, 0, pulselen);
  }
  delay(500);

die andere (nach einer Verzögerung von 500 ms – delay(500) – ) von SERVOMAX auf SERVOMIN herunter.

  for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--){
    myServos.setPWM(servonum, 0, pulselen);
  }
  delay(500);

Die Zähltervariable pulselen speichert dabei den aktuellen Wert, also den Wert zwischen SERVOMIN und SERVOMAX). Und dieser Wert wird jetzt einfach an das PCA9685-Modul gesendet, welches dann den Servo (servonum) auf den Wert pulselen stellt.

myServos.setPWM(servonum, 0, pulselen);

Die Eintragungen im Setup habe ich übersprungen. Also zur Erläuterung. Die erste Zeile startet das myServos-Objekt, die zweite setzt die Servo-Frequenz auf 60 Hz. Das ist auch die Frequenz für Servomotoren (LEDs bevorzugen z. B. 1KHz).

  myServos.begin();
  myServos.setPWMFreq(60);
  delay(10);

Servos auf bestimmte Position stellen

Hier noch mal ein Programm, dass 16 Servos zuerst auf zwei je vordefinierte Positionen stellt, wobei die Positionen von Servo zu Servo unterschiedlich sein können. Danach wird jeder Servo auf eine zufällige Position gestellt.

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver myServos = Adafruit_PWMServoDriver();

int servosPos1[]={150,160,170,180,190,200,210,220,230,240,250,260,270,280,290,300};
int servosPos2[]={500,490,480,470,460,450,430,420,410,400,390,380,370,360,350,340};

#define SERVOMIN  150
#define SERVOMAX  600

int numberOfServos = 16;

void setup() {
  myServos.begin();
  myServos.setPWMFreq(60);
  delay(10);
}

void loop() {
  for (int i=0; i<numberOfServos; i++){
    myServos.setPWM(i, 0, servosPos1[i]);
  }
  delay(500);

  for (int i=0; i<numberOfServos; i++){
    myServos.setPWM(i, 0, servosPos2[i]);
  }
  delay(500);

  for (int i=0; i<numberOfServos; i++){
    myServos.setPWM(i, 0, random(SERVOMIN,SERVOMAX));
  }
  delay(500);

}

Mehrere PCA9685 verwenden

Sollen es noch mehr Servos sein? Kein Problem. Es lassen sich bis zu 62 PCA9685-Module koppeln. (Bitte kauf die 62 Module und die dazu gehörigen 922 Servos über meine Affiliate Links 😂 )

Um mehrere Module zu verwenden, muss man auf den PCA8574-Modulen eindeutige Adressen zuweisen. Das macht man, indem man die dazu gehörigen Lötstellen mit Lötzinn überbrückt (A0 – A5). In der Grafik kannst du sehen, wie die Adressen zugewiesen werden.

Viele Servos mit Arduino steuern - Viele PCA9685 Module Adressen zuweisen

Schaltplan: Viele PCA9685-Module verwenden

Viele Servos mit Arduino steuern - Viele PCA9685 Module

Markus stand genau vor dem Problem, mehrere Module zu betreiben und hat uns den folgenden Beispiel-Code zur Verfügung gestellt. Vielen Dank nochmal dafür. :-)

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver board1 = Adafruit_PWMServoDriver(0x40);
Adafruit_PWMServoDriver board2 = Adafruit_PWMServoDriver(0x41);

int board1gerade[] = {150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280, 290, 300}; //Min Position Servos Board1
int board1abzw[] = {500, 490, 480, 470, 460, 450, 430, 420, 410, 400, 390, 380, 370, 360, 350, 340}; //Max Position Servos Board1

int board2gerade[] = {150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280, 290, 300}; //Min Position Servos Board2
int board2abzw[] = {500, 490, 480, 470, 460, 450, 430, 420, 410, 400, 390, 380, 370, 360, 350, 340}; //Max Position Servos Board2

#define SERVOMIN 150
#define SERVOMAX 600

int numberOfServos = 16;

void setup() {
  board1.begin();
  board2.begin();
  board1.setPWMFreq(60);
  board2.setPWMFreq(60);
  delay(10);
}

void loop() {
  for (int i = 0; i < numberOfServos; i++) { //Servos auf im Array vorgegebene Stellung (gerade) setzen
    board1.setPWM(i, 0, board1gerade[i]);
  }
  delay(500);

  for (int i = 0; i < numberOfServos; i++) { //Servos auf im Array vorgegebene Stellung (abzw) setzen
    board1.setPWM(i, 0, board1abzw[i]);
  }
  delay(500);

  for (int i = 0; i < numberOfServos; i++) { //Servos auf im Array vorgegebene Stellung (gerade) setzen
    board2.setPWM(i, 0, board2gerade[i]);
  }
  delay(500);

  for (int i = 0; i < numberOfServos; i++) { //Servos auf im Array vorgegebene Stellung (abzw) setzen
    board2.setPWM(i, 0, board2abzw[i]);
  }
}

Wenn du mehr Fragen hast, schreibe diese gerne in die Kommentare. Ansonsten viel Spaß mit der Servo-Party!

Bezugsquellen des PCA9685

Original PCA9685 von Adafruit (1 Stück für ca. $14.95)
AZDelivery PCA9685 auf Amazon* (3 Stück für ca. 12,99 €)

Projekte mit Servos


Wenn dir das Projekt gefallen hat und du von weiteren interessanten Projekten inspiriert werden willst, sieh dir doch mal mein neues E-Book an!

Darin findest du die beliebtesten Arduino-Projekte von StartHardware. Jedes Projekt umfasst Schaltplan, Bauteile, Beschreibung und Code. Für Einsteiger gibt es ein Arduino-Schnellstart-Kapitel und einen kompakten Programmierkurs. Zusätzlich findest du Zwischenkapitel mit Expertenwissen zu den Komponenten, die verwendet werden. Alle Code-Beispiele gibt es natürlich als Download.

Weitere Informationen findest du auf der Produktseite. Klicke jetzt auf den Button, um Details anzusehen.


Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

 

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.

15 Kommentare zu »Viele Servos mit Arduino steuern«

  1. Ist ja super beschrieben…ich bin ein gaaaz blutiger Anfänger…wie sieht es aus wenn ich jeden Servo einzeln Steuern will ??? z.B. aus einem Empfänger 12 Servos einer Fernsteuerung durch einen Slipring (wenig Pole) wieder auf die passenden Servos verteilen will ? hat das jemand schon gemacht ?

      1. ich brauche das…. stell dir vor: Fernsteuerung im Baggerfahrwerk und die Steuersignale müssen durch den Drehkranz nach oben zumRest…

    1. Guten Abend,

      das wäre schön, wenn es ohne großen Aufwand funktionieren würde, aber Du hast ja nur einen Schleifring. Darüber könnte man theoretisch die nötige Adresse pro Board ansprechen, aber wie willst Du mit der Funke dannn sagen, welches Servo was machen soll pro Board? Schön wäre so eine Lösung auf jeden Fall.

      Gruß
      Markus

      1. Nabend…
        also meine Idee wäre die Ausgänge der Steuerung in den Arduino, dann mit den 4 Kabeln durch den Schleifring und aus dem PCA dann auf die Servos. Der Schleifring hat 18 Pole, brauch aber für andere Sachen auch noch Leitungen (Stromversorgungen , Beleuchtungen etc.), dann wäre das mit dem PCA super, 18-4 wären noch 12 Leitungen übrig für die anderen Sachen…oder andere PCA´s für noch mehr Dummheiten…

  2. Ich würde gerne mehrere dieser Boads verbinden. Die Adresseinstellung wird ja über die Lötbrücken realisiert, aber wie werden die weiteren Module dann angesprochen?

    1. Hi Markus,

      spannend! Was baust du für ein Projekt? Weißt du das schon?

      Also: Scheinbar kann man das in der Deklaration des Objektes gleich mit übernehmen:

      Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(0x40);
      Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(0x41);

      Hier ist das noch mal genauer beschrieben: https://learn.adafruit.com/16-channel-pwm-servo-driver/chaining-drivers

      Sag bescheid, ob es klappt.

      Liebe Grüße

      Stefan

      1. Hi Stefan,

        habs gestern auch schon gefunden. Funktioniert einwandfrei diese Lösung. Will eine Weichensteuerung für Sohnemann bauen mit Touchdisplay zum schalten der Weichen.

        Gruß
        Markus

          1. Hi Stefan,

            vielleicht magst Du Deinen SUPER Artikel ja ergänzen! Es können bis zu 62?? Boards a 16 Servos angesprochen werden.

            Ich habe nun testweise nur zwei genommen und A0 gebrückt.
            Anbei der Testsketch:

            #include
            #include

            Adafruit_PWMServoDriver board1 = Adafruit_PWMServoDriver(0x40);
            Adafruit_PWMServoDriver board2 = Adafruit_PWMServoDriver(0x41);

            int board1gerade[]={150,160,170,180,190,200,210,220,230,240,250,260,270,280,290,300}; //Min Position Servos Board1
            int board1abzw[]={500,490,480,470,460,450,430,420,410,400,390,380,370,360,350,340}; //Max Position Servos Board1

            int board2gerade[]={150,160,170,180,190,200,210,220,230,240,250,260,270,280,290,300}; //Min Position Servos Board2
            int board2abzw[]={500,490,480,470,460,450,430,420,410,400,390,380,370,360,350,340}; //Max Position Servos Board2

            #define SERVOMIN 150
            #define SERVOMAX 600

            int numberOfServos = 16;

            void setup() {
            board1.begin();
            board2.begin();
            board1.setPWMFreq(60);
            board2.setPWMFreq(60);
            delay(10);
            }

            void loop() {
            for (int i=0; i<numberOfServos; i++){ //Servos auf im Array vorgegebene Stellung (gerade) setzen
            board1.setPWM(i, 0, board1gerade[i]);
            }
            delay(500);

            for (int i=0; i<numberOfServos; i++){ //Servos auf im Array vorgegebene Stellung (abzw) setzen
            board1.setPWM(i, 0, board1abzw[i]);
            }
            delay(500);

            for (int i=0; i<numberOfServos; i++){ //Servos auf im Array vorgegebene Stellung (gerade) setzen
            board2.setPWM(i, 0, board2gerade[i]);
            }
            delay(500);

            for (int i=0; i<numberOfServos; i++){ //Servos auf im Array vorgegebene Stellung (abzw) setzen
            board2.setPWM(i, 0, board2abzw[i]);
            }
            }

            Nochmals besten Dank und Gruß
            Markus

          2. Hi Markus,

            wow! Echt cool. Vielen Dank dafür :-)
            Ich hoffe ich komme am Wochenende dazu, den Code mit einzubinden. Das wird bestimmt vielen Leuten helfen!

            Liebe Grüße

            Stefan

          3. So, erledigt. Noch mal lieben Dank Markus. War echt super, dass du mir den Code geschickt hast :-)

  3. Funktioniert das ganze auch mit einem ANGEEK PCA9685? Und wenn ja worauf muss ich achten? Also gibt es dann eine andere Programmbibliothek? Und wenn ja wo bekomme ich die her?

    1. Hi Patrick, also das sollte (eigentlich) genau so funktionieren. Bei so Open-Hardware-Sachen gibt es immer eine große Anzahl von Herstellern für exakt das gleiche Modul. Probiert hab ich es mit genau diesem Teil aber noch nicht.

Datenschutz
Stefan Hermann, Besitzer: Stefan Hermann (Firmensitz: Deutschland), würde gerne mit externen Diensten personenbezogene Daten verarbeiten. Dies ist für die Nutzung der Website nicht notwendig, ermöglicht aber eine noch engere Interaktion mit Ihnen. Falls gewünscht, treffen Sie bitte eine Auswahl:
Datenschutz
Stefan Hermann, Besitzer: Stefan Hermann (Firmensitz: Deutschland), würde gerne mit externen Diensten personenbezogene Daten verarbeiten. Dies ist für die Nutzung der Website nicht notwendig, ermöglicht aber eine noch engere Interaktion mit Ihnen. Falls gewünscht, treffen Sie bitte eine Auswahl: