Steuerung


Aufgabenstellung der Steuerung


Allgemeines über Systeme mit Steuerung

Die Steuerung eines Prozesses kann mechanisch, elektrisch, elektronisch (digital) oder als eine Kombination davon erfolgen. Desweiteren unterscheidet man zwischen Steuerung mit und Steuerung ohne Rückkopplung (Feedback). Rückkopplung bedeutet, dass ständig kontrolliert wird, ob die Steuergrösse (Eingabe- oder Sollgrösse der Steuerung) auch dem realen, momentanen Zustand des Systems entspricht. Die Differenz, die sich bei dieser Kontrolle ergibt, ist dann wieder Teil der Eingangsgrössen der Steuerung. Man spricht dann von einem Regelkreis. Typischerweise wird ein Regelkreis wie in Abb. 3 dargestellt.

 figure53
Abbildung 3: Typisches Modell eines Regelkreises

Im folgenden wollen wir uns auf digitale Steuerungen beziehen. Ausserhalb des gesteuerten Prozesses gibt es also nur Bits und Bytes und Berechnungen auf diesen.

R(t) ist der Sollwert (als digitale Information) der Stellgrösse zum Zeitpunkt t.
B(t) ist der tatsächliche Wert, in gewandelter und evtl. transponierter Form (z.B. Temperatur tex2html_wrap_inline399 digitale Information).
E(t) ist einfach R(t) - B(t).
G(t) ist die Transferfunktion, die zentrale Komponente der Steuerung, in ihr wird mit E(t) als Eingabe der neue Steuerwert C(t) berechnet. Dieser soll den Prozess derart manipulieren, da\3 der Fehler E(t) möglichst klein wird, also der Proze\3 sich so verhält, wie er eben soll (tex2html_wrap_inline399 steuern, regeln).

Historisch, vor dem Durchbruch der Elektronik, wurden zum Steuern analoge sogenannte PID-Regler (Proportionalglied, Integralglied und Differenzialglied) verwendet. Deren Transferfunktion hatte folgende Gestalt:
displaymath397

Dabei sind math70 geeignet gewählte Konstanten. Die PID-Funktion war auch Ausgangspunkt beim Suchen der geeigneten Transferfunktion für unser Fahrzeug.


Vorgehensweise bei der Realisierung

Auf unserem Fahrzeug befinden sich zwei Regelkreise, die jedoch miteinander gekoppelt sind. Zunächst möchten wir feststellen, wie man am besten für ein einzelnes Rad eine Steuerung programmiert, die zugleich schnell auf Störungen reagiert, d.h. die Drehzahl wieder schnell auf konstant tex2html_wrap_inline377 regelt, als auch gleichzeitig ein stetiges, ruckelfreies Laufverhalten gewährleistet. Danach wollen wir uns dem Problem widmen, wie man die beiden Regelkreise am besten zu einem einzigen vereinigt.


Regelung einer konstanten Geschwindigkeit eines einzelnen Rades

Der Regelkreises sieht hier dem allgemeinen Schema ganz ähnlich:

 figure75
Abbildung 4: Regelkreis für ein einzelnes Rad

Eine momentane Geschwindigkeit, wie sie in Abb.4 vorkommt, mu\3 erst einmal berechnet werden, dazu haben wir zwei Möglichkeiten getestet:

  1. Man gibt ein Zeitintervall T vor und misst, wieviele Lichtschrankenimpulse darin vorkommen. Daraus lässt sich die momentane Drehgeschwindigkeit der Achse berechnen. Resultat der Realisierung: Um einen vernünftigen Wert für die Momentangeschwindigkeit zu bekommen, muss T gross (im Bereich von Sekunden) gewählt werden, da ansonsten zu wenige Lichtschrankenimpulse pro Zeitmesseinheit anfielen und dadurch grosse Schwankungen der Geschwindigkeit bei den einzelnen Messungen gemessen würden, obwohl diese nicht der Realität entsprechen würden. Aus einem grossen Zeitintervall T folgt aber eine sehr langsame Anpassungsgeschwindigkeit der Steuerung. Auf plötzliche Änderungen des Drehwiderstandes wird zu langsam reagiert ( ca. 5 s Anpassungszeit).
  2. Wir benutzen den Capture-Modus, wie im vorigen Abschnitt beschrieben. Jeder Lichtschrankenimpuls verursacht, dass der aktuelle Timerwert in ein Capture-Register geschrieben wird (Genauer: in eines der 16 CapComX (X=1..16) Register). Der anschliessend ausgelöste Interrupt wird von einer Interruptbehandlungsroutine (konkret: ``Segment-handler'') bedient, in ihr wird der Wert des entsprechenden CapCom-Registers zwischengespeichert. Danach wird durch Differenzbildung der beiden zuletzt zwischengespeicherten Timerwerte der Zeitabstand der Lichtschrankenimpulse berechnet. Die momentane Geschwindigkeit des Rades ist aber einfach umgekehrt proportional zu diesem Zeitabstand. Resultat: Mit Hilfe dieser Methode der Momentangeschwindigkeitsmessung lässt sich eine schnelle und zuverlässige Geschwindigkeitsregelung realisieren. Wir entscheiden uns also für diese Art, die Momentangeschwindigkeit zu messen.

In beiden Fällen gehen wir davon aus, dass die Drehgeschwindigkeit von Motor und Fahrzeug stets im gleichen Verhältnis zueinander stehen, d.h. da\3 die Räder stets festen Kontakt zum Boden haben, es also keinen Schlupf gibt.

Nun ergibt sich das Problem des Timer-Überlaufes. Man kann, wie schon erwähnt, den Timer mit unterschiedlicher Geschwindigkeit hochzählen lassen (19 kHz bis 2,5 MHz). Bei einem 16-Bit Zähler ergeben sich dann alle 26 ms bis 3,36 s Überlaufe. Wir müssen nun einen vernünftigen Wert für die Timergeschwindigkeit herausfinden. Einerseits soll die Zeitmessung feinkörnig sein, um die Geschwindigkeit auch exakt zu erfassen und regeln zu können, auf der anderen Seite sollte der Timer nicht zu schnell sein und nicht zwischen zwei Lichtschrankenimpulsen mehr als einmal überlaufen.


Die Steuerung der Fahrtrichtung

Nachdem wir nun in der Lage waren, ein einzelnes Rad auch bei wechselndem Drehwiderstand mit einer vorgegebenen Geschwindigkeit drehen zu lassen, mussten wir uns überlegen, wie wir die beiden bis jetzt unabhängigen Regelkreise zu einem einzigen zusammenfassen. Die Fahrtrichtung des Fahrzeugs ergibt sich, wie bereits angedeutet, aus dem Geschwindigkeitsverhältnis der beiden Antriebsräder. Bezogen auf einen Streckenabschnitt (Kreisausschnitt oder Gerade) ist aber das Verhältnis der Geschwindigkeiten gleich dem Verhältnis der zurückgelegten Wegstrecke. Die Wegstrecke ist dabei gerade das Integral der Geschwindigkeit. Die Variable segmentcounterR/L enthält die Anzahl der in einem Streckenabschnitt erhaltenen Lichtschrankenimpulse. Diese lä\3t sich, da wir von Schlupf abstrahieren, als Ma\3 für die bisher zurückgelegte Strecke (bezüglich eines Streckenabschnitts) interpretieren. Daraus ergibt sich für einen Streckenabschnitt:
displaymath409
Da wir nur Integerzahlen innerhalb unserer Steuerug kennen, schreiben wir für c tex2html_wrap_inline417. Diese Zahl (Bruch) schreibt also das Sollverhältnis der Geschwindigkeiten von rechtem Rad zu linkem Rad vor.

Die Idee bei der Richtungssteuerung ist, da\3 ständig überwacht wird, ob das Verhältnis tex2html_wrap_inline419 gleich tex2html_wrap_inline417 ist. Ein (Richtungs-)Fehler ergibt sich dann folgenderma\3en:
displaymath410
Wenn der Richtungsfehler stets sehr klein ist, können wir davon ausgehen, da\3 unser Fahrzeug recht genau die richtige Bahn beschreibt. Dem wollen wir oberste Priorität geben, ob dabei die korrekte Geschwindigkeiten gefahren wird hat zweite Priorität. Aus tex2html_wrap_inline377 und der Zahl tex2html_wrap_inline417 lassen sich die Sollgeschwindigkeiten von linkem und rechtem Rad (sollspeedR/L) berechnen. Daraus ergibt sich dann ein Geschwinigkeitsfehler für jedes Rad (fehlerR = sollspeedR - speedR). Nun die Transferfunktion für das rechte Rad:
displaymath411
und fürs linke Rad:
displaymath412
Bemerkungen:


Zeitliches Verhalten der Steuerungen

Nun möchten wir Aussagen darüber treffen, ob unser Realzeitsystem auch wirklich zuverlässig ist. Auf der einen Seite stehen da natürlich praktische Tests, auf der anderen Seite versuchen wir durch eine mehr theoretische Herangehensweise auszuschlie\3en, da\3 es zu einer Situation kommen kann, in der unsere Steuerung versagt. Wir beschränken uns dabei auf das Betrachten des zeitlichen Verhaltens der Steuerung. Einen Beweis der Korrektheit im klassischen Sinne der Informatik, also ob Ergebnisse vom Inhalt her korrekt berechnet werden, wird es nicht geben. Interessant ist vor allem Extremsituationen zu betrachten, in denen das System unter hoher Last steht, d.h. innerhalb kurzer Zeit viele Eingaben bzw. Unterbrechungen anfallen. Wir wollen die Frage beantworten, ob es sein kann, da\3 unser System bei Maximallast, d.h. bei maximaler Frequenz aller Interruptquellen, die Steuerungsaufgabe nicht mehr erfüllen kann. Wir versuchen die Situation auszuschlie\3en, da\3 ein Routine, die den Interrupt A bedient, noch bevor sie vollständig abgearbeitet ist, erneut durch den Interrupt A (verursacht durch externe (Lichtschranken) oder auch interne Geräte (Timer)) aufgerufen wird. Hierzu mu\3 man in Erwägung ziehen, da\3 die Ausführung einer Routine in die Länge gezogen wird, falls sie von Routinen, welcher Interrupts höherer Priorität bedienen, unterbrochen wird. Die Routine (Funktion) regelung() wird beispielsweise bei Eintreffen einen Lichtschrankeninterrupts von den Routinen segment-handlerR/L() unterbrochen.

Da uns keine entsprechenden Werkzeuge zur Verfügung stehen, müssen wir die Analyse des zeitlichen Verhaltens vollständig von Hand unternehmen. Zunächst wollen wir wissen, wieviel Rechenzeit die einzelnen Routinen maximal verbrauchen. Dazu müssen wir auch einen Blick auf die Art und Weise der Übersetzung des C Compilers legen, denn nur von den Assemblerbefehlen kennen wir die genauen Ausführungszeiten.

Wir schreiben also ein paar einfache Testprogramme, um herauszufinden, wie der C Compiler einzelne C-Instruktionen in Assemblercode übersetzt:
Seien a,b integer (16 Bit)

tabular113

dabei ist KR die aktuelle Kellerrahmenadresse, Rx/y Register der CPU und A(a) der Offset der Adresse von a bezüglich des Kellerrahmens. Die mov-Befehle haben die Form mov Ziel,Quelle .

Sämtliche anderen Übersetzungen folgen dem allgemeinen Schema für imperative Programmiersprachen (siehe Buch Übersetzerbau von Wilhelm/Maurer). Insbesondere kommen beim jedem Funktionsaufruf 7 mov-Befehle, 2 Additionsbefehle und ein return-Befehl im Pro- bzw Epilog dazu. Verwenden wir long statt 16 Bit-integer Arithmetik, so besteht wird eine Addition mit 4 mov- und 2 add-Befehlen und ein Multplikation mit 6 mov-, 2 add- und 3 mul-Befehlen übersetzt.

Nun wollen wir wissen, wieviel Zeit die einzelnen Assemblerbefehle benötigen. Diese wird zunächst angegeben in States, welche abhängig von der Taktfrequenz des Chips ist. Für 40 MHz gilt:

displaymath427

Jedem Maschinenbefehl B ist ein Wert T(ROM) (B) zugeordnet. Dieser gibt an, wieviel states ein Befehl zur Abarbeitung benötigt, würde er im ROM stehen. Für die meisten Befehle liegt dieser Wert bei 2 oder 4. T(ROM) (mul) = 10, T(ROM) (div) = 20.

Da der Programmcode jedoch im externen Speicher steht, und dieser nur über einen 8-Bit Bus mit der CPU verbunden ist gelten folgende Werte:
bei 2-Byte-Befehl:

displaymath428
bei 4-Byte-Befehl:

displaymath429

ACT ( ALE (Adress Latch Enable) Cycle Time) gibt Auskunft über die Geschwindigkeit des externen Buszugriffs (Multiplex, Demultiplex). ACT berech- net sich aus verschieden Registerinhalten und beträgt im konkreten Fall 4 states.

Es ergeben sich folgende Zeitwerte für die Maschienenbefehle:


tabular123



Berechnen der Laufzeiten aller Funktionen/Interruptroutinen

Zusammenfassung:


tabular162



Darstellung auf der Zeitachse

Wir zeichnen nun eine Zeitachse mit dem Schlechtesten-Fall-Zenario an Interrupteingängen:

 figure167
Abbildung 5: Schlechtester Fall beim konkreten Fahrzeug. Die Laenge der Routinen ist um den Faktor 10 zu breit dargestellt.

Die Situation in Abb.5 geht von einer höchsten Drehzahl von 2 Umdrehungen pro Sekunde (also maximal 96 Lichtschrankeninterrupts) aus, was für unser Fahrzeug (leider) vollkommen ausreicht, jedoch nicht mit den geforderten 6 km/h übereinstimmt. Wie würde die Situation dann aussehen: Bei einem Raddurchmesser von 18 cm ergäben sich rund 9 Umdrehungen pro Sekunde. Das sind 216 Impulse pro Rad und Sekunde, also 432 Impulse pro Sekunde insgesamt. Wiederum sollte die Steuerfunktion so schnell laufen, das sie auf jede Geschwindigkeitsänderung sofort reagieren kann. Gehen wir also von einer Frequenz von 400 Hz für die Steuerfunktion aus und konstruieren nochmals obiges Schaubild:

 figure173
Abbildung 6: Schlechtester Fall beim Fahrzeug der Aufgabenstellung, die Laenge der Routinen ist ebenfalls um den Faktor 10 zu breit dargestellt

Wie man nun in Abb.6 sieht, kann es nie vorkommen kann , dass die Steuerfunktion regelung(), die ja mit konstant 400 Hz läuft, von hereinbrechenden Interruptanforderungen für Segment-handlerL/R() oder time-handler() so lange unterbrochen wird, dass sie nicht vor ihrem erneuten Aufruf abgearbeitet wäre. Das gleiche gilt (offentsichtlich) für die anderen (wesentlich kürzeren) Routinen auch. Diese Aussagen sind nur möglich, weil wir sinnervollerweise von einer Oberschranke für die Aufruffrequenzen der Interruptroutinen (432 Hz) ausgehen können.


Bemerkungen zur Implementierung



Roland Schlosser [Fopra Realtime]
Wed Jan 22 20:12:29 CET 1997