Sie sind auf Seite 1von 27

Anwendung der Prozessdatenverarbeitung 2007

IINVENVERSESRSESPPENENDELDEL

Thu Ha Dang André Wilhelm

der Prozessdatenverarbeitung 2007 I I NVE NVE RSES RSES ← ← P P EN EN DEL

IINHNHAALLTTSSVVEERRZZEEIICCHHNNIISS

I. Einleitung

3

 

I.1

Aufgabenstellung

3

I.2 Analyse

3

I.3 Anmerkungen zur Ausführung

3

II.

Aufbau

6

II.1 Der Pendelapparat

6

II.2 Microchip Picdem 18F452

7

II.3 Microchip Picdem 2 Plus Demo Board

8

II.4 H-Brücke

10

II.5 Gelötete H-Brücke

11

II.6 Verschaltung der Komponenten

12

II.7

Motor

12

III.

Entwicklung

13

III.1 Entwicklungsumgebung

13

III.2 Softwarestruktur

14

III.3

Konfiguration

16

III.4 Interrupt-Handler

17

IV.

Realisierung

19

IV.1 Allgemeines

19

IV.2 Zentrieren

21

IV.3 Aufschwingen

22

IV.4 Randabbruchbedingung

23

IV.5 Regelalgorithmen

24

IV.5.1 P-Regler

24

IV.5.2 PI-Regler

25

IV.5.3 PID-Regler

25

V.

Ausblick und Fazit

27

VI.

Quellen

28

I. Einleitung

I.1 Aufgabenstellung

Im Rahmen dieses Projektes soll die Regelung eines „Inversen Pendels“ entwickelt werden. Charakteristisch für diese Pendelvariante ist, dass es aus seiner Ruhelage (analog zu einem einfachen Pendel) über die Horizontale hinaus in eine aufrechte Position geschwungen und dort balanciert und stabilisiert wird. Idealerweise ruht das Pendel im Endzustand dann in seiner aufrechten Position (es zeigt nach oben).

I.2 Analyse

Das inverse Pendel wird mechanisch mit einer zweckentfremdeten Druckkopfsteuerung realisiert, mit dem das am Druckkopf angebrachte Pendel horizontal hin- und herbewegt werden kann. Aus der Aufgabenstellung lassen sich demzufolge drei Pendelphasen ableiten:

1. Zentrierung/Kalibrierung Das Pendel muss in die Mitte der Führungsschiene gebracht werden. Dazu müsste die Regelung zunächst die Breite bzw. die zur Verfügung stehende Strecke „ausmessen“, um das Pendel anschließend in eine zentrale Position zu bringen.

2. Einschwingphase Nun muss das Pendel in eine annähernd senkrechte Position bewegt werden, damit eine Balancierung oberhalb der Horizontalen möglich ist. Dieser Schwingvorgang wird durch gezieltes hin- und herbewegen ausgelöst.

3. Stabilisierung Nach dem Einschwingvorgang kann das Pendel ab einem bestimmten Auslenkungswinkel stabilisiert werden. Die horizontale Bewegung dient nun nicht mehr zur Verstärkung der Schwingung. Das Pendel kippt solange es noch in Bewegung ist zur Seite hin weg, d.h. es gilt dieser Bewegung entgegenzuwirken.

I.3 Anmerkungen zur Ausführung

Zwecks Übersicht wird in diesem Abschnitt die Konfiguration des Programms besprochen.

Alle

vorgenommen:

möglichen

Einstellungen

werden

im

// Reglerarten #define P_REGLER

0

#define PI_REGLER

1

#define PID_REGLER

2

#define TEST_REGLER

3

#define TEST2_REGLER

4

#define DANG_REGLER

5

//#define TIMER_IR_TEST

#ifndef TIMER_IR_TEST #define REGLER P_REGLER #define CENTERED #define UP TRUE

#endif

Hauptheader

(global.h)

über

#define

Zunächst können neue Regler hinzugefügt werden. Es genügt, analog zur obigen Liste weitere Konstanten unter Beachtung der Nummerierung mit aussagekräftigen Bezeichnungen anzulegen. Wenn #define TIMER_IR_TEST nicht auskommentiert ist, wird in main.c lediglich eine Schleife ausgeführt, deren zeitliche Ausführungsdauer mit der Stopwatch des MPLAB- Simulators gemessen werden kann. Somit erhält man einen Wert für das zeitliche Auftreten der Timer0-Interrupts. Zur regulären Ausführung des Programms muss #define TIMER_IR_TEST auskommentiert sein. Dann können im unteren Codesegment weitere wichtige Einstellungen vorgenommen werden: Zunächst lässt sich der zu verwendende Regler einstellen. Dazu genügt es, in

#define REGLER Reglerkonstante für Reglerkonstante den Namen der verwendeten

Konstanten einzusetzen. Im Beispiel wird also der P-Regler verwendet. Wird die komplette Zeile auskommentiert, findet keine Regelung statt.

CENTERED bestimmt, ob das Pendel bei Programmstart bereits als zentriert

#define

interpretiert werden soll, wobei für die Breite des Apparats dann ein Erfahrungswert verwendet wird:

void center(void)

{

 

#ifdef CENTERED

width = 0xc50;

left = width / LIMIT; right = width - left; centered = TRUE; position = width / 2; return;

#endif

}

Soll eine automatische Zentrierung ausgeführt werden, muss #define CENTERED auskommentiert sein. #define UP Wert legt die Startposition des Pendels fest. TRUE signalisiert dem Programm, dass der Benutzer das Pendel per Hand aufgerichtet hat, d.h. dass der Regler sofort

einsetzen kann. Wird stattdessen der Wert FALSE verwendet, führt das Programm zunächst einen Aufschwingvorgang durch, um das Pendel in eine regelbare aufrechte Position zu bringen.

II. Aufbau

II.1 Der Pendelapparat

Zur mechanischen Realisierung des Pendels kommt ein alter Druckkopfmechanismus zum Einsatz:

Pendels kommt ein alter Druckkopfmechanismus zum Einsatz: Der seitlich angebrachte Motor bewegt den Schlitten (Motor

Der seitlich angebrachte Motor bewegt den Schlitten (Motor 1). An Stelle des Druckkopfes wurde ein zweiter Motor auf dem Schlitten angebracht, an dem sich das Pendel befindet. Beide Motoren enthalten jeweils zwei Lichtschranken, die der Bestimmung der Geschwindigkeit sowie der Drehrichtung dienen. Die Schienen selbst sind 37cm lang, der darauf befestigte Schlitten ca. 8cm.

II.2 Microchip Picdem 18F452

II.2 Microchip Picdem 18F452 Verwendete Pins: 15 → Bremse 16 → Drehrichtung des Motors 17 →

Verwendete Pins:

15

→ Bremse

16

→ Drehrichtung des Motors

17

→ PWM-Input

33

→ Erste Lichtschranke des 2. Motors (Pendel)

34

→ Zweite Lichtschranke des 2. Motors (Pendel)

35

→ Erste Lichtschranke des 1. Motors (bewegt den Druckkopf)

37

→ Zweite Lichtschranke des 1. Motors (bewegt den Druckkopf)

II.3 Microchip Picdem 2 Plus Demo Board

II.3 Microchip Picdem 2 Plus Demo Board (1) Microchip (PIC 18F452) / DIP Sockets (2) Stromversorgung

(1) Microchip (PIC 18F452) / DIP Sockets (2) Stromversorgung

9V über Netzstecker

(3) RS232 Schnittstelle

Nicht verwendet

(4) In-Circuit Debugger Schnittstelle

Zum Aufspielen der Software verwendet

(5) Potentiometer

Wird über einen A/D-Wandler abgefragt

(6) 3 Schalter

Nicht verwendet

(7) Stromversorgungs LED (8) LEDs (Port B)

Zu Beginn zum Testen des Ports benutzt

(9) Jumper für Port B

Je nachdem, wie man den Jumper setzt, kann man die LEDs (8) ansteuern. Dann verliert man aber die Möglichkeit zur Interrupt-Abfrage an Port B.

(10) 4 MHz Oszillator (11) Freie Löcher für Oszillator reserviert (12) Timer 1

A/D-Wandlung für das Potentiometer

(13) Jumper (14) Speicher (15) LCD Display

Nicht verwendet

(16) Summer

Testweise zur akustischen Wahrnehmung der Timer-Interrupts verwendet

(17) Prototypische Vorrichtung für Benutzer Hardware

Befestigung der angeschlossenen H-Brücke

(18) Wärmesensor (TC74 )

Nicht verwendet

II.4 H-Brücke

II.4 H-Brücke LMD 18200 T H-Brücke mit Testbeschaltung Die Testbeschaltung haben wir in unserem Projektaufbau entfernt,

LMD 18200 T

II.4 H-Brücke LMD 18200 T H-Brücke mit Testbeschaltung Die Testbeschaltung haben wir in unserem Projektaufbau entfernt,

H-Brücke mit Testbeschaltung

Die Testbeschaltung haben wir in unserem Projektaufbau entfernt, da diese nicht benötigt wurde. Dazu wurden die beiden 12 Ω Widerstände weglassen.

II.5 Gelötete H-Brücke

II.5 Gelötete H-Brücke Die H-Brücke überträgt das erzeugte PWM-Signal auf den Motor („PWM INPUT“). Zusätzlich

Die H-Brücke überträgt das erzeugte PWM-Signal auf den Motor („PWM INPUT“). Zusätzlich kann man die Richtung über „DIRECTION INPUT“ bestimmen und den Motor blockieren lassen („BRAKE“). Sie muss mit mindestens 10V Versorgungsspannung betrieben werden.

II.6 Verschaltung der Komponenten

II.6 Verschaltung der Komponenten Vollständig verschalteter Aufbau II.7 Motor Der seitlich angebrachte Motor (Motor 1)

Vollständig verschalteter Aufbau

II.7 Motor

Der seitlich angebrachte Motor (Motor 1) bewegt den Schlitten (siehe Bild Kapitel II.1). Sowohl Motor 1, als auch Motor 2 enthalten jeweils zwei Lichtschranken. Der 2. Motor dient lediglich zur Registrierung der Interrupts, er wird nicht zu Steuerung verwendet! Die Motordrehscheibe des 2. Motor hat 504 Segmente und zwei Lichtschranken. In unserem Projekt berücksichtigen wir die ansteigende und abfallende Taktflanke, das ergibt 1008 Interrupts pro Lichtschranke und bei zwei Lichtschranken insgesamt 2016 Interrupts pro Motorumdrehung. Die Anzahl der Segmente des 1. Motors ist nicht bekannt. Diese Information ist aber für unsere Zwecke auch nicht notwendig.

III. Entwicklung

III.1 Entwicklungsumgebung

Als

eingesetzt:

Entwicklungsumgebung

wurde

Microchips

MPLAB

IDE

v7.52

unter

Windows

wurde Microchips MPLAB IDE v7.52 unter Windows Screenshot MPLAB IDE Microchips c18 -Compiler, der

Screenshot MPLAB IDE

Microchips c18-Compiler, der unabhängig von der Entwicklungsumgebung installiert wird, wurde zur Kompilierung der in C erstellten Software eingebunden. Im Lieferumfang des Compilers befinden sich bereits angepasste Header bzw. Bibliotheken für den verwendeten PIC 18F452 Prozessor.

Zur Programmierung des Picdem 2 Plus Demo Boards wurde der MPLAB ICD 2 In- Circuit Debugger verwendet, der auch einige Möglichkeiten zur Simulation bietet, d.h. die Software könnte theoretisch getestet werden, ohne direkt auf der Hardware zu laufen. Für dieses Projekt war das Feature jedoch eher unnütz. Lediglich das Bestimmen des zeitlichen Auftreten der Timer-Interrupts wurde durch den Simulator deutlich erleichtert.

wurde durch den Simulator deutlich erleichtert. MPLAB ICD 2 In-Circuit Debugger III.2 Softwarestruktur Nach

MPLAB ICD 2 In-Circuit Debugger

III.2 Softwarestruktur

Nach einigen Überlegungen haben wir uns für eine modularisierte Struktur (jeweils Header- und Source-File) entschieden, um die Steuerung der Komponenten „räumlich“ soweit möglich voneinander zu trennen und so die Wartbarkeit zu erleichtern. Mit fortschreitender Entwicklung zeigte sich jedoch, dass eine einzige Quelldatei (mit entsprechender Kommentierung) möglicherweise übersichtlicher gewesen wäre, da man bei der Modularisierung viele globale Variablen und einige Funktionen in mehreren Modulen benötigt, was zu vielen „extern“-Deklarationen geführt hat. Hinzu kommt noch, dass die Regelalgorithmen in der letzten Version in den Timer0-Interrupt-Handler (befindet sich in main.c) verlagert wurden, obwohl sie dem Pendel zuzuordnen sind. Um einen besseren Überblick über den Quellcode zu vermitteln, folgt nun eine Erläuterung der Module:

0. Modul: Die komplette Anwendung

Dieses Modul stellt die gesamte Anwendung dar. Zur Abstimmung auf den verwendeten Prozessor musste dem Projekt noch ein Linker-Skript (18f452i.lkr) hinzugefügt werden. Zusätzlich existiert ein Header-File „global.h“, das allgemein gültige Definitionen (etwa zu TRUE, FALSE, usw.) und Switches zur Konfiguration des Programmablaufs enthält (z.B. ob eine automatische Zentrierung durchgeführt werden soll). Wichtig ist hier die Einbindung des spezifischen Prozessor-Headers

#include <p18f452.h>

1. Modul: Demo Board

Dieses Modul bietet die Funktionalität zur Konfiguration bzw. Initialisierung des Picdem 2 Plus Demo Boards. Die Methode void initBoard() kümmert sich um die Initialisierung der benötigten Pins bzw. Register des Prozessors, dazu gehören auch die Interrupts bzw. Hardware-Timer.

2. Modul: Der Motor

Das Motor-Modul repräsentiert den aktiven Motor, mit dem der Druckschlitten in horizontaler Richtung bewegt wird. Dazu gehören die Methoden:

int setDC(int dc);

stellt den PWM-Duty-Cycle ein (Vorzeichen gibt die Richtung an, Rückgabewert ist der eingestellte Duty-Cycle).

void interrupt_A_motor(void); void interrupt_B_motor(void);

behandeln die Lichtschranken-Interrupts.

void center(void);

positioniert den Schlitten zentral.

unsigned long move(int dc);

bewegt den Schlitten mit dem angegebenen Duty-Cycle solange, bis der Rand erreicht ist, d.h. keine Interrupts mehr folgen, und gibt die tatsächlich zurückgelegte Strecke in Interrupts zurück.

unsigned long go(int dc, unsigned long s);

bewegt den Schlitten wie unsigned long move(int dc), jedoch nur für eine vorgegebene Strecke „s“ (Weg in Interrupts).

void brake(unsigned short status);

setzt den Status der Motorbremse. Diese Funktionalität wurde nur experimentell eingesetzt.

void stop(void);

ist eine Funktion zum sofortigen Stoppen des Motors, die außerdem die Interrupts und Timer abschaltet.

3. Modul: Das Pendel

Dieses Modul stellt den passiven Motor dar, an dem das Pendel befestigt wurde. Er dient lediglich der Winkelbestimmung durch Auswertung der Lichtschranken-Interrupts. Die Regler zur Pendelstabilisierung würde man im Grunde auch zu diesem Modul zählen, sie wurden aber aus Effizienzgründen in der Timer0-Interruptroutine (in main.c) implementiert. Folgende Funktionalität enthält das Pendel-Modul:

void start(void);

regt das Pendel aus dem Startzustand heraus zum Schwingen an.

void interrupt_A_pendel(void); void interrupt_B_pendel(void);

behandeln die Interrupts der beiden Lichtschranken.

4. Main

Die Datei main.c enthält neben der Hauptfunktion (diese muss mit einer Endlosschleife enden, damit die Hardware weiterhin etwas zu tun hat, sonst gerät sie womöglich in einen undefinierten Zustand) auch den Interrupt-Verteiler sowie die Interrupt-Handler für beide Timer:

void high_interrupt(void);

prüft welcher Interrupt vorliegt und ruft die entsprechende Interrupt-Routine auf.

void tmr0_interrupt(void);

behandelt die Timer0-Interrupts und enthält die Regelalgorithmen.

void tmr1_interrupt(void);

behandelt Timer1-Interrupts und kümmert sich um die A/D-Wandlung für das Poti.

5. Sonstiges

Im Projekt befinden sich noch weitere Quelldateien, etwa xlcd.*, delay.* oder compiler.h. Diese wurden zum Ansteuern des Displays hinzugefügt. Leider hat dies nicht funktioniert. Das Poti sollte dazu dienen, Vorfaktoren der Regelalgorithmen während der Programmausführung quasi „on the fly“ einzustellen. Wegen Zeitknappheit und technischer Probleme in der Endphase konnte dieses Feature nicht mehr ausgiebig eingesetzt werden.

III.3 Konfiguration

In den System-Headern hat der Hersteller für alle Pins des Prozessors bzw. für die Funktionen des Demo Boards bereits globale Konstanten vordefiniert, mit denen im Code leicht auf die Funktionalität zurückgegriffen werden kann. Hier ein Beispiel für die Belegung von PortB:

werden kann. Hier ein Beispiel für die Belegung von PortB: Portbelegung am Beispiel von PortB Die

Portbelegung am Beispiel von PortB

Die wichtigsten Konfigurationsschritte sind das Einstellen der „Nutzungsrichtung“ über das TRIS-Register, d.h. ob ein Pin als Eingang oder Ausgang verwendet wird, und der Interrupt-Register:

TRISCbits.TRISC0 = 0; TRISCbits.TRISC1 = 0; TRISCbits.TRISC2 = 0;

// BRAKE (Output) // DIRECTION (Output) // PWM (Output)

konfiguriert die ersten drei Pins von PortC als Ausgangspins zur Motoransteuerung.

TRISBbits.TRISB0 = 1; TRISBbits.TRISB1 = 1; TRISBbits.TRISB2 = 1;

// INT0 (Input) // INT1 (Input) // INT2 (Input)

initialisiert die ersten drei Pins von PortB als Eingänge, über die beide Lichtschranken des Pendel-Motors und eine Lichtschranke des Steuerungs-Motors angeschlossen sind. Die

Konfiguration der Interrupts erfolgt über die Register INTCON, INTCON2 und INTCON3:

INTCONbits.INT0IE = 1; INTCONbits.INT0IF = 0;

aktiviert den Interrupt INT0 und setzt das Flag zurück auf Null. Mit diesem lässt sich prüfen, ob ein bestimmter Interrupt vorliegt.

INTCON3bits.INT1IE = 1; INTCON3bits.INT1IF = 0; INTCON3bits.INT2IE = 1; INTCON3bits.INT2IF = 0;

konfiguriert analog die Interrupts INT1 und INT2, und

INTCON2bits.INTEDG0 = 1; INTCON2bits.INTEDG1 = 1; INTCON2bits.INTEDG2 = 1;

definiert, dass alle drei Interrupts zunächst bei ansteigender Taktflanke auftreten (bei INT0 und INT1 erfolgt in den Handlern ein Taktflanken-Toggle). Eine Interrupt-Priorisierung (in Hardware) wurde nicht verwendet,

RCONbits.IPEN = 0;

schaltet diese ab.

INTCONbits.GIE = 1; INTCONbits.PEIE = 1;

aktiviert schließlich die Interrupts. Diese Konfigurationsschritte werden alle in der Methode void initBoard() ausgeführt. Neben den Interrupts und den Registern für das Poti müssen auch die Timer und die Pulsweitenmodulation initialisiert werden. Zu diesem Zweck existieren bereits vorgefertigte Funktionen: OpenPWM1( startet die „erste“ Pulsweitenmodulation, mit OpenTimer0( bzw. OpenTimer1( werden die beiden Timer gestartet. Für detailliertere Beschreibungen sei an dieser Stelle auf den Quellcode und die c18 Header-Dateien bzw. die Microchip-Dokumentation (siehe Quellenangaben) verwiesen. In der Anwendung wurde ein Test eingebaut, mit dem man (unter Verwendung des Simulators) das zeitliche Auftreten der Timer0-Interrupts experimentell ermitteln kann. Dazu muss lediglich #define TIMER_IR_TEST in global.h erscheinen. Der somit ermittelte Wert liegt bei 13,1064ms.

)

)

)

III.4 Interrupt-Handler

Die korrekte Implementierung der Interrupt-Routinen sorgte in der frühen Projektphase für einige Schwierigkeiten, da insbesondere bei der Auszeichnung (#pragma-Zusätze) und Platzierung der Methoden einige Dinge zu beachten sind, damit der Compiler diese auch

entsprechend behandelt. Bei abgeschalteter Interrupt-Priorisierung springen alle Interrupts in den High-Vector. Es wird zunächst eine Hauptmethode benötigt, die eingehende Interrupts (IRs) registriert und den IR-Verteiler aufruft:

#pragma code high_vector = 0x00000008 void interrupt_at_high_vector(void)

{

_asm GOTO high_interrupt _endasm

}

#pragma code

Liegt ein IR vor, ruft diese Methode den IR-Verteiler auf:

void high_interrupt(void)

{

 

if (INTCONbits.INT0IF) { _asm GOTO interrupt_A_pendel _endasm

}

else if (INTCON3bits.INT1IF) { _asm GOTO interrupt_B_pendel _endasm

}

else if (INTCON3bits.INT2IF) { _asm GOTO interrupt_A_motor _endasm

}

else if (INTCONbits.TMR0IF) { _asm GOTO tmr0_interrupt _endasm

}

else if(PIR1bits.TMR1IF) { _asm GOTO tmr1_interrupt _endasm

}

}

Dieser prüft anhand der Interrupt-Flags, welcher IR vorliegt und übergibt an den jeweiligen Handler, z.B. an

#pragma interrupt interrupt_B_pendel void interrupt_B_pendel(void)

{

// Interrupt-Flag zurücksetzen INTCON3bits.INT1IF = 0;

}

#pragma code

Jeder Handler setzt nach Durchführung seiner Aufgaben das IR-Flag wieder zurück, damit weitere IRs registriert werden können.

IV. Realisierung

IV.1 Allgemeines

Die Ansteuerung des Motors durch die Pulsweitenmodulation erfolgt über die Methode int setDC(int dc), die im Grunde lediglich die in pwm.h vordefinierte Funktion void

SetDCPWM1(PARAM_SCLASS unsigned int duty_cycle) kapselt und dabei den Direction-

Output übernimmt. Anhand des Vorzeichens von dc wird die Drehrichtung bestimmt (negativ entspricht links herum). Der nutzbare Wertebereich liegt zwischen 0 und 1023

(10bit).

Wichtigster und sensibelster Teil des Projekts ist zweifellos die Sensorik. Gerade beim Pendel-Motor ist eine robuste und zuverlässige Winkelerkennung von immenser Bedeutung. Für den Schlittenmotor dagegen genügt es, eine der beiden Lichtschranken abzufragen und anhand der Interrupts und der angegebenen Richtung (aus setDC) im IR- Handler die aktuelle Position zu bestimmen:

if (m_direction == LEFT) { position--; if (position < 0) position = 0;

}

else if (m_direction == RIGHT) { position++; if (position > width) position = width;

}

Dieser Ansatz ist fehleranfällig, da die Drehrichtung des Motors natürlich nicht immer der vorgegebenen Richtung entspricht (z.B. durch die Trägheit bei abrupten Richtungsänderungen). Um die Drehrichtung bestimmen zu können, müssten aber auch die Interrupts der zweiten Lichtschranke berücksichtigt werden, was aufgrund fehlender IR-Register leider nicht realisiert werden konnte. Eine Möglichkeit wäre die Nutzung des „Port-Change-Interrupts“ aus dem INTCON-Register. Bei frühen Tests führte dieser jedoch zu erheblichen Störungen und wurde zunächst abgeschaltet. Für zukünftige Weiterentwicklungen könnte an dieser Stelle nochmals angesetzt werden.

Für das Pendel selbst werden zwingend beide Lichtschranken benötigt, da nur so die Drehrichtung bestimmt werden kann. Die Handler merken sich zunächst den Zustand der jeweils anderen Lichtschranke, also

ls2_old = ls2; bzw. ls1_old = ls1;

und lesen dann den aktuellen Zustand beider Lichtschranken ein

ls1 = LS1; ls2 = LS2;

Anschließend wird der Wert der jeweils anderen Lichtschranke mit ihrem gespeicherten alten Wert verglichen, da eine Richtungsbestimmung nur dann möglich ist, wenn sich dieser verändert hat, und anhand der beiden Lichtschrankenwerte die Drehrichtung bestimmt, z.B.

if (ls2 != ls2_old) { if (ls2 == ls1) { p_direction = RIGHT;

ir_ls1++;

// dreht rechts herum

}

else {

 

p_direction = LEFT;

// dreht links herum

ir_ls1--;

}

}

Der „Winkel“ ergibt sich dann stets aus der Summe der beiden individuellen „Winkelzähler“

angle = ir_ls1 + ir_ls2;

Dieser Wert wird nicht in Grad umgerechnet und basiert auf dem „Ruhepunkt“ 0, d.h. er nimmt auch negative Werte entsprechend der Drehrichtung an.

Folgende Skizze lässt den allgemeinen Zusammenhang der Lichtschranken gut erkennen:

allgemeinen Zusammenhang der Lichtschranken gut erkennen: Am Ende der IR-Routinen passiert wie bereits erwähnt ein

Am Ende der IR-Routinen passiert wie bereits erwähnt ein Toggeln der Taktflanken, so dass die nächste abfallende oder ansteigende Taktflanke wieder einen IR liefert. Beispiel für die erste Lichtschranke:

if (ls1) INTCON2bits.INTEDG0 = 0;

else

INTCON2bits.INTEDG0 = 1;

Somit werden nie zwei ansteigende Taktflanken direkt hintereinander registriert und die Problematik des „Flatterns“ (Eindruck, dass sich das Pendel kontinuierlich weiterbewegt, obwohl es auf der Stelle „zittert“) vermieden. Die Winkeländerung wird in der Timer0-Interruptroutine berechnet und bezieht sich dann auf einen auftretenden IR:

// Winkelaenderung pro Timer-IR ermitteln dang = angle - angle_old; angle_old = angle;

IV.2 Zentrieren

Idee:

Die Ausgangsposition des Schlittens ist nicht von Bedeutung. Zunächst wird der rechte Rand der Schiene gesucht, indem der Schlitten solange nach rechts fährt, bis keine Interrupts mehr reinkommen. Danach wird er ganz nach links bewegt und wieder zurück, d.h. der Schlitten legt die komplette Strecke der Schiene zweimal zurück. Die Interrupts werden in einer Variablen X abgespeichert. Bei der eigentlichen Zentrierung steht der Schlitten ganz rechts und fährt X/4 nach links. Die automatische Zentrierung kann in global.h abgeschaltet werden, wobei dann ein Erfahrungswert für die mittlere Position verwendet wird. Ein manuelles Bewegen des Schlittens in eine zentrale Position ist dann vor Programmausführung unbedingt notwendig.

zentrale Position ist dann vor Programmausführung unbedingt notwendig. Implementierung: void center( void ) { 21 /27

Implementierung:

void center(void)

{

}

centered = FALSE; move(DC_CENTER); width = move(-DC_CENTER);

position = width = (width + move(DC_CENTER)) / 2;

go(-DC_CENTER, width / 2);

setDC(0);

left = width / LIMIT; right = width - left; position = width / 2; centered = TRUE;

// Rechten Rand finden // Erster Durchlauf

// In die Mitte fahren // Stoppen

// Position setzen

IV.3 Aufschwingen

// Zweiter Durchlauf

Idee:

Nach dem Zentrieren befindet sich das Pendel in der Mitte der Schiene in einer Ruhephase (senkrecht nach unten). In dieser Ausgangssituation ist der Winkel 0°. Dann bekommt es einen Impuls und beginnt zu pendeln. Sobald das Pendel den 0°- Punkt überschreitet, bekommt es einen Impuls in die entgegengesetzte Richtung. Dies wird solange wiederholt, bis das Pendel über die aufrechte Lage hinaus schwingt. Dann wird der „Winkel“ per Modulo umgerechnet, so dass sich der neue Nullpunkt nun oben befindet und über eine globale Variable (is_up) angezeigt, dass der Regler einsetzen kann. Der Aufschwingvorgang kann auch in global.h abgeschaltet werden. Vor Programmstart sollte dann das Pendel per Hand senkrecht aufgerichtet werden.

Implementierung:

void start()

{

int i; // "Anstossen" for (i = 0; i < 3; i++){ go(CRACK, width / 6); go(-CRACK, width / 6);

}

// "Aufschwingen" while(!is_up) { // Pendel aufgeschwungen? if ((angle > HALFTURN) || (angle < -HALFTURN)) { angle %= HALFTURN; is_up = TRUE;

 

}

if((p_direction == RIGHT) && (angle > -2) && (angle < 0)){ go(CRACK, width / 6);

}

if((p_direction == LEFT) && (angle < 2) && (angle > 0)){

 

go(-CRACK, width / 6);

 

}

}

}

IV.4 Randabbruchbedingung

Idee:

Die Randabbruchbedingung wird benötigt, da die Schienen doch sehr kurz sind (37cm). Wenn man dann noch den Schlitten von 8cm abzieht, hat man nur noch einen Spielraum von ca. 30cm. Diese sind auch sehr schnell ausgeschöpft, so dass der Schlitten immer wieder mit hoher Geschwindigkeit gegen den Rand fährt. Sobald die Position des Schlittens rechts oder links einen bestimmten Wert über- bzw. unterschreitet, wird der Motor angehalten und das Programm abgebrochen (es verharrt in einer Endlosschleife).

Implementierung:

Nach der Zentrierung werden die Grenzen festgelegt:

left = width / LIMIT; right = width - left;

wobei LIMIT einfach ein definierter Bruchteil der Gesamtbreite der Schiene ist. Im Interrupt-Handler des (Schlitten-)Motors wird dann immer die aktuelle Position entsprechend geprüft und bei Erreichen des Randes der „Notstop“ ausgelöst:

if ((position < left) || (position > right)) stop();

IV.5 Regelalgorithmen

Die Regelalgorithmen sind in der Timer0-Interrupt-Routine implementiert. In global.h lässt sich die gewünschte Reglerart einstellen oder die Regelung komplett abschalten. Zunächst prüft das Programm, ob eine Regelung überhaupt noch möglich ist, oder ob das Pendel bereits „fällt“:

if ((angle > ABORT) || (angle < -ABORT)) {

setDC(0);

}

Jeder Regler errechnet einen Wert power für die Pulsweitenmodulation. Anschließend wird dieser Betrag um einen Offset zur Überwindung der Reibung auf der Schiene erhöht:

if (power < 0) { power -= DC_OFFSET;

}

else if (power > 0) { power += DC_OFFSET;

}

Zuletzt wird überprüft, ob sich das Pendel in einem stabilen senkrechten Zustand befindet. Ist dies der Fall wird der Motor angehalten, ansonsten wird der errechnete power Wert an die PWM übergeben:

if ((angle < ANGLE_STABLE) && (angle > -ANGLE_STABLE)) {

 

setDC(0);

}

else {

 

setDC(power);

}

IV.5.1 P-Regler

Der P-Regler multipliziert einen konstanten Faktor k mit dem Winkel e(t).

Allgemeinen Formel:

u(t) = k · e(t)

Für unsere Anwendung ist der P-Regler ungeeignet, da die Reglung linear zum Eingangssignal reagiert. Es gibt also kein „Übersteuern“, das heißt, im Idealfall kann das Pendel nicht weiter fallen. Bei kleinen Winkelveränderungen verhält sich die Reglung ähnlich wie ein Kraftverstärker.

Implementierung:

case P_REGLER:

power = 3 * angle; break;

// 3: Prototypischer experimenteller Wert

IV.5.2 PI-Regler

Der PI-Regler bestimmt den Stellwert durch zeitliche Integration des Winkels e() mit Gewichtung durch die Nachstellzeit T (I-Regler), welche wir 1 gesetzt haben, addiert mit dem Eingangswinkel e(t). Dies wird dann mit einem konstanten Faktor k multipliziert (P- Regler).

Allgemeinen Formel:

u(t) = k ( e(t) + 1/T N 0 t e() d)

Der PI-Regler ist im Vergleich zum P-Regler viel besser. Bei kleinen Winkelveränderungen funktioniert die Reglung sehr gut. Für größere Winkelveränderungen ist der PI-Regler jedoch zu langsam.

Implementierung:

case PI_REGLER:

iangle += angle; // Integrieren power = 1 * (angle + iangle / 1); // 1: experimenteller Wert break;

IV.5.3 PID-Regler

Der PID-Regler besitzt im Grunde den gleichen Vorteil wie der PI-Regler. Der D-Anteil wird aus der zeitlichen Ableitung des Winkels berechnet (T V d/dt). Je schneller sich die Abweichung verändert, desto größer ist der Stellwert. Auf konstante Abweichungen hat der D-Anteil keinen Einfluss. T N = T V = 1 gesetzt.

Allgemeinen Formel:

u(t) = k ( e(t) + 1/T N

e(t) dt + T V d/dt e(t) )

Im Gegensatz zum PI-Regler ist der PID-Regler in der Theorie für größere Winkelveränderungen geeigneter. In der Praxis kann man keinen deutlichen Unterschied erkennen. Das kann aber auch auf das doch sehr große und schwerfällige Pendel zurückgeführt werden.

Implementierung:

case PID_REGLER:

iangle += angle; // Integrieren power = 1 * (angle + iangle / 1 + dang * 1); //experimenteller Wert break;

V. Ausblick und Fazit

Die Hardware ist sehr empfindlich, durch leichte Berührung an den Kabeln kann es schon zu Wackelkontakten führen, was höchstwahrscheinlich an der nicht ganz passenden Stromversorgungsbuchse liegt. Dies ließe sich mit Hilfe eines Oszilloskopen genauer verifizieren. Ein weiteres Probleme ist, dass einige Interrupts nicht wahrgenommen werden. Leider konnten wir noch nicht erörtern, woran das liegt. Auch die Implementierung des Displays brachte unerwartet Problematiken mit sich. Es ist sehr interessant zu erleben, wie empfindlich ein System sein kann, und dass der Teufel doch recht oft im Detail steckt.

Einige Ideen, um das Pendel „noch besser zu machen“, haben wir leider aus zeitlichen Gründen nicht realisieren können.

Pendel kleiner und zierlicher machen, jedoch schwerer, damit es träger wird

Schienen verlängern

Der Microcontroller bearbeitet im Moment die Interrupts, sprich Sensorik und die Steuerung. Die einzelnen Aufgaben könnte man auf mehrere Microcontroller verteilen, um Störanfälligkeit zu verringern. Diese Modularität erhöht die Wartbarkeit des Systems und die Performance.

Dreipunktregler

Regler mit Beschleunigung, d.h. der neue PWM-Wert wird durch Addition des aktuellen PWM-Wertes und des Winkels (multipliziert mit einem zu ermittelnden Faktor) berechnet

genauere Positionsbestimmung beim Motor durch Hinzunahme der 2. Lichtschranke, nochmaliger Test des Port-Change-Interrupts.

Verringerung der Reibung auf der Schiene; bisherige Versuche mit Silikonspray und Paste führten nicht zum Erfolg