Tutorial · Mittelstufe · 18 Min. Lesezeit
Servos mit dem PCA9685 ansteuern (I²C-PWM-Treiber)
Viele Servos mit einem PCA9685 über I²C treiben: PWM-Frequenz setzen, einen Winkel in kalibrierte Impulsbreite verwandeln und die Servos sicher versorgen.
Ein Mikrocontroller hat nur eine Handvoll Hardware-PWM-Kanäle. Für ein, zwei Servos reicht das – doch ein Roboterarm hat vier, ein Hexapode achtzehn, und dem Arduino gehen die Timer aus, lange bevor dir die Gelenke ausgehen. Der PCA9685 löst das: Eine kleine Platine erzeugt sechzehn unabhängige PWM-Signale in Hardware, und alle befiehlt man über nur zwei I²C-Drähte. So fährt man jedes Gelenk des Roboterarmsimulators auf echter Hardware.
Warum die Servos nicht einfach an den Arduino verdrahten?
Zwei Gründe. Erstens Pins: Dem Uno reichen die Timer für wenige Servo-Objekte, bevor sich die Bibliothek selbst im Weg steht. Zweitens, und wichtiger, Leistung: Ein Servo kann blockiert 500–700 mA ziehen, und vier gemeinsam fahrende mehrere Ampere. Dieser Strom darf nicht aus dem 5-V-Pin des Arduino kommen – er lässt das Board per Brownout zurücksetzen, mitten in der Bewegung. Der PCA9685 behebt beides: Er lagert die PWM-Erzeugung auf einen eigenen Chip aus und führt Servostrom aus seiner eigenen V+-Klemme – hoher Strom bleibt damit vollständig von deiner Logik fern.
Die PWM-Frequenz setzen
Jedes Hobbyservo erwartet ein 50-Hz-Steuersignal – einen Impuls alle 20 ms. Der PCA9685 setzt eine Frequenz für den ganzen Chip – also einmal in setup() setzen:
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40); // default I²C address
void setup() {
pwm.begin();
pwm.setPWMFreq(50); // 50 Hz — the standard servo refresh rate
}
Alle sechzehn Kanäle pulsen nun mit 50 Hz. Welchen Winkel jedes Servo hält, setzt die Breite seines Impulses, nicht die Frequenz – das steuert man pro Kanal.
Winkel zu Impulsbreite – die Kalibrierung, auf die es ankommt
Ein Servo liest die Impulsbreite als Positionsbefehl: grob 1 ms für 0°, 1,5 ms für Mitte, 2 ms für 180°, fünfzigmal pro Sekunde wiederholt. Das ist derselbe Impulsbreite-als-Winkel-Code, den der SG90 nutzt; der PCA9685 erzeugt ihn nur in Hardware.
Der PCA9685 nimmt Millisekunden nicht direkt. Er teilt jede 20-ms-Periode in 4096 Zählwerte (12-Bit-Auflösung) und man sagt ihm, wie viele Zählwerte der Impuls high bleibt. Bei 50 Hz sind eine volle Periode 4096 Zählwerte = 20 ms, also:
- 1 ms ≈ 205 Zählwerte (0°)
- 1,5 ms ≈ 307 Zählwerte (90°)
- 2 ms ≈ 410 Zählwerte (180°)
Das sind die Nennwerte. Echte Servos streuen – dasselbe SG90 kann bei 130 unten anschlagen und bei 490 oben. Das ist die eine Zahl, die man kalibrieren muss. Pro Servo Min und Max speichern und den Winkel in diesen Bereich mappen:
// Calibrate these two per servo by eye — see "Gotchas" below.
const int SERVO_MIN = 130; // counts at the servo's 0° hard stop
const int SERVO_MAX = 490; // counts at the servo's 180° hard stop
int angleToCount(int deg) {
return map(deg, 0, 180, SERVO_MIN, SERVO_MAX);
}
Kalibrierung auszulassen ist der Grund, warum ein „90°“-Befehl an einem Gelenk bei 78° und am anderen bei 96° landet – und warum ein Arm auf unkalibrierten Servos die Pose nie ganz erreicht, die der Inverskinematik-Löser verlangte.
Verdrahtung: zwei Stromschienen, eine Masse
- Logik –
VCC,GND,SDA,SCLvom Arduino. Das versorgt nur den Chip. - Servostrom – eine separate 5–6-V-Versorgung an die V+-Schraubklemme, dimensioniert auf den Blockierstrom aller Servos zugleich, nicht ihren Leerlauf.
- Gemeinsame Masse – die Versorgungsmasse und die Arduinomasse müssen verbunden sein, sonst hat das PWM-Signal keine Referenz und die Servos zucken oder ignorieren dich. Dasselbe gilt für die I²C-Leitungen, die die Befehle tragen.
Einen Kondensator über V+ legen, der die Stromspitze beim Servoanlauf schluckt; das PCA9685-Breakout hat genau dafür eine Bestückfläche.
Der Code: ein Gelenk bewegen
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);
const int SERVO_MIN = 130;
const int SERVO_MAX = 490;
int angleToCount(int deg) {
return map(deg, 0, 180, SERVO_MIN, SERVO_MAX);
}
void setServo(uint8_t channel, int deg) {
pwm.setPWM(channel, 0, angleToCount(deg)); // on at count 0, off at the angle count
}
void setup() {
pwm.begin();
pwm.setPWMFreq(50);
setServo(0, 90); // base to centre
setServo(1, 45); // shoulder
setServo(2, 120); // elbow
setServo(3, 10); // gripper open
}
void loop() {}
Jedes setPWM(Kanal, 0, Zählwert) sagt: „Diesen Kanal am Periodenbeginn einschalten und nach Zählwert Ticks ausschalten.“ Ein Gelenk durchfahren, indem man den Winkel in einer Schleife mit kurzem delay() zwischen Schritten erhöht – damit das Servo Zeit zum Fahren hat statt zu springen.
Fallstricke
SERVO_MIN/SERVO_MAXpro Servo kalibrieren. 0° befehlen, notieren, wo das Horn wirklich sitzt, und den Zählwert nachführen, bis es stimmt. Dasselbe bei 180°. Zehn Minuten hier retten jede nachgelagerte Pose.- V+ nie aus dem Arduino treiben. Ein Servo mag es überleben; vier setzen das Board per Brownout zurück.
- Die Versorgung auf Blockieren auslegen, nicht Leerlauf. Eine unterdimensionierte Versorgung sackt, wenn mehrere Servos gemeinsam fahren, und das ganze Board zittert.
- Die Adresse prüfen. Standard ist
0x40. Die Lötjumper brücken, um Platinen zu verketten, und die neue Adresse dem Konstruktor übergeben. - Sanft bewegen. Zum Ziel schreiten schlägt springen – es senkt die Stromspitze und ist freundlicher zu Kunststoffzahnrädern.
- Der Greiferkanal ist der, der gart. Jedes andere Gelenk erreicht seinen Winkel und ruht; ein Greifer, der ein Objekt hält, ist ein dagegen blockiertes Servo, das dauerhaft etwa ein halbes Ampere zieht. Öffnen, sobald das Teil abgelegt ist – und siehe Servogreiferkraft, Blockieren und Nachgiebigkeit, warum feinere PWM-Auflösung keine feinere Kraft kauft.
Antwortet jedes Gelenk erst auf einen Winkel, ist der Arm bereit für die Geometrie: aus einem Zielpunkt die Gelenkwinkel machen, die ihn erreichen – genau wofür Inverskinematik eines Zweigelenkarms und der Roboterarmsimulator da sind.
Den Graphen erkunden
Teil dieser Bauprojekte
Projekte und Lernpfade, die dieses Tutorial enthalten.
Weiterführende Literatur