Tutorial · Gevorderd · 18 min lezen

Servo's aansturen met de PCA9685 (I²C PWM-driver)

Bestuur veel servo's met een PCA9685 over I²C: stel de PWM-frequentie in, zet een hoek om in een gekalibreerde pulsbreedte, en voed de servo's veilig.

Een microcontroller heeft maar een handvol hardware-PWM-kanalen. Dat is prima voor één of twee servo’s, maar een robotarm heeft er vier, een hexapod achttien, en de Arduino is allang door zijn timers heen voordat je door je gewrichten heen bent. De PCA9685 lost dat op: één klein bord genereert zestien onafhankelijke PWM-signalen in hardware, en je bestuurt ze allemaal over slechts twee I²C-draden. Zo bestuur je elk gewricht van de robotarmsimulator op echte hardware.

Schematische tekening van een PCA9685 16-kanaals PWM/servodriverbord: langs de bovenkant zestien driepins servoheaders, in het midden de PCA9685-chip, links een vijfpins I²C-besturingsheader, adres-soldeerjumpers, en rechts een V+-schroefterminal voor de servovoeding.
Zestien PWM-kanalen in hardware, bestuurd over twee I²C-pinnen; de servostroom komt uit de aparte V+-terminal, nooit uit de microcontroller. SVG downloaden

Waarom de servo’s niet gewoon aan de Arduino hangen?

Twee redenen. Ten eerste pinnen: de Uno heeft genoeg timers voor een paar Servo-objecten voordat de bibliotheek met zichzelf begint te vechten. Ten tweede, en dat is belangrijker, voeding: een servo kan 500–700 mA trekken als hij vastloopt, en vier tegelijk kunnen meerdere ampère trekken. Die stroom kan niet uit de 5 V-pin van de Arduino komen — dan zakt de spanning in en reset de print zichzelf midden in de beweging. De PCA9685 lost beide op. Hij legt de PWM-generatie neer in een eigen chip en voert de servostroom via zijn eigen V+-aansluiting, zodat de hoge stroom helemaal uit je logica blijft.

Stel de PWM-frequentie in

Elke hobby-servo verwacht een besturingssignaal van 50 Hz — één puls elke 20 ms. De PCA9685 stelt één frequentie in voor de hele chip, dus je zet hem één keer in setup():

#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 zestien kanalen pulseren nu op 50 Hz. De hoek die elke servo aanneemt wordt bepaald door de breedte van zijn puls, niet door de frequentie — en dat is het deel dat je per kanaal aanstuurt.

Hoek naar pulsbreedte — de kalibratie die ertoe doet

Een servo leest de pulsbreedte als een positiecommando: ongeveer 1 ms voor 0°, 1,5 ms voor het midden, 2 ms voor 180°, 50 keer per seconde herhaald. Dit is dezelfde code die pulsbreedte als hoek gebruikt zoals de SG90; de PCA9685 genereert hem alleen in hardware.

Besturingsschema van een servo: een 50 Hz PWM-signaal waarvan de pulsbreedte de servohoek bepaalt. Een puls van 1 milliseconde stuurt 0 graden aan, 1,5 milliseconde 90 graden, en 2 milliseconden 180 graden, weergegeven op gradenbogenschijven.
De servo leest de pulsbreedte, niet de spanning: 1 ms is 0°, 1,5 ms is het midden, 2 ms is de volle slag — 50 keer per seconde herhaald. SVG downloaden

De PCA9685 neemt geen milliseconden aan. Hij verdeelt elke periode van 20 ms in 4096 tellingen (12-bits resolutie), en jij geeft aan hoeveel tellingen de puls hoog blijft. Bij 50 Hz is één volledige periode 4096 tellingen = 20 ms, dus:

  • 1 ms ≈ 205 tellingen (0°)
  • 1,5 ms ≈ 307 tellingen (90°)
  • 2 ms ≈ 410 tellingen (180°)

Dat zijn de nominale waarden. Echte servo’s verschillen — dezelfde SG90 kan op 130 blijven staan en op 490. Dit is het getal dat je moet kalibreren. Bewaar per servo een minimum en een maximum en kaart je hoek daarin af:

// 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);
}

De kalibratie overslaan is de reden dat een commando van “90°” bij het ene gewricht op 78° uitkomt en bij het andere op 96° — en waarom een arm met ongekalibreerde servo’s nooit precies de pose haalt die de inverse-kinematische oplosser gevraagd heeft.

Bedrading: twee voedingslijnen, één massa

  • Logica — VCC, GND, SDA, SCL vanaf de Arduino. Dit voedt alleen de chip.
  • Servovoeding — een aparte 5–6 V-voeding op de V+-schroefterminal, gedimensioneerd op de vastloopstroom van alle servo’s tegelijk, niet op hun stationaire verbruik.
  • Gemeenschappelijke massa — de massa van de voeding en die van de Arduino moeten aan elkaar, anders heeft het PWM-signaal geen referentie en trillen de servo’s of negeren ze je. Hetzelfde geldt voor de I²C-lijnen die de commando’s dragen.

Zet een condensator over V+ om de stroompiek op te vangen die ontstaat zodra de servo’s gaan bewegen; de PCA9685-breakout heeft daarvoor precies een printvlak.

De code: een gewricht 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() {}

Elke setPWM(channel, 0, count) betekent: “zet dit kanaal aan aan het begin van de periode en uit na count ticks.” Stuur een gewricht door de hoek in een lus met een korte delay() tussen de stappen te doorlopen, zodat de servo tijd heeft om te bewegen in plaats van om te knallen.

Valkuilen

  • Kalibreer SERVO_MIN / SERVO_MAX per servo. Commandeer 0°, noteer waar de servohoorn werkelijk staat, en stel de telling bij tot het klopt. Doe hetzelfde bij 180°. Tien minuten hier bespaart elke pose die erop volgt.
  • Voed V+ nooit vanaf de Arduino. Eén servo overleeft het misschien; vier trekken de spanning eruit en resetten de print.
  • Dimensioneer de voeding op vastloopstroom, niet op stationair verbruik. Een te kleine voeding zakt weg zodra meerdere servo’s tegelijk bewegen, en de hele print gaat schudden.
  • Controleer het adres. De standaard is 0x40. Overbrug de soldeerjumpers om borden aan elkaar te koppelen, en geef het nieuwe adres aan de constructor.
  • Beweeg rustig. In stappen naar een doelpositie gaan is beter dan er direct naartoe knallen — dat verlaagt de stroompiek en is zachtzamer voor kunststof tandwielen.
  • Het grijperkanaal is het kanaal dat warm wordt. Elk ander gewricht bereikt zijn hoek en blijft staan; een grijper die een object vasthoudt is een servo die ertegenaan vastgelopen zit en doorlopend ongeveer een halve amp trekt. Open hem zodra het onderdeel is geplaatst, en zie kracht, vastloopstroom en meegaandheid van een servogrijper waarom fijnere PWM-resolutie je geen fijnere kracht oplevert.

Zodra elk gewricht op een hoek reageert, is de arm klaar voor de geometrie: een doelpositie omzetten in de gewrichtshoeken die het bereiken, en dat is precies waarvoor inverse kinematica van een twee-links arm en de robotarmsimulator bestaan.

Verken de graaf

Onderdeel van deze bouwprojecten

Projecten en leerroutes waarin deze tutorial voorkomt.

Verder lezen

Bronnen