Arduino Servos Animatronic PCA9685

Animatronic mit Arduino

Patrick vom Tussnat Studio entwickelt Animatronics und hat mir eine Anfrage geschickt. Grund genug, dass ich mich mit dem Thema beschäftigen möchte. Bei Animatronic handelt es sich um Figuren, die sich elektromechanisch bewegen lassen. Mit dem hier beschriebene Projekt kann man z. B. einen animatronischen Kopf oder Körper steuern. Insgesamt sind 16 Servo-Motoren ansteuerbar.

Bauteile

Schaltplan und Aufbau

Arduino Servos Animatronic PCA9685

Der Aufbau basiert auf dem PCA9685-Modul, das in der Lage ist, 16 Servo-Motoren per I2C-Schnittstelle zu steuern. Es ist mit dem SDA-Anschluss mit dem Arduino Pin A4 und mit dem SCL-Anschluss mit dem Pin A5 verbunden. Zusätzlich gibt es eine Verbindung mit dem GND und dem 5V+ vom Arduino zum VCC-Anschluss des PCA9685.


Sieh dir jetzt meinen neuen Arduino-Videokurs an: Jetzt ansehen!


Servo-Motoren benötigen relativ viel Strom. Um sie zu versorgen, ist eine externe Stromquelle nötig. Sie lässt sich unkompliziert an das PCA9685 anschließen.

Die Servo-Motoren werden dann einfach auf das PCA9685-Modul gesteckt. Die Reihenfolge der Servos muss man sich für das Programmieren merken.

Dieses Beispiel geht von der Steuerung eines Kopfes aus. Die Servo-Motoren repräsentieren also 2x Ohren, 2x Augenbrauen, 2x Augenlieder, 2x Augen Drehung X (rechts, links), 2x Augen Drehung Y (unten, oben), 2x Mundwinkel, 2x Mundöffnung, Kopf X (rechts, links) und Kopf Y (hoch, runter).

Es könnten aber genauso gut Körperteile, Arme, Füße oder alles Mögliche andere sein.

Arduino Code und Funktionsweise

Das Programm verwendet die Adafruit PWM Servo Driver Bibliothek. Sie lässt sich installieren, indem man über das Arduino-Menü das Fenster Sketch>Bibliothek einbinden>Bibliothek verwalten aufruft. Hier kann man einfach nach Adafruit PWM Servo Driver suchen und die Bibliothek in der aktuellen Variante installieren.

Der Code verwendet nur ein PCM-Modul. Es können aber sehr viele dieser Module in Reihe geschaltet werden.

Über die Variablen SERVOMIN und SERVOMAX lassen sich die minimalen und maximalen Servo-Stellwinkel anpassen. Das passiert hier für alle Servos gleichermaßen.

Da es sich in dem Beispiel um einen Kopf handelt, kann man sich die Zielwerte als Gesichtsausdruck vorstellen. Es gibt vier Gesichtsausdrücke, die im Array theExpressions[4][16] gespeichert werden. Die 4 steht für die Anzahl der Gesichtsausdrücke, die 16 für die Anzahl der Servo-Motoren, die am PCA9685 angeschlossen sind. Natürlich könnte man einfach mehr Gesichtsausdrücke anlegen. Jeder Eintrag definiert eine Zielposition eines Servomotors an dieser Stelle.

int theExpressions[4][16] = {
  {180, 20, 10, 90, 30, 120, 40, 80, 12, 0, 0, 0, 0, 0, 0, 0},  // fröhlich
  {30, 90, 100, 180, 150, 30, 10, 70, 150, 100, 0, 0, 0, 0, 0, 0},  // ärgerlich
  {90, 30, 60, 120, 80, 90, 120, 70, 10, 40, 90, 0, 0, 0, 0, 0},  // suspect R
  {120, 180, 150, 20, 30, 40, 110, 180, 90, 150, 0, 0, 0, 0, 0, 0}   // suspect L
};

Die Bewegungsgeschwindigkeit lässt sich im Array int theExpressionsDelays[4][16] einstellen. Jeder Servo kann für jeden Gesichtsausdruck unterschiedlich schnell bewegt werden. Je größer der Wert ist, desto langsamer bewegt sich der Servo.

int theExpressionsDelays[4][16] = {
  {5, 10, 3, 8, 2, 12, 9, 8, 20, 10, 0, 0, 0, 0, 0, 0},  // fröhlich
  {10, 1, 12, 2, 20, 10, 7, 9, 5, 7, 0, 0, 0, 0, 0, 0},  // ärgerlich
  {1, 25, 25, 12, 8, 4, 12, 2, 18, 6, 0, 0, 0, 0, 0, 0},  // suspect R
  {50, 5, 17, 25, 12, 30, 3, 15, 1, 12, 0, 0, 0, 0, 0, 0}   // suspect L
};

Im Loop wird alle zwei Sekunden exemplarisch ein zufälliger Gesichtsausdruck aufgerufen. Hier könnte man z. B. auch Taster zum Auslösen spezifischer Gesichtsausdrücke verwenden.

if (millis() % 2000 < 1) {
    targetExpression = updateExpression(random(4));
}

Hier der zusammenhängende Code

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

/* Mit diesem Programm lassen sich Bewegungsmuster mit 16 Servomotoren erzeugen,
   z. B. für einen Gesichtsausdruck von Animatronics. Genauso gut könnten es aber auch Positionen eines Roboterarms sein. */

// Adafruit Servo Library Einstellungen
Adafruit_PWMServoDriver module1 = Adafruit_PWMServoDriver(0x40);    // PCA9685 1
// Depending on your servo make, the pulse width min and max may vary, you
// want these to be as small/large as possible without hitting the hard stop
// for max range. You'll have to tweak them as necessary to match the servos you
// have!
#define SERVOMIN  150 // this is the 'minimum' pulse length count (out of 4096) -> Minimaler Servo-Winkel
#define SERVOMAX  600 // this is the 'maximum' pulse length count (out of 4096) -> Maximaler Servo-Winkel

/*
    Beispiel 1 für einen Animatronics-Kopf
 
    OhrR, OhrL
    AugenbraunR, AugenbraunL
    AugenliedR, AugenliedL
    AugeXR, AugeXL
    AugeYR, AugeYL
    MundwinkelR, MundwinkelL
    MundOeffnungR, MundOeffnungL
    KopfX, KopfY
*/

int servoNum = 16; // Anzahl der Servos

/* Das folgende Array kann zum Anlegen von Zielzuständen verwendet werden. Im Beispiel sind es vier Gesichtsausdrücke. Die Anzahl kann beliebig erweitert werden. */
int theExpressions[4][16] = {
  {180, 20, 10, 90, 30, 120, 40, 80, 12, 0, 0, 0, 0, 0, 0, 0},  // fröhlich
  {30, 90, 100, 180, 150, 30, 10, 70, 150, 100, 0, 0, 0, 0, 0, 0},  // ärgerlich
  {90, 30, 60, 120, 80, 90, 120, 70, 10, 40, 90, 0, 0, 0, 0, 0},  // suspect R
  {120, 180, 150, 20, 30, 40, 110, 180, 90, 150, 0, 0, 0, 0, 0, 0}   // suspect L
};

/* Dieses Array legt fest, wie schnell sich die einzelnen Servos zum jeweiligen Zielzustand bewegen sollen. Je größer der Wert, desto langsamer. */
int theExpressionsDelays[4][16] = {
  {5, 10, 3, 8, 2, 12, 9, 8, 20, 10, 0, 0, 0, 0, 0, 0},  // fröhlich
  {10, 1, 12, 2, 20, 10, 7, 9, 5, 7, 0, 0, 0, 0, 0, 0},  // ärgerlich
  {1, 25, 25, 12, 8, 4, 12, 2, 18, 6, 0, 0, 0, 0, 0, 0},  // suspect R
  {50, 5, 17, 25, 12, 30, 3, 15, 1, 12, 0, 0, 0, 0, 0, 0}   // suspect L
};

int targetExpression = 0; // der Ausdruck, der gezeigt werden soll, im Beispiel wird er per Zufall ausgewählt

/* Die folgenden Variablen müssen nicht angepasst werden */

int theCurrentPosition[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
long theCurrentTimer[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int theMovementDirection[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};

void setup() {
  module1.begin();
  module1.setPWMFreq(60);  // analoge Servos brauchen ~60 Hz
  delay(10);
  theCurrentTimer[0] = millis();
}

void loop() {
  updateServos(targetExpression);   // stellt die Servos um, muss bei jedem Druchlauf aufgerufen werden

  /* Die folgende Konstruktion setzt alle zwei Sekunden einen anderen Gesichtsausdrück.
    Hier könnte man z. B. auch vier Taster verwenden, um einen bestimmten Ausdruck auszulösen */
  if (millis() % 2000 < 1) {    // zeige alle zwei Sekunden per Zufall einen anderen Gesichtsausdruck
    targetExpression = updateExpression(random(4));
  }
}

int updateExpression(int theExpression) {
  for (int i = 0; i < servoNum; i++) {
    if (theCurrentPosition[i] > theExpressions[theExpression][i])  theMovementDirection[i] = -1;
    else  theMovementDirection[i] = 1;
    theCurrentTimer[i] = millis();
  }
  return theExpression;
}

void updateServos(int theExpression) {
  for (int i = 0; i < servoNum; i++) {
    if (millis() > theCurrentTimer[i] + theExpressionsDelays[theExpression][i]) {
      if (theCurrentPosition[i] != theExpressions[theExpression][i]) {
        theCurrentPosition[i] += theMovementDirection[i];
        module1.setPWM(i, 0, map(theCurrentPosition[i], 0, 180, SERVOMIN, SERVOMAX));

      }
      theCurrentTimer[i] = millis();
    }
  }
}


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

  • Die beliebtesten Arduino-Projekte von StartHardware
  • Inklusive Schaltplan, Beschreibung und Code
  • Arduino-Schnellstart-Kapitel
  • Kompakter Programmierkurs


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.