Roboter steuern mit ROBOprogy
Programmbeispiele für den ROBOprogy
in der Sprache FORTH
Die folgende Sammlung getesteter Programme wurde aus mehreren Gründen zusammengestellt:
Anfänger lernen, wie man überhaupt programmiert: Alles in überschaubare Einheiten zerlegen
Fortgeschrittene Programmierer finden vielleicht neue Ideen
Immer wieder benötigte Programme müssen nicht immer wieder neu „erfunden“ werden.
Alle Beispiele sind kommentiert, damit man nachvollziehen kann, was im ROBOprogy abläuft. Die Kommentare stehen entweder zwischen runden Klammern (Zwischenraum lassen!) oder folgen auf einen „Backslash“ \ , nach dem ebenfalls ein Zwischenraum eingefügt werden muss. Viel Spaß beim Nachvollziehen! Anregungen sind stets willkommen, ebenso die Bereitstellung weiterer Beispiele für eine Programmsammlung, die von allgemeinem Interesse sein könnte. eMail genügt.
Inhalt
Mehrere Programme im Speicher – eines davon starten.
------------------------------------------------------
Sollen Zeitspannen unter 1 ms erzeugt werden, bieten sich programmierte Verzögerungsschleifen an. Im folgenden Beispiel wird gezeigt, wie man den ROBOcontroller in eine Heulboje verwandeln kann. Der Lärmerzeuger ist im ROBOprogy mit dem Atmel MEGA32 an den Ausgang P3 angeschlossen und erzeugt je nach Pausenlänge zwischen P3ON und P3OFF unterschiedliche Frequenzen. Diese Pausenlänge kann mit folgender Funktion
: wart { n -- } 0 do loop ; \ Zählerendwert per DatenStack übergeben.
\ Der Befehl DO holt immer zwei Werte (Endwert und Startwert) vom DatenStack!
variiert werden: Diese Wiederholung macht nichts außer Zeit zu "verbrauchen": Ein Umlauf dauert recht genau 2,0 Mikrosekunden, es werden n Umläufe ausgeführt. Wegen der hohen Verarbeitungsgeschwindigkeit des ROBOcontrollers dauert die längstmögliche Verzögerung für
255 wart
nur 510 Mikrosekunden. Die tiefste so erzeugbare Schwingungsdauer beträgt also etwa 1,02 ms, entsprechend 980 Hz, wenn man nicht einige „Zeitverbraucher“ hintereinander schaltet. Die folgende Funktion mit dem Namen "TON" erzeugt 10 Schwingungen einstellbarer Periodendauer. Diese muss wieder per DatenStack übergeben werden:
: TON { n -- } 10 0 \ Wartezeit auf dem Stack liegen lassen; 10 Schwingungen beginnen
do P3ON dup wart \ Spannung am Beeper einschalten, Warteschleife laufen lassen
P3OFF dup wart \ Spannung ausschalten, Warteschleife laufen lassen
loop drop ; \ Nach Schleifenende: Wartezeit vom DatenStack entfernen
Nun wird eine bei etwa 2600 Hz beginnende, auf 7200 Hz ansteigende Frequenzfolge erzeugt, indem der Funktion TON nacheinander die Periodendauern 90, 89, 88, 87...32, 31 übergeben werden:
do 90 i - ton
loop ;
Erklärung: der Befehl i kopiert den aktuellen Wert des Wiederholungszählers auf das Datenstack. Die Differenz bis 90 (Sie können auch andere Werte testen) wird an die vorher definierte Funktion TON übergeben, die den Lautsprecher ein- und ausschaltet.
Die Funktion y2 macht das Gegenteil von y1, die erzeugten Frequenzen sinken von 7200 Hz auf 2600 Hz.
: y2 60 0
do 30 i + ton
loop ;
Nun werden immer zwei solche Folgen y1 und y2 nacheinander ausgeführt und das Ganze vier mal wiederholt. Fertig ist die Sirene.
: Heulboje 4 0 do y1 y2 loop ;
Wenn sich jemand die Mühe macht und herausbekommt, mit welchen Schwingungsdauern man Musik machen kann, kann der ROBOcontroller ganze Melodien spielen. Der Kammerton A hat 440 Hz, eine halbe Schwingung dauert also 1136 Mikrosekunden. Einige Mikrosekunden verbraucht die Ausführung der Befehle, diese Zeit muss man raten oder messen. Die Funktion TON wird geringfügig modifiziert, in TONa umbenannt und könnte so aussehen (wenn der ROBOcontroller genau 8 MHz hat):
: TONa { n -- } 0 \ Anzahl der Schwingungen per DatenStack übergeben
do p3on 255 wart 255 wart
p3off 255 wart 255 wart
loop ;
Nun ja, klingt ein bisschen schräg, weil die Hintergrundprogramme immer wieder die Zeiten verlängern. Vielleicht gilt das aber als ultimativer Kick...
á
Mehrere Programme im Speicher – eines davon starten.
Der ROBOcontroller besitzt einen Start/Stop-Eingang, mit dem man nur das zuletzt übertragene Programm starten kann. Mehr kann man mit einer einzigen Taste nicht erreichen, da noch Sonderaufgaben erfüllt werden müssen („Tiefschlaf“ bei längerer Betätigungsdauer). Es gibt aber gute Gründe, zwischen mehreren Programmen umschalten zu können:
Man will schnell nacheinander unterschiedliche Programmversionen vergleichen.
Soll der Roboter einer Fahrbahn folgen, müssen wegen wechselndem Umgebungslicht die Fototransistoren unmittelbar vor dem Start „geeicht“ werden. Dazu muss man die Helligkeitswerte „hell“ und „dunkel“ messen, diese Referenzwerte in Variablen speichern und an ein anderes Programm übergeben.
Will man also zwischen mehreren Programmen wählen, muss man das dem ROBOcontroller über einen Digitaleingang X mitteilen. Man schließt einen weiteren Taster zwischen (beispielsweise) 4in und 0 V an, der innerhalb von jeweils einer Sekunde nach Start/Stop gedrückt werden kann und wählt damit:
nur Start/Stop Programm 1
Start/Stop und 1 mal X Programm 2
Start/Stop und 2 mal X Programm 3
Start/Stop und 3 mal X Programm 4
Erfahrungsgemäß hat jeder Schalter Wackelkontakte, die bei unvorsichtiger Programmierung Mehrfachbetätigung vortäuschen. Deshalb wird erst eine lange, ununterbrochene Folge von „offener Kontakt“ als „wirklich offen“ akzeptiert. Man konstruiert also eine Wiederholung, in der der Kontakt immer wieder geprüft wird. Jedes Schließen startet den Zähler neu, indem der bisherige Stand durch den Wert 0 ausgetauscht wird. War der Kontakt oft genug offen, wird die Wiederholung beendet. Die folgende Funktion wartet, bis die Taste sicher wieder losgelassen wurde.
: Tastefrei { --- } 0 \ internen Zähler initialisieren (der Wert bleibt ohne Namen auf dem Stack)
BEGIN 4in \ Taste frei?
IF 1+ \ ja, Zähler erhöhen
ELSE drop 0 \ nein, alten Zählerstand entfernen und wieder bei 0 beginnen
THEN 1 ms \ den MEGA32 runterbremsen, Wackelkontakte dauern etwa 20 ms
dup 50 = \ war der Kontakt 50 mal nacheinander offen?
UNTIL drop ; \ ja, dann ist er auch offen! Zähler noch entfernen, Stack sauber halten
Das Programm „Tastefrei“ wird also nach frühestens 50 ms wieder verlassen, das reicht bei Wackelkontakten normalerweise aus. Wenn man besonders prellfreie Schalter verwendet, kann man die Zeit möglicherweise unter 10 ms reduzieren. Das muss man testen.
Nun beginnt das Zählen der Tastenbetätigungen. Die Idee: Das Programm wartet zunächst 1000 ms. War in diesem Zeitraum kein Tastendruck, wird bei Programmende der Wert 0 geliefert. Mit jedem Tastendruck wird der oberste Wert auf dem Stack erhöht und die Uhr beginnt erneut zu laufen. Das Programm wird frühestens eine Sekunde nach Programmstart bzw. dem letzten Tastendruck verlassen. Nebenbei: Wer mindestens 256 Mal auf die Taste drückt, erlebt eine Überraschung J
: P-Nr { --- n } 0 \ Dieses Programm liefert einen Wert; diesen initialisieren
10 AT100! \ Zeitgeber starten, nach 1000 ms ist die Uhr abgelaufen
BEGIN 4in 0= \ Taste gedrückt? (gleichen Input wie in „Tastefrei“ verwenden!)
IF 1+ brumm \ ja, mitzählen, akustische Rückmeldung
Tastefrei \ Prellen abwarten
10 AT100! \ Zeitgeber beginnt wieder bei 1000 ms; ein internes Programm subtrahiert eine Einheit pro 100 ms.
THEN
AT100@ 0= \ Zeit abgelaufen?
UNTIL ; \ Wert auf dem Stack gibt die Anzahl der Tastenbetätigungen an
Damit sind die Vorbereitungen abgeschlossen, nun müssen die Einzelprogramme entworfen werden, die im Hauptprogramm „Start“ verwendet werden sollen. Zum Testen einige kurze Kostproben (Ihre Programm werden hoffentlich sinnvoller und länger sein):
: P1 beep ;
: P2 beep 100 ms beep ;
: P3 beep 100 ms beep 100 ms beep ;
: P4 P2 100 ms P2 ; \ con variazione J
Nun kommt das Hauptprogramm, das immer zuletzt vom PC an den ROBOprogy übermittelt und mit der Start/Stoptaste gestartet wird. Darin wird – je nach Anzahl der Tastendrucke am Digitalport 4in – eines der Programme P1, P2, P3.... aufgerufen und abgearbeitet:
: Start { --- } \ dieses Programm ganz am Schluss übertragen, damit es per Start/Stoptaste gestartet werden kann!
P-Nr dup 0= \ kein Tastendruck?
IF P1 \ ja, dazugehörendes Programm aufrufen
ELSE dup 1 = \ nein, einmal gedrückt?
IF P2
ELSE dup 2 =
IF P3
ELSE dup 3 =
IF P4
ELSE brumm \ es gibt kein 5. Programm
THEN
THEN
THEN
THEN drop ; \ Prog-Kennziffer entfernen
So eintippen und testen – bei mir hat es geklappt. Noch ein letzter Hinweis: Die in diesem Programm benötigte Hilfstaste am Digitalinput 4 kann in anderen Programmteilen sehr gute Dienste leisten, denn nach Ende der eben beschriebenen Programmauswahl, wenn also der Roboter tatsächlich startet, ist diese Taste „arbeitslos“. Ein pfiffiger Konstrukteur hat den Berührungssensor rechts vorn, der bei jedem anständigen Roboter zu finden ist, als Programmwähler „missbraucht“: Er drückt auf die Starttaste und wählt durch Antippen der Stoßstange das Programm. Wenn er sich vertippt, kann er jederzeit durch erneuten Druck auf die Starttaste abbrechen. Sobald der ROBO losfährt, ist die Programmauswahl beendet und ein anderes Programm wird den Stossstangenkontakt richtig auswerten.
Platzbedarf: 101 Bytes (einschließlich P1..P4). Mit einigen Tricks kann man den Speicherbedarf vielleicht auf 80 Bytes drücken.
á
Nützliche Hilfsprogramme
Wenn der Roboter in eine Situation gerät, in der er stoppen muss, sollte man durch ein akustisches Signal die Ursache mitgeteilt bekommen. Das folgende Programm nimmt eine Zahl, beispielsweise 3, vom Stack und piepst entsprechend oft.
: MOTZ { n -- } 0 \ n ist Anzahl der Beeps
0 mmgo \ Alle Motore stoppen
do beep 150 ms loop ;
Man kann auch am Ausgang P1 eine LED anschließen (Vorwiderstand nicht vergessen!) und diese blinken lassen:
: BLINK { n -- } 0 \ n ist Blinkerzahl
do p1on 200 ms p1off 200 ms loop ;
Bei manchen Wettbewerben wird verlangt, dass der Roboter ein Notaus-Relais besitzt, das die Stromzufuhr zu allen Motoren zuverlässig unterbricht. Der Schalter zum Starten und Stoppen des Programms darf damit keine Verbindung haben. In der Hektik des Wettbewerbs passiert es aber leider oft, dieses Notaus-Relais einzuschalten, bevor das Programm gestartet wird. Dann läuft zwar die Elektronik, es bewegt sich aber kein Motor. Da ist es überaus sinnvoll, dass das Programm den Schaltzustand des Relais überwacht. Folgende Schaltung hat sich bewährt:
In Serie zur Relaisspule liegt ein Thyristor, der zunächst isoliert. Deshalb ist die Motor-Zuleitung unterbrochen (bei zwei Motoren benötigt man ein Relais mit 2 mal Ein-Kontakten) und der Transistor rechts ist durchgeschaltet. Dessen Kollektor ist mit dem Digitaleingang 1IN des ROBOprogy verbunden und wirkt wie ein geschlossener Schalter. Ein Abfrage von 1IN ergibt also den Wert 255 (oder TRUE).
Drückt man nun auf die Bereitschaftstaste (rechts), „zündet“ der Thyristor und bleibt an, die Relaisspule bekommt Strom und der Motor ist betriebsbereit – man muss nur noch den Befehl 22 MMGO geben. Durch den stromführenden Thyristor sinkt die Basis-Emitter-Spannung des Transistors unter 0,5 V und der Transistor isoliert. Fragt man nun den Eingang 1IN ab, so meldet dieser den Wert 0 (oder FALSE). Als Transistor kann ein beliebiger NPN-Typ verwendet werden.
Betätigt man den Not-Aus-Schalter, wird der Haltestrom des Thyristors kurzzeitig unterschritten, dieser isoliert wieder und das Relais unterbricht die Stromzufuhr des Motors (Bei manchen Thyristoren muss man den Wert des Elkos vor dem Not-Aus-Schalter auf 50 Mikrofarad erhöhen).
Hier das dazugehörige Programm: Motorrelais vor Start prüfen
\ f=255 bedeutet: Motorzuleitung geschlossen
\ f=0 bedeutet: Motor ist stromlos
: mot_ein? { -- f }
1in \ vom Relais
if 5 motz cr # Motor_aus 0
else 255
then ;
á
Einen Roboter sollte man nicht „auf gut Glück“
: tgo { maxzeit -- f } \ f>0 Überlast
at100! 33 mmgo
begin at100@ 0=
mload umax 12 u> or
until 0 mmgo
at100@ ; \ 0=Zeit war aus
á
á
© Herbert Weidner 2006-5