Servoansteuerung

Hallo,

ich habe ein Problem meine Servos anzusteuern. Ich verwende ein Uno mit einem Servoshield.
Das Ansteuern eines einzelnen Servos funktioniert anstandslos. Verwende ich mehrere gleichzeitig, werden die Servos irgendwann durcheinander angesteuert und alles steht.

Hier ist mein Code:
Ich habe jede Menge Println drin, damit ich feststellen kann wo es hackt. Doch dazu kommt es nicht.

  #include <Wire.h>
  #include <Adafruit_PWMServoDriver.h>
  
  Adafruit_PWMServoDriver minutenservos = Adafruit_PWMServoDriver(0x41);
  //Adafruit_PWMServoDriver stundenservos = Adafruit_PWMServoDriver(0x42);
  int i; //zähler für den em-zahlen
  int em_zahl;
  
  
  void setup() {
      Serial.begin(9600);
      Serial.println("Setup");
    pinMode(13, OUTPUT);
    minutenservos.begin();
    minutenservos.setPWMFreq(80);
  Wire.begin();
  em(0);
  
  i=0;
  }
  
  void loop() {
    Serial.println("loop");
    Serial.print("stellen zahl :");
    Serial.print(i);
    Serial.println();
    em(i);
  
  warten(3000);
  i=i+1;
      Serial.println(i);
    if (i==3){i=0;}
  }
  
  void em(int emz){
    switch (emz) {
      case 0:
  Wire.beginTransmission(0x41);
    minutenservos.setPWM(0, 0, 300);//zu
    Serial.print("0 zu");
    Serial.println();
   warten(50);
    minutenservos.setPWM(1, 0, 300);//zu
    Serial.print("1 zu");
     Serial.println();
   warten(50);
    minutenservos.setPWM(2, 0, 200);//zu
    Serial.print("2 zu");
     Serial.println();
  warten(50);
    minutenservos.setPWM(3, 0, 300);//zu
    Serial.print("3 zu");
     Serial.println();
     warten(50);
    minutenservos.setPWM(4, 0, 300);//zu
    Serial.print("4 zu");
     Serial.println();
     warten(50);
    minutenservos.setPWM(5, 0, 220);//zu
    Serial.print("5 zu");
    Serial.println();
     warten(50);
    minutenservos.setPWM(6, 0, 550);//auf
    Serial.print("6 auf");
     Serial.println();
     warten(50);
      Wire.endTransmission();
  break;
  case 1:
  Wire.beginTransmission(0x41);
    minutenservos.setPWM(0, 0, 600);//auf
    Serial.print("0 auf");
     Serial.println();
     warten(50);
    minutenservos.setPWM(3, 0, 550);//auf
    Serial.print("3 auf");
     Serial.println();
     warten(50);
    minutenservos.setPWM(4, 0,550);//auf
    Serial.print("4 auf");
     Serial.println();
     warten(50);
    minutenservos.setPWM(5, 0, 550);//auf
    Serial.print("5 auf");
     Serial.println();
     warten(50);
        Wire.endTransmission();
   break;
   case 2:
  Wire.beginTransmission(0x41);
    minutenservos.setPWM(0, 0, 300);//zu
    Serial.print("0 zu");
     Serial.println();
     warten(50);
    minutenservos.setPWM(2, 0, 530);//auf
    Serial.print("2 auf");
     Serial.println();
     warten(50);
    minutenservos.setPWM(3, 0, 300);//zu
    Serial.print("3 zu");
     Serial.println();
     warten(50);
    minutenservos.setPWM(4, 0, 300);//zu
    Serial.print("4 zu");
     Serial.println();
     warten(50);
    minutenservos.setPWM(6, 0, 150);//zu
    Serial.print("6 zu");
     Serial.println();
     warten(50);
     Wire.endTransmission();
  break;
    }
  }
  
  void warten(int zeit){
    delay(zeit);
  }

Hallo,
kann es auf Grund der Zeit sein zwischen

Wire.beginTransmission(0x41);

und

Wire.endTransmission();

nimm mal nur zwei Antriebe, das was geht und mach dann die delay mal viel länger.

die Befehle

und

werden überhaupt nicht benötigt.
Die library macht die Details der Wire = I2C-Datenübertragung alle selbst.
Die Befehle dürften der Library dazwischen funken und genau das verursacht dann das komische Verhalten.

Das ist der Demo-code vom Hersteller

/*************************************************** 
  This is an example for our Adafruit 16-channel PWM & Servo driver
  Servo test - this will drive 8 servos, one after the other on the
  first 8 pins of the PCA9685

  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/products/815
  
  These drivers use I2C to communicate, 2 pins are required to  
  interface.

  Adafruit invests time and resources providing this open source code, 
  please support Adafruit and open-source hardware by purchasing 
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.  
  BSD license, all text above must be included in any redistribution
 ****************************************************/

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

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);
// you can also call it with a different address and I2C interface
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire);

// 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)
#define SERVOMAX  600 // This is the 'maximum' pulse length count (out of 4096)
#define USMIN  600 // This is the rounded 'minimum' microsecond length based on the minimum pulse of 150
#define USMAX  2400 // This is the rounded 'maximum' microsecond length based on the maximum pulse of 600
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates

// our servo # counter
uint8_t servonum = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("8 channel Servo test!");

  pwm.begin();
  /*
   * In theory the internal oscillator (clock) is 25MHz but it really isn't
   * that precise. You can 'calibrate' this by tweaking this number until
   * you get the PWM update frequency you're expecting!
   * The int.osc. for the PCA9685 chip is a range between about 23-27MHz and
   * is used for calculating things like writeMicroseconds()
   * Analog servos run at ~50 Hz updates, It is importaint to use an
   * oscilloscope in setting the int.osc frequency for the I2C PCA9685 chip.
   * 1) Attach the oscilloscope to one of the PWM signal pins and ground on
   *    the I2C PCA9685 chip you are setting the value for.
   * 2) Adjust setOscillatorFrequency() until the PWM update frequency is the
   *    expected value (50Hz for most ESCs)
   * Setting the value here is specific to each individual I2C PCA9685 chip and
   * affects the calculations for the PWM update frequency. 
   * Failure to correctly set the int.osc value will cause unexpected PWM results
   */
  pwm.setOscillatorFrequency(27000000);
  pwm.setPWMFreq(SERVO_FREQ);  // Analog servos run at ~50 Hz updates

  delay(10);
}

// You can use this function if you'd like to set the pulse length in seconds
// e.g. setServoPulse(0, 0.001) is a ~1 millisecond pulse width. It's not precise!
void setServoPulse(uint8_t n, double pulse) {
  double pulselength;
  
  pulselength = 1000000;   // 1,000,000 us per second
  pulselength /= SERVO_FREQ;   // Analog servos run at ~60 Hz updates
  Serial.print(pulselength); Serial.println(" us per period"); 
  pulselength /= 4096;  // 12 bits of resolution
  Serial.print(pulselength); Serial.println(" us per bit"); 
  pulse *= 1000000;  // convert input seconds to us
  pulse /= pulselength;
  Serial.println(pulse);
  pwm.setPWM(n, 0, pulse);
}

void loop() {
  // Drive each servo one at a time using setPWM()
  Serial.println(servonum);
  for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {
    pwm.setPWM(servonum, 0, pulselen);
  }

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

  delay(500);

  // Drive each servo one at a time using writeMicroseconds(), it's not precise due to calculation rounding!
  // The writeMicroseconds() function is used to mimic the Arduino Servo library writeMicroseconds() behavior. 
  for (uint16_t microsec = USMIN; microsec < USMAX; microsec++) {
    pwm.writeMicroseconds(servonum, microsec);
  }

  delay(500);
  for (uint16_t microsec = USMAX; microsec > USMIN; microsec--) {
    pwm.writeMicroseconds(servonum, microsec);
  }

  delay(500);

  servonum++;
  if (servonum > 7) servonum = 0; // Testing the first 8 servo channels
}

Da sind null wire.xxxx-Aufrufe im Sketch weil die alle intern in der library gemacht werden

Wenn man nicht mit der default-adresse 0x40 arbeitet dann sieht der Aufruf so aus

Adafruit_PWMServoDriver minutenservos = Adafruit_PWMServoDriver(0x41, Wire);

Adafruit_PWMServoDriver(0x41, Wire);

Damit weiß die library verwende dieses I2C-interface und das reicht.

https://raw.githubusercontent.com/adafruit/Adafruit-PWM-Servo-Driver-Library/master/examples/servo/servo.ino

vgs

Vielen Dank für die Antworten. Ich werde die Tipps bei nächster Gelegenheit umsetzen.

Hallo,

da bin ich wieder. Die Tipps haben leider nichts gebracht. Beim Testen habe ich festgestellt, dass das gleichzeitige Bewegen von 3 Servos funktioniert. Bei 4 oder mehr geht nichts mehr. Nummer 4 wird gerade noch bewegt, Nummer 5 hängt und danach geht alles durcheinander. Ich habe schon vor jedem bewegen Wire.begin(); und Wire.beginTransmission(0x41); gesetzt. Nach jedem bewegen eines Servos kommt dementsprechend Wire.end(); , Wire.endTransmission(0x41); und ein delay(1000);. Selbst das brachte nicht die Lösung.
Hintergrund des ganzen ist der, dass ich eine digitale Uhr ( 4 7-Segment-Anzeigen) mit den Servos realisieren möchte. Dabei benutze ich 2 16-Kanal Servo-Shields. Bei 28 Servos, kann ich diese nicht an einen UNO anschließen.

Hallo,
das beginTransmission und das endTransmission wird doch alles nicht benötigt. das macht doch die Lib intern.

Lese nochmal #3 und schau Dir das Beispiel an
Heinz

Wie versorgst du die Servos mit Strom? Aus dem Arduino heraus?

Hallo,

es macht, so habe ich festgestellt, keinen Unterschied ob die 5 V USB Spannung oder 12 Volt über die Buchse anliegen.

Wenn du alle Servos aus dem Arduino versorgst, wird der 5V des Arduinos überlastet, deswegen geht das in die Hose. Nimm für die Servos eine externe Spannungsversorgung.

Hallo,

selbst wenn ich 12 Volt an die Buchse des UNO anlege, könnte es diese Probleme geben? :roll_eyes:

Ja.
Der Regler auf dem Arduino "verbrennt" alles zwischen 5V und 12V als Wärme und seine Leistungsfähigkeit ist sehr eingeschränkt...
Versorge die Servos direkt aus einem 5V-Netzteil.
+ vom Servo mit +5V vom Netzteil
-/GND vom Servo mit GND vom Netzteil UND GND vom Arduino
PWM vom Servo mit dem PIN vom Arduino.

Hallo.
OK! Dann besorge ich mir eine externe Spannungsversorgung.
Ich halte Euch auf dem Laufenden.

Irgendwo ein kaputtes USB-Kabel? Stecker B abschneiden. Kabel aufdröseln. Und ein USB-Netzgerät (Kein Ladegerät) und gut....

Je höher die Eingangsspannung am Arduino-Board desto geringer die mögliche Stromentnahme.
Das hört sich erst einmal an als ob es umgekehrt sein müsste.

Der Hintergrund ist: Der µCpntroller braucht nur 5V. Die überschüssige Spannung wird vom Spannungsregler in Wärme umgewandelt.
12V - 5V = 7V die in Wärme umgewandelt werden müssen.

Um es präziser zu beschreiben: Wärmeleistung = Spannung mal Strom
Arduino mit einer LED deren Vorwiderstand so groß gewählt ist das nur 2mA durch die LED fließen
7V * 0.002A = 0,014 W = 14 mW Wärmeleistung. Das ist kein Problem für den Spannungsregler

Ein Servo 50 mA
7 V * 0.050A = 0,35W = 350 mW. Das mag noch gehen.
Jetzt 4 Servos
7V * 0.2A = 1,4W = 1400 mW da droht der Spannungsregler zu überhitzen und regelt ab
=> Reset des Arduinos

Am Eingang nur 7V
7V - 5V = 2V
2V * 0.2A = 0,4W = 400 mW. Das kann der Spannungsregler wahrscheinlich noch vertragen.

Deshalb führt eine höhere Eingangsspannung zu niedrigeren Strömen die noch iO sind.

Der Mikrocontroller selbst braucht natürlich auch Strom. Ca 50 mA
Der Atmega 328 kann maximal 200 mA.
Das Maximum was man aus dem 5V-Pin ziehen kann sind dann vorsichtig geschätzt ca.
300 mA Gesamt - 200 mA Eigenbedarf = 100 mA.
Deshalb Servos immer so versorgen

gar nicht

aus dem Arduino5V pin sondern direkt von einer 5V Stromversorgung

Das wird immer wieder thematisiert.
800mA wirst Du niemals - auch nicht bei VIN 7V - dauerhaft fehlerfrei rausbekommen.
2V * 800mA = 1,6W -> Dafür ist die Kühlfläche nicht ausgelegt.
Uwe hat das sehr gut erklärt.

Ich finde es nicht besonders gut erklärt. Man muss mehrere Postings durchlesen um dann nur zu wissen "am besten nimmt man den Onboard-Regler gar nicht"
Tja und jetzt traue dich mal einen Zahlenwert in mA festzulegen
Meinetwegen einen für 12V und einen für 7V

Ich schon.

Na rechne doch mal selbst, wieviel Leistung auf der Kühlfläche umgesetzt werden kann?
Kannst Du nicht?
Da ich Deine Kühlfläche nicht habe - und auch nicht die von x-beliebigen Clonen - sowie der Regler auch noch ggfls. etwas anderes ist, als das Original, ist die Aufgabenstellung unlösbar.
Wenn Du nur von Deiner Fehlinformation ablenken willst...

Deine 800mA sind definitiv unbrauchbar.

Das wage ich zu bezweifeln. Wenn der Motor des Servos sich bewegt oder seine Position gegen eine Drehmoment hält braucht er bedeutend mehr Strom. Der Strombedarf eines Servos ist (fast) nie in den Datenblättern vermerkt. Ich rechne aber je nach Moment (Kraft) am Servoarm von 100 bis 1000mA.

Grüße Uwe

Wire.begin(); Wire.beginTransmission(0x41);`

weglassen

nicht hinzufügen!

Hallo Zusammen,

Ihr hattet Recht mit der externen Spannungsversorgung. Die Servos laufen durch. Ich kann mich jetzt weiter mit der Programmierung und dem Ablauf durchquälen.

1 Like