Roboter programmieren mit Arduino (Teil 2: Antriebstechnik]

 

 

Zu den grundlegenden Funktionen eines jeden Roboters gehört die Fortbewegung bzw. Bewegungen überhaupt. Beschränken wir uns auf die elektrische Antriebstechnik, so sind es vor allem die folgenden 4 Technologien, die dafür zum Einsatz kommen:

 

  • DC-Motoren mit Kohlenbürsten
  • bürstenlose DC-Motoren
  • Servo-Motoren
  • Schrittmotoren

Die bürstenlosen DC-Motoren sind korrekterweise überhaupt keine Gleichstrommotoren, sondern Drehstromantriebe. Die Gleichspannung wird dabei in 3 phasenverschobene Wechselspannungen zerlegt. Intern haben wir es also mit einem gewöhnlichen Drehstrommotor zu tun, der nach außen wie ein Gleichstromtyp wirkt. Er benötigt ganz spezielle Motorcontroller. In diesem Tutorial werden wir uns auf die reinen DC-Motoren mit Kohlenbürsten und die Servoantriebe konzentrieren, weil diese im Böcker RoverROM-B3000 zum Einsatz kommen. In späteren Tutorial gehen wir dann auch auf die beiden anderen Technologien ein.

 

Gleichstrom-Motoren

Die reinen DC-Motoren sind als Antrieb besonders gut geeignet, weil ihre Drehzahl einfach über die Spannung eingestellt werden kann. Bei Drehstrommotoren kann dies nur über eine Änderung der Frequenz des Drehfeldes erreicht werden, was sehr aufwendig ist. Da die Drehzahlen meist im Bereich von mehreren 1000 Umdrehunge pro Minute liegen, werden DC-Antriebsmotoren in der Robotik in der Regel als Getriebemotor ausgeführt. Typische Übersetzungsverhältnisse sind 1:50 bis 1:100 mit denen sich Nutzdrehzahlen von ca. 100 bis 200 Umdrehungen erzielen lassen. Durch die recht starke Übersetzung erhöht sich natürlich auch das Drehmoment entscheidend. 

 

Getriebemotor

 

Doch so einfach die Steuerung per Spannungseinstellung auch sein mag, wie lässt sich das mit einem Mikrocontroller erledigen? An den digitalen Ausgängen liegt ja entweder nur ein High-Pegel (in der Regel 5 oder 3,3 V) oder ein Low-Pegel mit 0 Volt. Einen integrierten D/A-Wandler mit analogen Ausgängen hat der Atmega328 ja nicht (und auch dann wäre noch eine Verstärkung und Stromwendung notwendig). 

Doch es gibt einen recht simplen Trick, ein Verfahren, das wir Pulsweitenmodulation (PWM) nennen. Dabei geht man davon aus, dass viele elektrische Verbraucher so träge sind, das ein schnelles Ein- und Ausschalten nicht erkennbar ist. Das Ganze lässt sich am an einem Heizdraht erläutern.

 

PWM_1

 

Im Bild sehen Sie den Spannungsausgang des Controllers (blau) und die Temperatur, die sich am Heizelement einstellt. Während der Einschaltphase steigt die Temperatur an. Der Anstieg ist hier linear dargestellt, was in der Praxis nicht der Fall ist, aber zur Veranschaulichung ist diese Vereinfachung akzeptabel. In der Ausschaltphase sinkt die Temperatur wieder ab, allerdings nicht so stark wie im Anstieg, weil dort aktiv Energie hinzugefügt wird. In der 2. Anschaltphase steigt die Temperatur abermals, allerdings nicht mehr so stark, weil immer mehr Energie benötigt wird, um einen Anstieg zu erzielen. Nach einer Weile kommt es zum einem leichten Pendeln und wenn Sie wie im unteren Teil des Bild die Frequenz erhöhen, dann bleibt die Temperatur konstant. 

Im Falle unseres Mikrocontrollers wäre der Maximalwert der Spannung z. B. 5 V. Exakt die gleiche Temperatur wie oben könnte man auch erreichen, wenn man eine kleinere Gleichspannung anlegen würde, die allerdings nicht ein- und ausgeschaltet würde. Da das gepulste Rechtecksignal die gleiche Wirkung (Effekt) hat, wie die geringere Gleichspannung sprechen wir von Effektivwert. Sind Ein- und Ausschaltphasen gleich lang ist der Effektivwert 50 % des Maximalwertes also 2,5 V. 

 

PWM_2

 

Wir können nun durch die Variation des Tastverhältnisses jede beliebige Spannung zwischen 0 und dem Maximalwert einstellen, z. B. 3,75 V:

 

PWM_3

 

 

oder 0,5 V: 

 

 PWM_4

 

Jede beliebige Spannung ist allerdings nicht ganz korrekt, denn das hängt natürlich von der zeitlichen Auflösung ab. 

Softwaremäßig lässt sich ein PWM-Signal ja sehr einfach realisieren, Sie müssen lediglich den entsprechenden Anschluss für eine bestimmte Zeit ein- und wieder ausschalten. Doch das Ganze hat einen nicht unerheblichen Nachteil. Stellen Sie sich vor, Sie haben das folgende Programm:

loop()
{
   digitalWrite(PIN1, HIGH);
   delay(1);
   digitalWrite(PIN1, LOW);
   delay(1);
}

Wenn Sie damit zufrieden sind, lediglich an PIN1 ein Rechtecksignal auszugeben, wäre das Programm zufriedenstellend, aber in den meisten Fällen wollen Sie noch eine Reihe anderer Dinge erledigen. Stellen Sie sich vor, Sie würden das Programm erweitern, um einen 2. Pin anzusteuern. Die beiden Rechtecksignal nutzen Sie nun für die Anteuerung von zwei Motoren. Sie wollen aber auch noch diverse Sensoren abfragen. Wo tun Sie das? Wenn Sie es am Ende der loop-Schleife einfügen, verlängert sich die 2. Wartezeit je nachdem wie lang Ihr Programm ist. Damit ändert sich aber auch das Tastverhältnis und somit die Spannung am Motor. Kommt es gar zu längeren Abfragen oder Berechnungen, könnte der Motor stehen bleiben oder auf maximale Geschwindigkeit auflaufen, je nachdem wo sich der längere Programmteil befindet. 

loop()
{
   digitalWrite(PIN1, HIGH);
   delay(1);
   digitalWrite(PIN1, LOW);
   delay(1);
   weitere_Funktionen(); //Programmteile an dieser Stelle reduzieren die Spannung
}
 
loop()
{
   digitalWrite(PIN1, HIGH);
   delay(1);
   weitere_Funktionen(); //Programmteile an dieser Stelle erhöhen die Spannung
   digitalWrite(PIN1, LOW);
   delay(1);
}
 
Genau aus diesem Grund verfügen die meisten Mikrocontroller über sogenannte I/O-Anschlüsse mit PWM-Funktion. Dabei handelt es sich um vollkommen eigeständige Funktionen, die praktisch parallel zu Ihrem Programm ablaufen. Sie konfigurieren dabei zu Programmbeginn einige Register im Controller und lassen dann den PWM-Timer kontinuierlich laufen. Lediglich, wenn Sie Änderungen vornehmen wollen, programmieren Sie ihn kurz um und er läuft mit den neuen Werten weiter. Wenn Sie ganz genau wissen wollen, wie das auch ohne Arduino abläuft, empfehle ich Ihnen unseren Video Mikrocontroller-Lehrgang MC1 Aber momentan arbeiten wir ja mit Arduino-Unterstützung und da ist das Ganze total einfach, denn die Arduino-Funktion:
 
analogWrite(PIN1, value); 
 
nimmt uns die ganze Konfiguration ab. Wenn Sie sich z. B. mit dem Atmega328 oder ähnlichen Bausteinen auskennen und schon einmal einen Arduino-Sketch geschrieben haben, werden Sie sich vielleicht gewundert haben, da der Controller ja keine analogen Ausgänge besitzt. Genau hier kommt unser PWM-Verfahren zum Einsatz. Sie müssen als Parameter nur den Anschluss-Pin und den Wert zwischen 0 und 255 eingeben. 
 

Tipp: Wenn es Sie interessiert, wie die Arduino-Funktion den PWM-Ausgang programmiert, können Sie sich das in der Quelldatei "wiring_analog.c" anschauen. Sie finden alle Arduino-Funktionen im Verzeichnis:

arduino_verzeichnis\hardware\arduino\cores\arduino

 

Motorsteuerung des RoverROM-B3000

 

Schauen wir uns jetzt aber konkret an, wie die Antriebe im RoverROM-B3000 gesteuert werden. Dazu werden wir ein kleines Programm schreiben, das unseren RoverROM-B3000 einfach vor und zurück fahren lässt. Wir haben 2 Motoren: rechts (M1) und links (M2). Für jeden Motor benötigen wir zwei Steueranschlüsse:

Richtung und Gechwindigkeit. Im Programmcode sieht das folgendermaßen aus:

 

Motor_einfach_1 

 

Der Anschluss "dirM1" liegt in unserem Fall an Pin 4 und steuert die Richtung des rechten Motors, mit "speedM1" (Pin 5) stellen wir die Geschwindigkeit ein. Da wir die PWM-Funktion nutzen, können wir natürlich auch nur Anschlüsse für den Motorenantrieb einsetzen, die diese Funktion unterstützen. Analog gehen wir beim linken Motor vor. Dann definieren wir uns noch zwei Symbole "FORW" und "BACK", damit wir uns nicht immer daran erinnern müssen, ob nun 1 vorwärts oder rückwärts bedeutet.

Hinweis: Die Anschlüsse steuern natürlich nicht direkt die Motoren, sie steuern lediglich den integrierten Motorcontroller an, der die Signale verstärkt und auf den Motor leitet. 

Wie schon im 1. Teil erläutert, brauchen wir am Anfang unseres Programm die setup()-Funktion, in der wir z. B. die Anschlüsse konfigurieren. Da alle Motorenanschlüsse Ausgänge sind, sieht die setup()-Funktion bei uns folgendermaßen aus:

 

Motor_einfach_2

 

Wenn wir die Geschwindigkeit ändern wollen, müssen wir für beide Motoren neue Werte eingeben und evtl. auch noch die Richtung ändern. Bei häufigen Änderungen wird das Ganze schnell unübersichtlich, deshalb schreiben wir uns eine eigene Ansteuerungsfunktion, die wir "MotorControl" nennen und die wie folgt aufgebaut ist:

 

Motor_Control

 

Wir übergeben die 4 Parameter und setzen dann die entsprechenden Werte über die digitalWrite()- und analogWrite()-Funktionen. Durch diese Funktion reduzieren wir die Eingaben auf jeweils eine Zeile. Wir könnten die Funktion auch in eine eigene Bibliothek auslagern und dann über eine #include-Anweisung einbinden. 

Am Anfang unserer Programm müssen wir die Funktion noch bekannt machen, das tun wir durch den Funktionsprototypen:

 

Motor_einfach_3b

 

Und in der loop()-Schleife rufen wir dann die MotorControl-Funktion auf:

 

Motor_einfach_4

 

Der RoverROM B3000 fährt zunächst für 2 Sekunden mit mittlerer Geschwindigkeit (Werte von 0 bis 255)  nach vorn, stoppt für 2 Sekunden und fährt dann sehr langsam rückwärts u.s.w. Beim Geschwindigkeitswert ist noch zu beachten, dass ein Gleichstrommotor natürlich nicht bei Werten direkt über 0 anfährt und linear bis 255 schneller wird. In der Regel setzt er sich erst bei Werten von ca. 30 bis 40 in Bewegung. Das hängt sehr stark vom Ladezustand der Batterien bzw. Akkus ab.

 

Auch Servo-Motoren benötigen PWM-Funktionen

 

HS-422-600x600

 

Im Grunde handelt es sich bei DC-Motoren und Servo-Motoren nicht um unterschiedliche Technologien, sie unterscheiden sich nur durch die Beschaltung und Ansteuerung. Die reinen Antriebseinheiten bei einem Servo sind ebenfalls DC-Motoren. Meist werden Servos aber nicht für kontinuierliche Drehbewegungen eingesetzt, sondern lediglich zum Einstellen eines bestimmten Drehwinkels. Dabei gibt es Servos mit einem bestimmten Schwenkbereich (z. B. 90, 120, 180 aber auch 360 °) aber auch kontinuierlich drehende Typen. Letzere sind praktisch identisch mit den schon behandelten DC-Antrieben, benötigen aber ein anderes Steuersignal.

 

PWM_Servo

 

Im Gegensatz zum "normalen" PWM-Signal entscheidet hier die reale Impulsdauer. Liegt der Wert genau bei 1,5 ms so befindet sich der Servo im Ruhezustand bzw. der Mittelposition. Die Endwerte stellen die Pulslängen von 2 ms und 1 ms dar. Die Dauer Pulslänge + Leerzeit beträgt ca. 20 ms. Bei einem Servo mit 180 ° Schwenkbereich befindet sich der Antrieb bei einer Pulslänge von 1,5 ms genau bei 90 °, bei 2 ms sind es z. B. 0 ° und bei 1 ms 180 °. Bei kontinuierlich drehenden Servos wird die Geschwindigkeit durch die Pulslänge gesteuert. Ein Wert von 1,5 ms bedeutet Stillstand, bei einer 1 ms dreht der Motor mit maximaler Geschwindigkeit links bzw. rechts herum, bei 2 ms mit maximaler Geschwindigkeit in die Gegenrichtung. 

Servomotoren haben in der Regel einen 3-poligen Anschluss (+V, GND, Steuersignal). Im Gegensatz zur DC-Steuerung dient das Signal selbst nicht zur Energieversorgung des Motors. 

Um die Ansteuerung über ein PWM-Signal vorzunehmen können wir folgende Überlegungen anstellen:

Der komplette Zählbereich des PWM-Timers sind 20 ms, wählen wir einen 8-Bit-Timer würden diese 20 ms in 256 Schritten aufgelöst. Unser effektiver Steuerungsbereich liegt aber zwischen 1 und 2 ms, also nur 1 ms. Wenn uns für 20 ms 256 Schritte zur Verfügung stehen, können wir 1 ms lediglich in 12,8 Schritte unterteilen. Sie sollten also den 10-Bit- oder den 16-Bit-PWM-Timer im Atmega328 nutzen. Aber um diese Systemtiefen müssen wir uns in diesem Tutorial nicht kümmern, denn es geht ja um die Programmierung mit Arduino und dort gibt es eine eigene Bibliothek "Servo", die wir im Folgenden  nutzen. Wir werden einfach einen Wert über die serielle Schnittstelle einlesen und dem Servo unseres RoverROM-B3000 übergeben: 

 

Servo_seriell

 

Dazu binden wir die Bibliothek ein und instanziieren ein Objekt vom Typ "Servo". Desweiteren benötigen wir noch eine Variable "pos", in der wir die Position speichern. In der setup()-Funktion wird über die Objektmethode "attach" das Servosignal an den Pin 9 gelegt und außerdem die serielle Schnittstelle initialisiert. 

In der loop()-Funktion fragen wird mit Serial.read die serielle Schnittstelle ab, wenn ein "n" eingegeben wurde, weiß das Programm, das nun ein neuer Positionswert folgt und liest diesen über "Serial.ParseInt()" ein. Danach übergibt er ihn an die Methode "write" der Servo-Instanz. 

 

Laden Sie das Programm in Ihren RoverROM-B3000 und rufen Sie den "Serial Monitor" im Menü "Tools" auf. 

 

Serial_Monitor

 

Geben Sie jetzt unterschiedliche Werte zwischen 0 und 180 mit führendem "n" ein und der Servomotor des Abstandsscanners stellt sich genau in die gewünschte Richtung. 

 

Tutorial_Antrieb

 

Im nächsten Tutorial werden wir und das Thema Encoder genauer anschauen. 

 

Für weitere Informationen und Bestellungen des

RoverROM-B3000

einfach auf nachfolgenden Link klicken: Böcker RoverROM-B3000 Version 2

oder als Bausatz: Böcker RoverROM-B3000 Version 2 Bausatz

 

Sie wollen Arduino-Profi werden? Dann ist unser Video Mikrocontroller-Lehrgang MC1 genau das Richtige für Sie!

 

Falls Sie Fragen zum Thema haben, wenden Sie sich einfach an uns.

Wir helfen Ihnen gern weiter!

 

Ihr Team von Böcker Systemelektronik

 

Hinweis: Unser Tutorialangebot wird in unregelmäßigen Abständen erweitert. Wenn Sie sofort über eine Neuerscheinung informiert werden möchten, tragen Sie sich bitte hier in unsere Benachrichtigungsliste "Tutorials" ein. Sie können diesen unverbindlichen Service jederzeit in Ihrem Kundenkonto oder per E-Mail wieder abbestellen.  

 

 

Copyright © Böcker Systemelektronik