Hier werden die Unterschiede zwischen zwei Versionen gezeigt.
projektesose2016:robboardpublic:start [2016/10/26 17:14] c.jaedicke angelegt |
projektesose2016:robboardpublic:start [2016/10/26 17:16] (aktuell) c.jaedicke |
||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
- | {{:projektesose2016:robboard:roboard.pdf|}} | + | ======Projektdokumentation====== |
- | + | ||
- | [[projektesose2016:robboard:doku|projektesose2016:robboard:doku]] | + | |
- | ======RoBoard====== | + | |
- | + | ||
- | + | ||
- | ======Projektplanung====== | + | |
+ | {{ :projektesose2016:robboard:fotoprofil.jpg |}} | ||
=====Einführung===== | =====Einführung===== | ||
- | RoBoard ist ein Roboter, der in erster Linie die Tafel wischt. Er soll sich zu dem vom Benutzer angegebenen Bereich bewegen und in diesem vorhandene Schrift entfernen. Eventuell soll er auch noch schreiben können. | ||
- | =====Prioritäten===== | + | RoBoard ist ein Roboter, der die Tafel wischen kann, wobei unser Roboter vorrangig für Whiteboards ausgelegt ist. Er hält sich mit Hilfe von Magneträdern an der senkrechten Ebene. Er soll sich zu einem Bereich bewegen, der vom Benutzer am Rechner ausgewählt wurde und in diesem vorhandene Schrift entfernen. Durch die Bildauswertung eines Kamerabildes weiß der Roboter bereits bevor er sich bewegt, ob in dem Bereich Schrift vorhanden ist, sodass er sich im entgegengesetzten Fall gar nicht erst zu bewegen braucht, sondern nur eine Rückmeldung an den Benutzer gibt. |
- | Er MUSS sich auf der Tafel positionieren und zum gewünschten Breich hin bewegen und die Tafel wischen können. | + | Ist der Wischvorgang abgeschlossen, kehrt der Roboter zu seinem Ausgangspunkt zurück und ist erneut einsatzbereit. |
- | Er SOLL die Schrift möglichst gründlich entfernen und auch nur genau den angegebenen Bereich säubern. | + | |
- | Wenn diese Teile funktionieren, wäre es NETT, den Roboter schreiben zu lassen und die Arbeit mit dem Kamerabild möglichst benutzerfreundlich zu gestalten, z.B. durch Markierung des zu wischenden Ausschitts statt durch Eingabe der exakten Koordinaten. | + | |
- | Bewusst VERZICHTEN wir auf die Steuerung des Stifts durch einen beweglichen Arm und auf die Nutzung von einer Sprühflasche, die die Nutzung auf einer Kreidetafel vereinfacht hätte. | + | |
- | =====Projektstrukturplan===== | ||
- | ===Gesamtprojekt=== | ||
- | RoBoard | ||
- | ====Aufgabenbereiche==== | ||
- | * Grundlagenrecherche | ||
- | * Physischer Aufbau | ||
- | * Bildauswertung | ||
- | * Dateneingabe | ||
- | * Schriftsuche | ||
- | * Datenverwertung | ||
- | * Kommunikation (Rechner <-> Arduino) | ||
- | * Ansteuerung des Roboters | ||
- | ====Grundlagenrecherche==== | + | =====Physischer Aufbau des Roboters===== |
- | Dazu gehört sich die physikalischen und mathematischen Grundlagen zu erarbeiten und eine Materialiste zu erstellen. | + | |
- | Das wird auch im Verlauf der weiteren eine Rolle spielen, da immer wieder neue Problemstellungen auftauchen werden. | + | |
- | Hierzu werden wir sowohl das Internet als auch Bücher und Fachzeitschriften zu Rate ziehen. | + | |
- | Zu Problemen könnten fehlerhafte Quellen und Berechnungen führen. | + | |
- | ====Physischer Aufbau des Roboters==== | + | ====Fahrende Plattform==== |
+ | {{:projektesose2016:robboard:konstruktiondraufsicht.png?300 |}} | ||
+ | {{ :projektesose2016:robboard:fotodraufsicht.jpg?300|}} | ||
- | ===Fahrende Plattform=== | + | Die Plattform des Roboters besteht aus einer runden Scheibe Holz. Wir haben uns bewusst für diese Form entschieden, damit es zu keinen Problemen kommt, wenn der Roboter nah am Rand der Tafel fährt, da es hier bei Kurven dazu kommen könnte, dass die Ecken über die Tafel stehen. |
- | Auf der fahrenden Plattform wird die Halterung für den Schwamm befestigt sein. Außerdem ein W- LAN Modul und eventuell ein Stift in der Mitte. Fortbewegt werden soll es mittels drei Räder samt Motoren. Der Roboter soll in der Lage sein, sich an der Tafel in alle Richtungen zu bewegen. | + | Auf der fahrenden Plattform ist die Halterung für den Schwamm befestigt, so dass der Schwamm vor dem Roboter hergeschoben wird, wenn dieser sich bewegt, außerdem ein W- LAN Modul und die Platine, auf der der Arduino und die restliche Elektronik gesteckt sind. |
- | ==Aufgaben== | + | Fortbewegt wird der Roboter mittels zweier Räder mit Getriebemotoren. Die Räder bestehen aus zwei Magnetscheiben, die wiederum von jeweils zwei Stahlscheiben umgeben sind. Dies dient der Umlenkung und Bündelung des Magnetfeldes. So haftet RoBoard an der Tafel und kann sich in alle Richtungen fortbewegen. |
- | * Planung der Plattform | + | Auf der Plattform sind außerdem zwei farbige Kreise angebracht, die der späteren Orientierung auf der Tafel dienen. |
- | * Besorgung der Materialien | + | |
- | * Bau der Plattform | + | ==Hierfür benöigte Materialien== |
- | ==Material== | + | * Arduino Nano |
- | * 3 Motoren (Leistung ist noch mit Hilfe des Drehmoments zu ermitteln) | + | |
- | * Akku zur Stromversorgung | + | |
- | * Räder mit Magentkern und Stahlummantelung (Verstärkung und Umlenkung des Magnetfelds) | + | |
- | * Magnetstreifen, um Räder zu umwickeln | + | |
* W- LAN Modul zur Datenübermittlung | * W- LAN Modul zur Datenübermittlung | ||
- | * gegebenenfalls Stift samt Halterung | + | * Akku: x A |
+ | * Kabel | ||
+ | * 2 Getriebemotoren: Pololu 3243 | ||
+ | * 2 Motortreibeplatinen: Pololu 1213 | ||
+ | * 2 Räder mit Magnetkern und Stahlummantelung (Verstärkung und Umlenkung des Magnetfelds) | ||
+ | |||
+ | * Holzplatte | ||
+ | * Kabelbinder | ||
+ | * Klettband | ||
* Schrauben, Muttern, etc. | * Schrauben, Muttern, etc. | ||
- | ==Wissen== | ||
- | Wir werden uns hierfür näher mit dem Drehmoment beschäftigen müssen, um die für den Motor benötigte Kraft berechnen zu können. | ||
- | Außerdem werden wir uns mit den Möglichkeiten zur Datenübertragung beschäftigen. | ||
- | ==Risiken== | ||
- | Risiken bestehen bei der Berechnung des Drehmoments, weil von der Korrektheit des Ergebnisses die spätere Funktionsfähigkeit des Roboters abhängt. Sonst könnte es passieren, dass es nicht möglich ist vertikal die Tafel hinunter zu fahren. | ||
- | ===Beweglicher Schwamm=== | + | ==Hintergrundwissen== |
- | Der Schwamm soll beweglich gelagert sein, so dass man den Schwamm nach oben von der Tafel weg klappen kann. Hierdurch soll es möglich sein, den Roboter über die Tafel zu bewegen, ohne sie dabei zu wischen. Die Halterung soll deswegen mit einem Motor an der Plattform befestigt sein. | + | Wir mussten uns zuerst näher mit dem Drehmoment beschäftigen müssen, um die für den Motor benötigte Kraft zu berechnen. |
- | ==Aufgaben== | + | Ein weiteres Problem stellten die Räder dar, da wir kein Material finden konnten, das stark genug an der Tafel geheftet hat, um ein Abrutschen durch das Eigengewicht des Roboter zu verhindern. Unsere momentane Zwischenlösung besteht in doppelseitigem Klebeband, da so zumindest ein Zerkratzen der Tafel und ein Abrutschen des Roboters vermieden wird. |
- | * Planung der Halterung | + | |
- | * Bau der Halterung | + | {{ :projektesose2016:robboard:konstruktionprofil.png |}} |
- | ==Material== | + | |
- | * Motor zu Bewegung des Schwammes | + | ====Beweglicher Schwamm==== |
- | * Alustangen für die Halterung des Schwamms | + | Der Schwamm ist beweglich gelagert, so dass man den Schwamm nach oben von der Tafel weg klappen kann. Hierdurch ist es möglich sein, den Roboter über die Tafel zu bewegen, ohne sie dabei zu wischen. Die Halterung ist deswegen durch ein Seil mit einem Servo an der Plattform verbunden, der durch ein Aufwickeln des Seils auf ein Laufrad den Schwamm abhebt. |
+ | Wird der Schwamm abgesenkt ziehen ihn Federn, die zwischen der Plattform und der Halterung befestigt sind, gegen die Tafel, wodurch die benötigte Kraft zum Säubern erzeugt wird. | ||
+ | |||
+ | ==Hierfür benötigte Materialien== | ||
+ | * Servo zu Bewegung des Schwammes | ||
+ | * Alustangen und Klemmringe für die Halterung des Schwamms | ||
* Schwamm für Whiteboard | * Schwamm für Whiteboard | ||
* Schrauben, Muttern, etc. | * Schrauben, Muttern, etc. | ||
- | ==Wissen== | ||
- | Wir werden zu den Möglichkeiten für Motoren der Halterung recherchieren. Hierbei ist die benötigte Kraft um den Schwamm in der Luft zu halten und ihm ihn fest auf die Tafel zu drücken zu berücksichtigen. | ||
- | ==Risiken== | ||
- | Ein Risiko könnte sein, dass der Schwamm nicht fest genug auf die Tafel gedrückt wird, um sie gründlich zu reinigen, weil der Motor der Halterung nicht stark genug ist. | ||
- | ===Bildaufnahme=== | + | ==Hintergrundwissen== |
- | Die Tafel soll durch eine fest installierte Kamera aufgenommen werden. Die Übertragung der Bilder der Kamera auf den Rechner soll wenn möglich ebenfalls über W- LAN erfolgen. | + | Wir mussten uns eine Möglichkeit überlegen, wie wir den Schwamm flexibel befestigen können und er trotzdem genug Kraft aufbringen kann, um die Tafel zu säubern. Das haben wir nun durch die felxible Halterung und die Federn, die den Schwamm gegen die Tafel ziehen, gelöst. |
- | Für die Auswertung der Daten u.ä. siehe „Bildauswertung“. | + | |
- | ==Aufgaben== | + | |
- | Verknüpfung der Kamera mit dem Rechner | + | |
- | ==Material== | + | =====Bildverarbeitung===== |
- | * Kamera | + | Ein wesentlicher Punkt, der zum Funktionieren unserer Idee von Nöten war, war die Aufnahme der Tafel und der sich auf ihr befindenden Schrift. |
- | * W-LAN Modul | + | |
+ | ====Bildaufnahme==== | ||
+ | Die Tafel wird durch eine fest installierte Kamera aufgenommen. Wir haben hierfür das Modell x verwendet. Die Übertragung der Bilder der Kamera auf den Rechner erfolgt über ein USB-Kabel. | ||
+ | |||
+ | ==Hierfür benöigte Materialien== | ||
+ | * Kamera mit USB-Kabel | ||
====Bildauswertung==== | ====Bildauswertung==== | ||
+ | ===Schrifterkennung=== | ||
+ | |||
+ | {{:projektesose2016:robboard:screenshot1.png?300 |}} | ||
+ | |||
+ | Zuerst haben wir die Daten die das Kamerabild übermittelt, eingelesen und ein von uns erarbeitetes Processing-Programm zur Auswertung der Daten genutzt. Dieses betrachtet jedes einzelne Pixel und färbt es, wenn es unter dem Grenzwert x liegt weiß und wenn es darüber liegt schwarz. | ||
+ | Als weitere Option lassen wir den roten und blauen Kreis, die auf der Plattform befestigt sind und zur Orientierung des Roboters dienen, jeweils rot und blau einfärben. Hierfür arbeiten wir den RGB-Werten eines jeden Pixels. Pixel, deren rot-Anteil größer als das 1.7-fache jeweils des blau- und grün-Anteils ist, werden rot gefärbt. Analog werden diejenigen Pixel, deren blau-Anteil den rot- und grün-Anteil um ein mindestens 1.7-faches übersteigt, blau eingefärbt. | ||
+ | |||
+ | |||
+ | {{ :projektesose2016:robboard:rgbdiagramm.jpg?300|}} | ||
+ | |||
+ | Pixel, deren RGB-Werte "innerhalb" des roten Körpers liegen, werden rot eingefärbt. Diejenigen innerhalb des blauen, blau und alle anderen schwarz bzw. weiß. | ||
+ | |||
+ | |||
+ | Die Einfärbung der Kreise sorgt für eine bessere Übersichtlichkeit für den Nutzer. | ||
+ | So entsteht ein, bis auf die besagten Kreise, schwarz-weißes Bild der Tafel. | ||
+ | Dem Benutzer ist es möglich per Mausklick ein rechteckiges Feld auszuwählen. Das Programm überprüft dann, ob innerhalb dieses Kästchens Schrift, also schwarze Pixel vorhanden sind. | ||
+ | Ist dies der Fall, sendet es ein Signal an den Roboter, so dass dieser weiß, dass er wischen muss. | ||
+ | |||
+ | Fehlerhafte Signale könnten hierbei durch Flecken oder Spiegelungen verursacht werden. Um dem vorzubeugen, ist es in der Testphase wichtig, auf einen sauberen Hintergrund und gute Lichtverhältnisse zu achten. | ||
+ | |||
+ | ---- | ||
+ | |||
===Position des Roboters=== | ===Position des Roboters=== | ||
- | In diesem Abschnitt geht es darum, wie die durch die Kamera aufgenommenen Bilder ausgewertet werden. Das Bild soll den Roboter (für den Nutzer auf dem Rechner sichtbar) innerhalb des Systems lokalisieren. | + | {{ :projektesose2016:robboard:screnshot2.png|}} |
- | ==Aufgaben== | + | Wie schon oben beschrieben, nutzen wir zwei farbige Kreise, um die Position des Roboters zu bestimmen. Das Programm berechnet aus den aufgenommenen roten Pixeln den Mittelpunkt und nutzt diesen als Orientierungswert für den Mittelpunkt des Roboters. |
- | Das Kamerabild soll mit einem Koordinatensystem verknüpft werden und so die genaue Position aus den übermittelten Daten bestimmt werden. Die Position des Roboters soll für den Nutzer klar erkennbar sein, deswegen soll das Koordinatensystem auch auf dem Bildschirm sichtbar sein. | + | Aus den aufgenommen blauen Pixeln wird ebenfalls der Mittelpunkt berechnet. Dieser dient als Orientierungswert für das vordere Ende des Roboters. |
- | ==Material== | + | Nun können wir bestimmen, in welche Richtung der Roboter schaut und so später berechnen, wie er zu dem ausgewählten Kästchen steht und welche Bewegungen er ausführen muss, um dorthin zu kommen. |
- | * eigener Rechner | + | |
- | * Bildverarbeitungsprogramm | + | |
- | ==Wissen== | + | =====Datenverwertung===== |
- | Wir werden Wissen über die Verarbeitung von Bilddaten auf dem Rechner benötigen. Außerdem, wie wir ein Koordinatensystem erstellen und die gemessenen Daten darin einfügen können. | + | In diesem Abschnitt geht es darum, die, durch Bildauswertung und Eingabe des Nutzers, gesammelten Daten auszuwerten, so dass diese für die Ansteuerung des Roboters genutzt werden können. |
- | ==Risiken== | + | Der Roboter bekommt, wie im Abschnitt „Schrifterkennung“ beschrieben, ein Signal gesendet, wenn in dem ausgewählten Bereich Schrift vorhanden ist. |
- | Risiken bestehen hierbei in einer ungenauen Justierung der Kamera, die zu fehlerhaften Daten führen könnte. Außerdem in einer fehlerhaften Einordnung der Koordinaten. | + | Nun geht es darum, den Weg zu der linken oberen Ecke des Kästchens auf Grundlage seiner aktuellen Position und Richtung zu berechnen. |
+ | Mit Hilfe des roten und des blauen Punktes können wir berechnen, in welchem Winkel sich der Roboter zu dem ausgewählten Bereich befindet. Außerdem können wir die Distanz zwischen Roboter und Kästchen berechnen. | ||
+ | Mit Hilfe dieser berechneten Werte und den bekannten Daten des Roboters, wie z.B. Radumfang und der Umfang der Holzplatte können wir berechnen, wie oft sich die Räder drehen müssen, um die gewollte Distanz bzw. den gewollten Winkel zurückzulegen. | ||
+ | Diese Werte sind auch die, die dann für die Radansteuerung genutzt werden. | ||
+ | |||
+ | ====Informationsfluss==== | ||
+ | |||
+ | {{:projektesose2016:robboard:informationsfluss.jpg[[|}} | ||
+ | |||
+ | |||
+ | ====Wegberechnung==== | ||
+ | ==1. Um einen Winkel drehen== | ||
+ | Will man den Roboter in eine bestimmte Richtung zum Beispiel in Richtung des Kästchens drehen (siehe Skizze), berechnet das Programm zunächst mithilfe des Arcustangens die Winkel $\alpha_1$ und $\alpha_2$. Aus der Differenz dieser Winkel ergibt sich der gesuchte Winkel $\alpha$. | ||
+ | Für die Anzahl der Radumdrehungen, die nun benötigt werden, um den Roboter um diesen Winkel zu drehen, berechnet das Programm mithilfe der Formel: | ||
+ | \[Strecke = \frac{winkel \cdot robumfang} {2 \cdot \pi}\] die zu fahrende Strecke in cm. | ||
+ | Diese Strecke wird dann durch den Radumfang geteilt, um die Anzahl der Umdrehungen zu erhalten. | ||
+ | |||
+ | {{:projektesose2016:robboard:wegberechnung_winkel.jpg|}} | ||
+ | |||
+ | ==2. Eine gerade Strecke fahren== | ||
+ | Wenn der Roboter eine gerade Strecke zwischen zwei Punkten fahren soll (siehe Skizze), muss man die Länge der Strecke, die man zunächst nur durch die Funktion dist(punkt1, punkt2) in Pixeln gegeben hat, in Zentimeter umrechnen. Dazu verwenden wir die farbigen Punkte auf dem Roboter, die mit 11cm einen festen Abstand haben. | ||
+ | Mit der Gleichung \[ \frac {Punktdistanz} {11cm} = \frac {Zieldistanz} {Strecke} \] können wir nun durch Umstellen die zu fahrende Strecke in cm berechnen. Die Distanzen sind hierbei in Pixeln gegeben. | ||
+ | Anschließend werden noch die gesuchte Anzahl an | ||
+ | Radumdrehungen durch die Formel \[Umdrehungen = \frac{strecke} {Radumfang} \] berechnet. | ||
+ | |||
+ | {{:projektesose2016:robboard:wegberechnung_strecke.jpg|}} | ||
+ | |||
+ | =====Motoren===== | ||
+ | ====Radansteuerung==== | ||
+ | Die Radansteuerung beschäftigt sich mit der eigentlichen mechanischen Arbeit des Roboters. Die beiden Räder werden jeweils von einem Getriebemotor angetrieben. | ||
+ | Wenn sich beide Räder gleich schnell in die gleiche Richtung drehen, fährt der Roboter entweder vorwärts oder rückwärts. Drehen sich beide Räder gleich schnell in entgegengesetzte Richtungen, dreht sich der Roboter auf der Stelle. | ||
+ | Beide Motoren besitzen einen Encoder, der es möglich macht, die Anzahl der Umdrehungen der Räder zu zählen. | ||
+ | Mit Hilfe eines Processing-Programms, das die zuvor berechneten Werte für Distanz und Winkel nutzt, und einem Arduino-Programms, das die Befehle von Processing „übersetzt“, konnten wir so die Räder ansteuern. | ||
+ | In wesentliches Problem, welches sich hier aufgetan hat, bestand darin, dass sich die Räder, trotz gleich stark angelegter Spannung, nicht gleich schnell gedreht haben. Das lag vor allem an der unterschiedlichen Belastung der beiden Räder. | ||
+ | Wir haben deswegen versucht, ein Programm zu schreiben, um die Geschwindigkeiten immer wieder abzufragen und aneinander anzupassen. | ||
+ | Sobald der Roboter am Zielpunkt angelangt ist, sendet er ein Signal. | ||
+ | |||
+ | ====Schwammansteuerung==== | ||
+ | Wie bereits erwähnt, ist der Schwamm in einer beweglichen Halterung gelagert. Dadurch ist es möglich den Schwamm bei Bedarf von der Tafel abzuheben und so ohne zu wischen von einem Ort zum anderen zu gelangen. Das Hochheben erfolgt mittels eines Servos, der sich dreht und dadurch, dem Prinzip einer Seilwinde folgend, ein Seil auf ein Rad mit einer Laufrille aufwickelt. Sobald sowohl das Signal, dass Schrift vorhanden ist, als auch das Signal, dass der Roboter am Zielpunkt angekommen ist, gesendet wurden, wird der Schwamm abgesenkt. Als verstärkender Faktor wirken hier die Federn, die zwischen Schwammhalterung und Plattform angebracht sind und ziehen den Schwamm gegen die Tafel. | ||
+ | Um den Servo ansteuern zu können, mussten wir ein Programm für Arduino schreiben, das die Befehle von Processing für den Arduino „übersetzt“ hat, der dann die entsprechenden Anweisungen an den Servo sendet. | ||
+ | |||
+ | |||
+ | =====Kommunikation===== | ||
+ | Dieser Teilbereich beinhaltet sowohl die Kommunikation, also Datenübertragung, zwischen Rechner und Arduino, als auch die Kommunikation zwischen Kamera und Rechner. | ||
+ | Zwischen Kamera und Rechner wäre eine drahtlose Übertragung optimal, um unabhängig von der Position der Kamera den Rechner aufbauen zu können, ist aber nicht zwingend notwendig und wird von uns zunächst auch nicht verwendet. | ||
+ | Die Datenübertragung zwischen Rechner und Arduino muss drahtlos erfolgen. | ||
+ | Die zu übertragenden Daten sind bei der Kamera das Bild in Echtzeit, und bei dem Arduino die für die Rad- und Servoansteuerung berechneten Werte, sowie die von den Encodern eingelesenen Werte der Radumdrehungen. | ||
+ | Wir nutzen hierfür das W-LAN Modul x. Für die Kommunikation mit dem Arduino mussten wir „Übersetzungsprogramme“ schreiben. | ||
+ | |||
+ | ===Pinliste Arduino=== | ||
+ | ^ Pin ^ Bauelement ^ | ||
+ | | 2 | Motor 1 Hall A (Interrupt) | | ||
+ | | 3 | Motor 2 Hall A (Interrupt) | | ||
+ | | 5 | Motor 1 Input 1 | | ||
+ | | 6 | Motor 1 Input 2 | | ||
+ | | 7 | Motor 2 Hall B | | ||
+ | | 8 | Motor 1 Hall B | | ||
+ | | 9 | Servo | | ||
+ | | 11 | Motor 2 Input 1 | | ||
+ | | 12 | Motor 2 Input 2 | | ||
+ | | A0 | WLAN | | ||
+ | | A1 | WLAN | | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | =====Endergebnis===== | ||
+ | Die Bildverarbeitung funktioniert bereits sehr gut und auch die Wegberechnung ist fertig. Vor dem Blockkurs hatten wir noch Probleme mit der Radansteuerung von RoBoard, da wir auf keine gemeinsame Geschwindigkeit kommen und der Roboter dadurch immer eine kleine Kurve fuhr, statt geradeaus zu fahren. | ||
+ | Nach einigen Änderungen am Programm, konnten wir das aber beheben. | ||
+ | Aktuell hatten wir noch Probleme damit, den Roboter zu der linken oberen Ecke des ausgewaehlten Bereichs zu bewegen. Zwar kann der Roboter bei Testprogrammen alle Bewegungen problemlos ausfuehren, in dem Gesamtprogramm scheinen jedoch noch Fehler in der Wegberechnung oder Ansteuerung zu liegen. | ||
+ | Einige mechanische Feinheiten müssten noch verbessert werden, wie z.B. die Befestigung des Akkus auf der Plattform. | ||
+ | |||
+ | |||
+ | =====Links===== | ||
+ | Bisheriger Programmcode: | ||
+ | Arduinoprogramm: | ||
+ | <file c Steuerung_Arduino.zip> | ||
+ | #include <Servo.h> | ||
+ | #include <SoftwareSerial.h> | ||
+ | |||
+ | //Anpassen: | ||
+ | const int minWert = 180; // untere Grenze für die Regelung der Motorausgänge | ||
+ | const int maxWert = 220; // obere Grenze für die Regelung der Motorausgänge | ||
+ | const int k = 20; //Proportionalitätsfaktor für die Regelung | ||
+ | const unsigned int test = 0; //Debug-Modus. Für test = 1 wird eine Ausgabe über die HardwareSerial erzeugt | ||
+ | |||
+ | //Pins: | ||
+ | int M1In1 = 5; | ||
+ | int M1In2 = 6; | ||
+ | int M2In1 = 11; | ||
+ | int M2In2 = 12; | ||
+ | |||
+ | int M1EncA = 2; | ||
+ | int M1EncB = 8; | ||
+ | int M2EncA = 3; | ||
+ | int M2EncB = 7; | ||
+ | |||
+ | //Motor 1 | ||
+ | unsigned long encoderPos1 = 0; //Umdrehungszähler des Rades an Motor1 mit 1 Umdrehung = 2370,72 Werte von encoderPos1 (Übersetzung = 98,78, Messungen pro HallSensordrehung = 24 -> 24 *98,78 = 2370,72) | ||
+ | unsigned long messzeit1Alt = 0; //Zeitpunkt der letzten Messung durch den Hallsensor in mikrosekunden | ||
+ | float messgeschwindigkeit1; //gemessen Geschwindigkeit durch den Hallsensor | ||
+ | int hall1BAlt; //Wert von Hall1B bei der letzen Messung | ||
+ | boolean ersteMessung1; //Ist Messung an Motor1 die erste Messung | ||
+ | float motorAusgang1 = 200; //Standarausgabe an InputPin von Motor1 | ||
+ | boolean fahrtRichtung1 = false; //Fahrtrichtung true = vorwaerts, false = rückwärts | ||
+ | |||
+ | //Motor 2 | ||
+ | unsigned long encoderPos2 = 0;//Umdrehungszähler des Rades an Motor2 mit 1 Umdrehung = 2370,72 Werte von encoderPos2 (Übersetzung = 98,78, Messungen pro HallSensordrehung = 24 -> 24 *98,78 = 2370,72) | ||
+ | unsigned long messzeit2Alt = 0; //Zeitpunkt der letzten Messung durch den Hallsensor in mikrosekunden | ||
+ | float messgeschwindigkeit2; //gemessen Geschwindigkeit durch den Hallsensor | ||
+ | int hall2BAlt; //Wert von Hall2B bei der letzen Messung | ||
+ | boolean ersteMessung2; //Ist Messung an Motor2 die erste Messung | ||
+ | float motorAusgang2 = 200; //Standarausgabe an InputPin von Motor2 | ||
+ | boolean fahrtRichtung2 = true; //Fahrtrichtung true = vorwaerts, false = rückwärts | ||
+ | |||
+ | int a = 0; //Zähler für die Ausgabe | ||
+ | float umdrehungen = 0; //Sollwert für die Radbewegung | ||
+ | boolean fahren = false; //Soll der Roboter fahren? true = ja, false = nein | ||
+ | Servo myservo; | ||
+ | SoftwareSerial mySerial(A0, A1); //Wifly-Modul an A0 und A1 | ||
+ | |||
+ | void setup() | ||
+ | { | ||
+ | pinMode(M1In1, OUTPUT); //In1 Motor 1 | ||
+ | pinMode(M1In2, OUTPUT); //In2 Motor 1 | ||
+ | pinMode(M2In1, OUTPUT); //In1 Motor 2 | ||
+ | pinMode(M2In2, OUTPUT); //In2 Motor 2 | ||
+ | |||
+ | pinMode(M1EncA, INPUT); //Motor 1 Encoder A | ||
+ | pinMode(M1EncB, INPUT); //Motor 1 Encoder B | ||
+ | pinMode(M2EncA, INPUT); //Motor 2 Encoder A | ||
+ | pinMode(M2EncB, INPUT); //Motor 2 Encoder B | ||
+ | |||
+ | attachInterrupt(digitalPinToInterrupt(M1EncA), Hall1Change, CHANGE); //Interrupt durch den Encoder an Motor 1 | ||
+ | attachInterrupt(digitalPinToInterrupt(M2EncA), Hall2Change, CHANGE); //Interrupt durch den Encoder an Motor 2 | ||
+ | |||
+ | myservo.attach(9); //Servo initialisieren | ||
+ | |||
+ | Serial.begin(19200); //serielle Schnittstelle Initialisieren (Hardware) | ||
+ | mySerial.begin(19200); //serielle Schnittstelle Initialisieren (Software / Wifly) | ||
+ | Serial.println("Start"); | ||
+ | |||
+ | messgeschwindigkeit1 = 0; | ||
+ | messgeschwindigkeit2 = 0; | ||
+ | |||
+ | } | ||
+ | |||
+ | void loop() | ||
+ | { | ||
+ | if (test == 1) //Debugausgaben erzeugen | ||
+ | { | ||
+ | if (a >= 10000) //nur alle 10000 loop-Durchgänge eine Ausgabe erzeugen | ||
+ | { | ||
+ | Serial.print(messgeschwindigkeit1); | ||
+ | Serial.print('\t'); | ||
+ | Serial.print(encoderPos1); | ||
+ | Serial.print('\t'); | ||
+ | Serial.print(encoderPos1 / 2370.72); | ||
+ | Serial.print('\t'); | ||
+ | Serial.print(motorAusgang1); | ||
+ | Serial.print('\t'); | ||
+ | Serial.print(messgeschwindigkeit2); | ||
+ | Serial.print('\t'); | ||
+ | Serial.print(encoderPos2); | ||
+ | Serial.print('\t'); | ||
+ | Serial.print(encoderPos2 / 2370.72); | ||
+ | Serial.print('\t'); | ||
+ | Serial.print(motorAusgang2); | ||
+ | Serial.print('\n'); | ||
+ | |||
+ | a = 0; | ||
+ | } | ||
+ | a++; | ||
+ | } | ||
+ | |||
+ | if ((encoderPos1 > umdrehungen * 2370.72) || (encoderPos2 > umdrehungen * 2370.72)) // Wenn der Roboter die geforderte Anzahl an Runden gefahren ist | ||
+ | { | ||
+ | encoderPos1 = 0; //Umdrehungszähler zurücksetzen | ||
+ | encoderPos2 = 0; //Umdrehungszähler zurücksetzen | ||
+ | fahren = false; //stoppen | ||
+ | mySerial.write('f'); //An Processing Signal für die Beendigung senden | ||
+ | } | ||
+ | Eingabe(); //Überprüfen ob ein Signal von Processing kam | ||
+ | motorSteuerung(); //Räder steuern | ||
+ | } | ||
+ | |||
+ | void Eingabe() | ||
+ | { | ||
+ | if (mySerial.available()) // Wenn Daten empfangen wurden und zum Lesen bereitstehen | ||
+ | { | ||
+ | int val = mySerial.read(); // lese das erste Zeichen in der Warteschlange und speichere es in der Variable 'val' zwischen | ||
+ | switch (val) | ||
+ | { | ||
+ | case 'R': // schwamm hoch | ||
+ | for (int i = 20; i < 160; i++) //Servo drehen | ||
+ | { | ||
+ | myservo.write(i); | ||
+ | delay(20); | ||
+ | } | ||
+ | break; | ||
+ | case 'H': //schwamm runter | ||
+ | for (int i = 160; i > 20; i--) //Servo drehen | ||
+ | { | ||
+ | myservo.write(i); | ||
+ | delay(20); | ||
+ | } | ||
+ | break; | ||
+ | case 'v': //vorwaerts fahren | ||
+ | fahren = true; | ||
+ | fahrtRichtung1 = true; | ||
+ | fahrtRichtung2 = true; | ||
+ | motorAusgang1 = 200; | ||
+ | motorAusgang2 = 200; | ||
+ | umdrehungen = mySerial.parseFloat(); //zu fahrende Umdrehungen einlesen | ||
+ | break; | ||
+ | case 'b': //rueckwaerts fahren | ||
+ | fahren = true; | ||
+ | fahrtRichtung1 = false; | ||
+ | fahrtRichtung2 = false; | ||
+ | motorAusgang1 = 200; | ||
+ | motorAusgang2 = 200; | ||
+ | umdrehungen = mySerial.parseFloat(); //zu fahrende Umdrehungen einlesen | ||
+ | break; | ||
+ | case 'r': //rechts drehen | ||
+ | fahren = true; | ||
+ | fahrtRichtung1 = true; | ||
+ | fahrtRichtung2 = false; | ||
+ | motorAusgang1 = 200; | ||
+ | motorAusgang2 = 200; | ||
+ | umdrehungen = mySerial.parseFloat(); //zu fahrende Umdrehungen einlesen | ||
+ | break; | ||
+ | case 'l': //links drehen | ||
+ | fahren = true; | ||
+ | fahrtRichtung1 = false; | ||
+ | fahrtRichtung2 = true; | ||
+ | motorAusgang1 = 200; | ||
+ | motorAusgang2 = 200; | ||
+ | umdrehungen = mySerial.parseFloat(); //zu fahrende Umdrehungen einlesen | ||
+ | break; | ||
+ | case 's': //stoppen | ||
+ | fahren = false; | ||
+ | umdrehungen = 0; | ||
+ | break; | ||
+ | default: | ||
+ | break; | ||
+ | } | ||
+ | //mySerial.write(val); | ||
+ | } | ||
+ | } | ||
+ | void Hall1Change() | ||
+ | { | ||
+ | boolean illegal = false; //Falls der Encoder prellt oder ähnliches kommt ein Signal, es solla ber keine Geschwindigkeit berechnet werden, da das Signal kein gültiges war | ||
+ | |||
+ | //Encoderwerte einlesen | ||
+ | int hall1A = digitalRead(M1EncA); | ||
+ | int hall1B = digitalRead(M1EncB); | ||
+ | |||
+ | if (ersteMessung1 == true) //Erste Messung ist immer illegal | ||
+ | { | ||
+ | illegal = true; | ||
+ | ersteMessung1 = false; | ||
+ | } | ||
+ | if (hall1BAlt == hall1B) //Wenn sich HallA änder aber HallB nicht ist die Messung illegal | ||
+ | { | ||
+ | illegal = true; | ||
+ | } | ||
+ | |||
+ | unsigned long currTime1 = micros(); //aktueller Zeitpunkt | ||
+ | if (illegal == false) | ||
+ | { | ||
+ | messgeschwindigkeit1 = 1000000 / float((currTime1 - messzeit1Alt) * 24 * 98.78); //Geschwindigkeit in Umdrehungen pro Sekunde | ||
+ | if (hall1A == 1) | ||
+ | { | ||
+ | if (hall1B == 1) //Falls Hall1A = Hall1B, dann dreht der Motor rückwärts | ||
+ | { | ||
+ | messgeschwindigkeit1 *= -1; | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | if (hall1B == 0) //Falls Hall1A = Hall1B, dann dreht der Motor rückwärts | ||
+ | { | ||
+ | messgeschwindigkeit1 *= -1; | ||
+ | } | ||
+ | } | ||
+ | encoderPos1 += 1; //Umdrehungszähler erhöhen | ||
+ | } | ||
+ | |||
+ | //Vergleichsvariablen neu beschreiben | ||
+ | hall1BAlt = hall1B; | ||
+ | messzeit1Alt = currTime1; | ||
+ | } | ||
+ | |||
+ | void Hall2Change() | ||
+ | { | ||
+ | boolean illegal = false; //Falls der Encoder prellt oder ähnliches kommt ein Signal, es solla ber keine Geschwindigkeit berechnet werden, da das Signal kein gültiges war | ||
+ | |||
+ | //Encoderwerte einlesen | ||
+ | int hall2A = digitalRead(M2EncA); | ||
+ | int hall2B = digitalRead(M2EncB); | ||
+ | |||
+ | if (ersteMessung2 == true) //Erste Messung ist immer illegal | ||
+ | { | ||
+ | illegal = true; | ||
+ | ersteMessung2 = false; | ||
+ | } | ||
+ | if (hall2BAlt == hall2B) //Wenn sich HallA änder aber HallB nicht ist die Messung illegal | ||
+ | { | ||
+ | illegal = true; | ||
+ | } | ||
+ | |||
+ | unsigned long currTime2 = micros(); //aktueller Zeitpunkt | ||
+ | if (illegal == false) | ||
+ | { | ||
+ | messgeschwindigkeit2 = 1000000 / float((currTime2 - messzeit2Alt) * 24 * 98.78); //Geschwindigkeit in Umdrehungen pro Sekunde | ||
+ | if (hall2A == 0) | ||
+ | { | ||
+ | if (hall2B == 1) //Falls Hall1A != Hall1B, dann dreht der Motor rückwärts, da die Motoren auf einer Scheibe angeordnet sind und damit der Roboter vorwärts fährt, muss immer ein Motor vorwärts, und einer Rückwärts drehen | ||
+ | { | ||
+ | messgeschwindigkeit2 *= -1; | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | if (hall2B == 0) //Falls Hall1A != Hall1B, dann dreht der Motor rückwärts, da die Motoren auf einer Scheibe angeordnet sind und damit der Roboter vorwärts fährt, muss immer ein Motor vorwärts, und einer Rückwärts drehen | ||
+ | { | ||
+ | messgeschwindigkeit2 *= -1; | ||
+ | } | ||
+ | } | ||
+ | encoderPos2 += 1; //Umdrehungszähler erhöhen | ||
+ | } | ||
+ | |||
+ | //Vergleichsvariablen neu beschreiben | ||
+ | hall2BAlt = hall2B; | ||
+ | messzeit2Alt = currTime2; | ||
+ | } | ||
+ | |||
+ | |||
+ | int regelung() | ||
+ | { | ||
+ | float difGeschwindigkeit = abs(abs(messgeschwindigkeit1) - abs(messgeschwindigkeit2)); //Differenz der Messgeschwindigkeiten | ||
+ | float x = k * difGeschwindigkeit; //x ist der Wert um den der MotorAusgang1 geändert wird, abhängig von der Differenz der Messgeschwindigkeiten | ||
+ | |||
+ | if (abs(messgeschwindigkeit1) > abs(messgeschwindigkeit2)) //Wenn Motor1 schneller als Motor2 ist wird Motor1 verllangsamt, sonst beschleunigt | ||
+ | x *= -1; | ||
+ | else if (abs(messgeschwindigkeit1) == abs(messgeschwindigkeit2)) //oder keine änderung vorgenommen | ||
+ | return 1; | ||
+ | |||
+ | motorAusgang1 += x; //Änderung der Kraft an Motor1 | ||
+ | |||
+ | int differenz = motorAusgang1 - motorAusgang2; //Differenz der MotorAusgänge | ||
+ | |||
+ | //Verschieben der MotorAusgäm+nge in den Bereich zwischen minWert und maxWert: | ||
+ | if (differenz < 0) //motorAusgang1 < motorAusgang2 | ||
+ | { | ||
+ | if (motorAusgang1 < minWert) | ||
+ | { | ||
+ | motorAusgang1 = minWert; | ||
+ | motorAusgang2 = minWert + abs(differenz); | ||
+ | } | ||
+ | else if (motorAusgang2 > maxWert) | ||
+ | { | ||
+ | motorAusgang2 = maxWert; | ||
+ | motorAusgang1 = maxWert - abs(differenz); | ||
+ | } | ||
+ | } | ||
+ | else if (differenz > 0) //motorAusgang1 > motorAusgang2 | ||
+ | { | ||
+ | if (motorAusgang2 < minWert) | ||
+ | { | ||
+ | motorAusgang2 = minWert; | ||
+ | motorAusgang1 = minWert + abs(differenz); | ||
+ | } | ||
+ | else if (motorAusgang1 > maxWert) | ||
+ | { | ||
+ | motorAusgang1 = maxWert; | ||
+ | motorAusgang2 = maxWert - abs(differenz); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //Falls dadurch motorAusgang1 oder motorAusgang2 > 255 oder <0 geworden ist, verschieben in den Bereich zwischen 0 und 255: | ||
+ | constrain(motorAusgang1, 0, 255); | ||
+ | constrain(motorAusgang2, 0, 255); | ||
+ | if (differenz < 255) | ||
+ | { | ||
+ | if (motorAusgang1 == 255) | ||
+ | motorAusgang2 -= abs(differenz); | ||
+ | else if (motorAusgang1 == 0) | ||
+ | motorAusgang2 += abs(differenz); | ||
+ | if (motorAusgang2 == 255) | ||
+ | motorAusgang1 -= abs(differenz); | ||
+ | else if (motorAusgang2 == 0) | ||
+ | motorAusgang1 += abs(differenz); | ||
+ | } | ||
+ | return 1; | ||
+ | } | ||
+ | |||
+ | void motorSteuerung() | ||
+ | { | ||
+ | if (fahren == true) //Wenn er fahren soll | ||
+ | { | ||
+ | regelung(); //Motoren nachregeln | ||
+ | if (fahrtRichtung1 == true) //Motor1 vorwärts | ||
+ | { | ||
+ | analogWrite(M1In1, int(motorAusgang1)); | ||
+ | digitalWrite(M1In2, LOW); | ||
+ | } | ||
+ | else //Motor1 rückwärts | ||
+ | { | ||
+ | digitalWrite(M1In1, LOW); | ||
+ | analogWrite(M1In2, int(motorAusgang1)); | ||
+ | } | ||
+ | |||
+ | if (fahrtRichtung2 == true) //Motor2 vorwärts | ||
+ | { | ||
+ | analogWrite(M2In1, int(motorAusgang2)); | ||
+ | digitalWrite(M2In2, LOW); | ||
+ | } | ||
+ | else //Motor2 rückwärts | ||
+ | { | ||
+ | digitalWrite(M2In1, LOW); | ||
+ | analogWrite(M2In2, int(motorAusgang2)); | ||
+ | } | ||
+ | } | ||
+ | else //stoppen (fahren = false) | ||
+ | { | ||
+ | digitalWrite(M1In1, LOW); | ||
+ | digitalWrite(M1In2, LOW); | ||
+ | digitalWrite(M2In1, LOW); | ||
+ | digitalWrite(M2In2, LOW); | ||
+ | } | ||
+ | } | ||
+ | </file> | ||
+ | |||
+ | Processingprogramm: | ||
+ | <file java Roboard_Processing.zip> | ||
+ | import processing.video.*; | ||
+ | import processing.serial.*; | ||
+ | import java.io.*; | ||
+ | import java.net.*; | ||
+ | |||
+ | String kameraName = "Logitech HD Webcam C270"; //Bei Alissa(mac): "USB-Kamera" Bei Theresa(windows): "Logitech HD Webcam C270" | ||
+ | |||
+ | final int test = 0; //Debug Modus zur ausführlichen Ausgabe | ||
+ | final int tafelbreite = 175; //in cm | ||
+ | final int tafelhoehe = 114; //in cm | ||
+ | final int high = 480; //Kamerahöhe | ||
+ | final int wide = 640; //Kamerabreite | ||
+ | final float schwammbreite = 16; | ||
+ | final float radumfang = 19.3; | ||
+ | final float robumfang = 2*16.5*PI; | ||
+ | final float schwammabstand = 24; //ausgleichen vom ueberstehen des schwamm | ||
+ | final float punktabstand = 11; //Abstandzwischen den farbeigen Punkten (Mittelpunkte) | ||
+ | |||
+ | Capture video; | ||
+ | Serial port; | ||
+ | Socket clientSocket; | ||
+ | DataOutputStream outToServer; | ||
+ | InputStream inFromServer; | ||
+ | |||
+ | boolean schwarz; | ||
+ | boolean angekommen; | ||
+ | |||
+ | int maus = 0; //Variable zum Hochzählen bei Mausklick, um verschieden Zustände zu unterscheiden | ||
+ | int a = 50; //Seite a des ausgewählten Rechtecks per Mausklick | ||
+ | int b = 50; //Seite b des ausgewählten Rechtecks per Mausklick | ||
+ | int x_1, y_1, x_2, y_2; //Mauspositionen bei Klick 1 und 2 | ||
+ | |||
+ | int threshold = 130; //Grenzwert, ob Pixel schwarz oder weiß wird | ||
+ | color black = color(0); | ||
+ | color white = color(255); | ||
+ | color red = color(255, 0, 0); | ||
+ | color blue = color(0, 0, 255); | ||
+ | int numPixels; | ||
+ | float rot_xPos = 0; | ||
+ | float rot_yPos = 0; | ||
+ | float blau_xPos = 0; | ||
+ | float blau_yPos = 0; | ||
+ | |||
+ | char val = ' '; //Befehlsvariable für Arduino | ||
+ | |||
+ | int actualState = 0; //aktueller Modus des Arduino/Roboters | ||
+ | int programState = 0; //Sollmodus des Arduino/Roboters | ||
+ | |||
+ | float schwammumdrehungen = schwammbreite / radumfang; | ||
+ | float punktdistanz = 0; | ||
+ | float schwammdistanz = 0; | ||
+ | |||
+ | void setup() | ||
+ | { | ||
+ | size(640, 580); | ||
+ | strokeWeight(5); | ||
+ | videoStart(); | ||
+ | smooth(); | ||
+ | /*println(Serial.list()); | ||
+ | port = new Serial(this, Serial.list()[0], 9600);*/ | ||
+ | |||
+ | //Wlan einrichten | ||
+ | try | ||
+ | { | ||
+ | String serverAddress="192.168.0.100"; | ||
+ | int serverPort=8000; | ||
+ | clientSocket = new Socket(serverAddress, serverPort); | ||
+ | outToServer = new DataOutputStream(clientSocket.getOutputStream()); | ||
+ | inFromServer = clientSocket.getInputStream(); | ||
+ | } | ||
+ | catch(Exception e) | ||
+ | { | ||
+ | print("Verbindung aufbauen setup"); | ||
+ | println(e); | ||
+ | } | ||
+ | if (video.available()) | ||
+ | Bildfaerbung(); //Kamerabild lesen und Bild einfärben | ||
+ | servo(); //Servo hochfahren | ||
+ | delay(2000); | ||
+ | punktdistanz = dist(rot_xPos, rot_yPos, blau_xPos, blau_yPos); //Umrechnungsfaktor Pixel <-> Zentimeter in echt | ||
+ | schwammdistanz = (punktdistanz/punktabstand) * schwammabstand; //Umrechnung in cm | ||
+ | } | ||
+ | |||
+ | |||
+ | void draw() { | ||
+ | float winkel = 0; | ||
+ | char input = ' '; | ||
+ | if (antwortArduino() == 'f') //Wenn Arduino sagt, er ist fertig... | ||
+ | programState++; //...Sollmodus um 1 erhöhen -> in den nächsten Zustand gehen | ||
+ | |||
+ | if (video.available()) | ||
+ | { | ||
+ | Bildfaerbung(); //Bild aufnehmen und einfärben | ||
+ | |||
+ | //Bereich auswaehlen | ||
+ | if (maus == 1) //Erster Klick: Erste Ecke des Rechtecks speichern | ||
+ | { | ||
+ | x_1 = mouseX; | ||
+ | y_1 = mouseY; | ||
+ | maus = 2; | ||
+ | } | ||
+ | if (maus == 2) //Rechteck mit erster Ecke und Maus malen | ||
+ | { | ||
+ | stroke(255, 0, 0); | ||
+ | line(x_1, y_1, x_1, mouseY); | ||
+ | line(x_1, y_1, mouseX, y_1); | ||
+ | line(x_1, mouseY, mouseX, mouseY); | ||
+ | line(mouseX, y_1, mouseX, mouseY); | ||
+ | } | ||
+ | if (maus == 3) //Zweiter Klick: Zweite Ecke des Rechecks speichern | ||
+ | { | ||
+ | x_2 = mouseX; | ||
+ | y_2 = mouseY; | ||
+ | //Falls die erste Ecke ncht oben links ist und die zweite Ecke unten Rechts, müssen Ecken getauscht werden, damit im Programm die erste Ecke oben links und die zweite Ecke unten rechts ist. | ||
+ | if (x_2 < x_1) | ||
+ | { | ||
+ | int hilf = x_1; | ||
+ | x_1 = x_2; | ||
+ | x_2 = hilf; | ||
+ | } | ||
+ | if (y_2 < y_1) | ||
+ | { | ||
+ | int hilf = y_1; | ||
+ | y_1 = y_2; | ||
+ | y_2 = hilf; | ||
+ | } | ||
+ | //Seitenlängen berechnen | ||
+ | a = x_2 - x_1; | ||
+ | b = y_2 - y_1; | ||
+ | if (test == 1) | ||
+ | { | ||
+ | print("a= "); | ||
+ | print(a); | ||
+ | print("\tb= "); | ||
+ | print(b); | ||
+ | print("\tx_1= "); | ||
+ | print(x_1); | ||
+ | print("\ty_1="); | ||
+ | print(y_1); | ||
+ | print("\tx_2 ="); | ||
+ | print(x_2); | ||
+ | print("\ty_2= "); | ||
+ | println(y_2); | ||
+ | } | ||
+ | maus = 0; //Zurück auf Anfang für ein neues Kästchen bereit sein | ||
+ | |||
+ | //Bereich auf Schwarz untersuchen | ||
+ | schwarz = bereichTesten(a, b); | ||
+ | println(schwarz); | ||
+ | |||
+ | if (schwarz) { | ||
+ | text("Schwarz vorhanden", 20, height - 60); | ||
+ | } else { | ||
+ | text("Kein Schwarz vorhanden", 20, height - 60); | ||
+ | } | ||
+ | |||
+ | //Roboter dreht sich Richtung linkes Ende des Kaestchens | ||
+ | if (schwarz) | ||
+ | { | ||
+ | winkel = berechneWinkel(mouseX-punktdistanz, mouseY); //Drehwinkel berechnen | ||
+ | println(winkel); | ||
+ | // actualState = 1; | ||
+ | roboterDrehen(winkel); | ||
+ | schwarz = false; | ||
+ | } | ||
+ | if (actualState < programState) //Wenn aktueller Zustand kleiner als Sollzustand ist... | ||
+ | { | ||
+ | actualState++; //..in Sollzustand gehen | ||
+ | if ((actualState > 7) && (programState > 7)) //Wenn beide Zustände > 7 -> Roboter ist fertig -> Zustände zurücksetzen | ||
+ | { | ||
+ | actualState = 0; | ||
+ | programState = 0; | ||
+ | } | ||
+ | |||
+ | switch (actualState) | ||
+ | { | ||
+ | case 1: //zur linken oberen eck des kaestchens bewegen | ||
+ | delay(3000); | ||
+ | fahren(dist((int)rot_xPos, (int)rot_yPos, x_1-schwammdistanz, y_1)); | ||
+ | delay(3000); | ||
+ | |||
+ | case 2: //Roboter waagerecht drehen | ||
+ | winkel = berechneWinkel(rot_xPos+1, rot_yPos); | ||
+ | roboterDrehen(winkel); | ||
+ | println(winkel); | ||
+ | angekommen = true; | ||
+ | println(angekommen); | ||
+ | |||
+ | case 3: //Schwamm absenken | ||
+ | servo(); | ||
+ | |||
+ | case 4: //kaestchen abfahren | ||
+ | kaestchenAbfahren(); | ||
+ | angekommen = false; | ||
+ | |||
+ | case 5: //Schwamm hoch | ||
+ | servo(); | ||
+ | |||
+ | case 6: //Roboter zurueck drehen | ||
+ | winkel = berechneWinkel(100, 400); | ||
+ | roboterDrehen(winkel); | ||
+ | |||
+ | case 7: //Zum Startpunkt fahren | ||
+ | fahren(dist((int)rot_xPos, (int)rot_yPos, 100, 400)); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | if (maus == 0) { //Mit Klick fertig -> Kästchen malen, bis zum nächsten Klick | ||
+ | noFill(); | ||
+ | stroke(0, 255, 0); | ||
+ | rect(x_1, y_1, a, b); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | fill(255); | ||
+ | stroke(0); | ||
+ | rect(0, height - 100, width, 100); | ||
+ | fill(0); | ||
+ | text("(x,y) = (" + mouseX + ", " + mouseY + ")", 20, height - 80); | ||
+ | } | ||
+ | |||
+ | void mouseClicked() { //Mauszähler erhöhen | ||
+ | if (maus == 0) { | ||
+ | maus = 1; | ||
+ | } else if (maus == 2) { | ||
+ | maus = 3; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /*void keyReleased() { //Wlan Testfunktion | ||
+ | try | ||
+ | { | ||
+ | outToServer.writeBytes("v3"); | ||
+ | } | ||
+ | catch (Exception e) | ||
+ | { | ||
+ | println(e); | ||
+ | } | ||
+ | }*/ | ||
+ | |||
+ | void videoStart() | ||
+ | { | ||
+ | //Verfügbare Kameras suchen und ausgeben | ||
+ | String[] cameras = Capture.list(); | ||
+ | if (cameras.length == 0) | ||
+ | { | ||
+ | println("There are no cameras available for capture."); | ||
+ | exit(); | ||
+ | } else { | ||
+ | println("Available cameras:"); | ||
+ | for (int i = 0; i < cameras.length; i++) | ||
+ | { | ||
+ | println(i + " " + cameras[i]); | ||
+ | } | ||
+ | //Kamera initialisieren | ||
+ | video = new Capture(this, 640, 480, kameraName, 30); | ||
+ | |||
+ | //Kameraaufnahme starten | ||
+ | video.start(); | ||
+ | |||
+ | numPixels = video.width * video.height; //Anzahl der Pixel des Kamerabildes berechnen | ||
+ | println(numPixels); | ||
+ | } | ||
+ | } | ||
- | ===Schriftsuche=== | + | void Bildfaerbung() |
+ | { | ||
+ | //Kamerbild und Pixel lesen | ||
+ | video.read(); | ||
+ | video.loadPixels(); | ||
+ | float pixelBrightness; | ||
+ | loadPixels(); | ||
- | Das Programm sucht den vom Nutzer angegebenen Bereich nach Schrift ab. Falls diese vorhanden ist, geht es in den nächsten Schritt der Datenverarbeitung. | + | //Variablen initialisieren |
+ | int currPos = 0; | ||
+ | color tempPixel; | ||
+ | int rotxmax = 0; | ||
+ | int rotxmin = wide; | ||
+ | int rotymax = 0; | ||
+ | int rotymin = high; | ||
- | ==Aufgaben== | + | int blauxmax = 0; |
+ | int blauxmin = wide; | ||
+ | int blauymax = 0; | ||
+ | int blauymin = high; | ||
- | * Schrift im angegebenen Bereich erkennen | + | for (int y = 1; y <= 480; y++) //Y-Achse des Bildes durchgehen (Zeilenweise) |
- | * Entscheiden (je nachdem, ob Schrift vorhanden ist), ob der nächste Schritt eingeleitet werden muss | + | { |
+ | for (int x = 1; x <= 640; x++) //X-Achse des Bildes durchgehen (Spaltenweise) | ||
+ | { | ||
+ | currPos = ((y-1) * wide) + (x-1); //von x,y-Koordinaten in Array-Position umrechnen | ||
+ | pixelBrightness = brightness(video.pixels[currPos]); //Helligkeit des Pixels speichern | ||
+ | tempPixel = video.pixels[currPos]; | ||
- | ==Material== | + | //int pixelColor = video.pixels[currPos]; |
+ | int r = (tempPixel >> 16) & 0xff; //Rotanteil des Pixels bestimmen | ||
+ | int g = (tempPixel >> 8) & 0xff; //Grünanteil des Pixels bestimmen | ||
+ | int b = tempPixel & 0xff; //Blauanteil des Pixels bestimmen | ||
- | * Computer | + | if (r > 1.7*g && r > 1.7*b && r>50) //Wenn Rotanteil überwiegt... |
- | * Daten aus Bildauswertung | + | { |
+ | pixels[currPos] = color(255, 20, 20); //...Pixel rot färben | ||
- | ==Wissen== | + | //Ersten und letzten rote Pixel bestimmen, sowhl auf der x-Achse, als auch auf der y-Achse |
+ | if (x < rotxmin) | ||
+ | rotxmin = x; | ||
+ | else if (x > rotxmax) | ||
+ | rotxmax = x; | ||
+ | if (y < rotymin) | ||
+ | rotymin = y; | ||
+ | else if (y > rotymax) | ||
+ | rotymax = y; | ||
+ | } | ||
+ | /* else if(g > 1.5*r && g > 1.5*b && g>20) | ||
+ | pixels[currPos] = color(0, 255, 0);*/ | ||
- | Wir müssten noch herausfinden, wie das Programm Schrift erkennen kann. | + | else if (b > 1.7*r && b > 1.7*g && b>50) { //Wenn Blauanteil überwiegt... |
- | ==Risiken== | + | pixels[currPos] = color(0, 0, 255); //...Pixel blau färben |
- | Flecken oder Spiegelungen könnten fälschlicherweise als Schrift erkannt werden und dann wird dort ungewollt gewischt. Das könnte mit einer hohen Wahrscheinlichkeit auftreten. Beziehungsweise könnte die Schrift auch zu undeutlich vom Hintergrund hervorstechen, sodass sie nicht erkannt wird und nicht gewischt wird. Wir müssten also immer auf einen sauberen Hintergrund achten und den Kontrast zwischen Tafel und Schrift hoch halten (z.B. Beleuchtung). | + | //Ersten und letzten blauen Pixel bestimmen, sowhl auf der x-Achse, als auch auf der y-Achse |
+ | if (x<blauxmin) blauxmin = x; | ||
+ | else if (x>blauxmax) blauxmax = x; | ||
+ | if (y<blauymin) blauymin = y; | ||
+ | else if (y>blauymax) blauymax = y; | ||
+ | } | ||
+ | else if (pixelBrightness > threshold) { //Wenn Pixel heller als der Grenzwert ist... | ||
+ | pixels[currPos] = white; //...Pixel weiß färben | ||
+ | } else { //Ansonsten... | ||
+ | pixels[currPos] = black; //...Pixel schwarz färben | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | updatePixels(); | ||
+ | //Mittelpunkte des roten und des blauen Kreises | ||
+ | rot_xPos = (rotxmin + rotxmax)/2; | ||
+ | rot_yPos = (rotymin + rotymax)/2; | ||
+ | blau_xPos = (blauxmin + blauxmax)/2; | ||
+ | blau_yPos = (blauymin+blauymax)/2; | ||
+ | //Mittelpunkte in Schwarz einzeichnen | ||
+ | strokeWeight(5); | ||
+ | stroke(0); | ||
+ | point(rot_xPos, rot_yPos); | ||
+ | point(blau_xPos, blau_yPos); | ||
+ | //rot_xPos = 0; | ||
+ | //rot_yPos = 0; | ||
+ | //blau_xPos = 0; | ||
+ | //blau_yPos = 0; | ||
+ | } | ||
+ | boolean bereichTesten(int seite_a, int seite_b) | ||
+ | { | ||
+ | for ( int i = x_1; i < seite_a + x_1; i++) { //x-Achse des Kästchens ablaufen | ||
+ | for (int j = y_1; j < seite_b + y_1; j++) { //y-Achse des Kästchen ablaufens | ||
+ | int testFarbe = get(i, j); | ||
+ | float testHelligkeit = brightness(testFarbe); | ||
+ | if (testHelligkeit < threshold) { //Falls Helligkeit am Punkt kleiner als der Grenzwert ist... | ||
+ | return true; //...ist Schwarz vorhanden | ||
+ | } | ||
+ | //print(j); | ||
+ | //print(" "); | ||
+ | } | ||
+ | //println(i); | ||
+ | } | ||
+ | return false; //Ansonsten ist kein Schwarz vorhanden | ||
+ | } | ||
- | ====Dateneingabe==== | + | float berechneWinkel(float xPos, float yPos) { // berechnet winkel zwischen richtung des Roboters und gerade Mittelpunkt des Roboters und punkt xPos yPos |
+ | float alphav = arctan(blau_xPos-rot_xPos, (height-blau_yPos)-(height-rot_yPos)); | ||
+ | float alphaz = arctan(xPos-rot_xPos, (height-yPos)-(height-rot_yPos)); | ||
+ | float winkel = alphaz-alphav; | ||
- | Der Bereich in dem gewischt werden soll, soll durch die manuelle Eingabe der Koordinaten (bzw. falls möglich durch Markierung des Bereichs im Koordinatensystem auf dem PC) angegeben werden. Damit der RoBoard weiß, wo er hinfahren und nach Schrift suchen muss. | + | if (winkel > PI) { |
+ | winkel = winkel - 2*PI; | ||
+ | } else if (winkel < -PI) { | ||
+ | winkel = winkel + 2*PI; | ||
+ | } | ||
+ | return winkel; | ||
+ | } | ||
- | ==Aufgaben== | + | float arctan(float deltax, float deltay) { //Berechnung des Arkustangens von 0 bis 360 Grad, damit die Drehrichtung bestimmt werden kann |
+ | if (deltay == 0) { | ||
+ | if (deltax > 0) { | ||
+ | return PI; | ||
+ | } else { | ||
+ | return -PI; | ||
+ | } | ||
+ | } else if (deltay > 0) { | ||
+ | return atan(deltax/deltay); | ||
+ | } else { | ||
+ | return atan(deltax/deltay) + PI; | ||
+ | } | ||
+ | } | ||
- | * Koordinatensystem der Tafel muss auf dem Bildschirm zusehen sein | + | void roboterDrehen(float winkel) |
- | * man braucht von dem rechteckigen Abschnitt, welcher vom RoBoard bearbeitet werden soll, die Koordinaten von der Ecke links oben und von der Ecke rechts unten -> diese müssen vom Koordinatensystem abgelesen werden | + | { |
- | * Programm in das die Zielkoordinaten von RoBoard eingegeben und passend verarbeitet (zum Rechteck gemacht) werden können, muss geschrieben werden | + | //gewünschte Radumdrehungen des Roboters berechnen |
+ | float strecke = (winkel*robumfang) / (2*PI); | ||
+ | float umdrehungen = strecke / radumfang; | ||
+ | //Signal an Roboter schicken | ||
+ | if (winkel < 0) //nach links drehen | ||
+ | { | ||
+ | val = 'l'; | ||
+ | try | ||
+ | { | ||
+ | outToServer.writeBytes(val + str(umdrehungen)); //Beispiel: "l3.24" | ||
+ | } | ||
+ | catch (Exception e) | ||
+ | { | ||
+ | println(e); | ||
+ | } | ||
+ | } else if (winkel > 0) //nach rechts drehen | ||
+ | { | ||
+ | val = 'r'; | ||
+ | try | ||
+ | { | ||
+ | outToServer.writeBytes(val + str(umdrehungen)); | ||
+ | } | ||
+ | catch (Exception e) | ||
+ | { | ||
+ | println(e); | ||
+ | } | ||
+ | } else if (winkel == 0) { //nichts tun bzw. stoppen | ||
+ | val = 's'; | ||
+ | try | ||
+ | { | ||
+ | outToServer.write(val); | ||
+ | } | ||
+ | catch (Exception e) | ||
+ | { | ||
+ | println(e); | ||
+ | } | ||
+ | } | ||
+ | } | ||
- | ==Material== | + | void fahren(float zieldistanz) { //Vorwärts fahren |
+ | //gewünschte Radumdrehungen berechnen | ||
+ | float punktdistanz = dist(rot_xPos, rot_yPos, blau_xPos, blau_yPos); | ||
+ | float strecke = (punktabstand/punktdistanz) * zieldistanz; | ||
+ | float wegumdrehungen = strecke / radumfang; | ||
+ | //Signal an Roboter schicken | ||
+ | val = 'v'; | ||
+ | try | ||
+ | { | ||
+ | outToServer.writeBytes(val + str(wegumdrehungen)); | ||
+ | } | ||
+ | catch (Exception e) | ||
+ | { | ||
+ | println(e); | ||
+ | } | ||
+ | delay(10000); | ||
+ | } | ||
- | * Computer | + | void kaestchenAbfahren() { |
- | * Kamerabild | + | |
- | ==Wissen== | + | int i = (int ) (b / schwammbreite) + 1; |
- | Wie man das mit dem Markieren im Koordinatensystem macht | + | fahren(a); |
+ | warten(); //Auf Rückmeldung des Roboters warten | ||
- | ==Risiken== | + | for (int j = 2; j<i; j++) |
+ | { | ||
+ | if (j%2 == 0) | ||
+ | { | ||
+ | roboterDrehen(PI/2); | ||
+ | warten(); | ||
+ | val = 'v'; | ||
+ | try | ||
+ | { | ||
+ | outToServer.writeBytes(val + str(schwammumdrehungen)); | ||
+ | } | ||
+ | catch (Exception e) | ||
+ | { | ||
+ | println(e); | ||
+ | } | ||
+ | delay(3000); | ||
+ | roboterDrehen(PI/2); | ||
+ | warten(); | ||
+ | fahren(a - 2 * schwammdistanz); | ||
+ | warten(); | ||
+ | } else { | ||
+ | println("else"); | ||
+ | roboterDrehen(-PI/2); | ||
+ | warten(); | ||
+ | val = 'v'; | ||
+ | try | ||
+ | { | ||
+ | outToServer.writeBytes(val + str(schwammumdrehungen)); | ||
+ | } | ||
+ | catch (Exception e) | ||
+ | { | ||
+ | println(e); | ||
+ | } | ||
+ | delay(3000); | ||
+ | roboterDrehen(-PI/2); | ||
+ | warten(); | ||
+ | fahren(a - 2 * schwammdistanz); | ||
+ | warten(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
- | Die Koordinaten könnten falsch abgelesen und daraufhin falsch eingegeben werden. Dies ist eher wahrscheinlich, wenn man unkonzentriert arbeitet. Wenn man sich Mühe gibt und die Koordinaten richtig eingibt, dürfte nichts schief gehen. | + | void servo() { |
- | RoBoard würde bei einer falschen Eingabe zu einer anderen Stelle fahren, dort nach Schrift suchen und diese gegebenenfalls wegwischen, auch wenn dies nicht gewünscht war. Wenn die Dateneingabe nicht funktionieren sollte, könnte man den RoBoard so vereinfachen, dass er immer die gesamte Tafel nach Schrift absucht und wischt. | + | if (angekommen) //Servo Runter lassen |
+ | { | ||
+ | val = 'R'; | ||
+ | try | ||
+ | { | ||
+ | outToServer.write(val); | ||
+ | } | ||
+ | catch (Exception e) | ||
+ | { | ||
+ | println(e); | ||
+ | } | ||
+ | delay(1000); | ||
+ | } else { //Servo hochfahren | ||
+ | val = 'H'; | ||
+ | try | ||
+ | { | ||
+ | outToServer.write(val); | ||
+ | } | ||
+ | catch (Exception e) | ||
+ | { | ||
+ | println(e); | ||
+ | } | ||
+ | delay(1000); | ||
+ | } | ||
+ | } | ||
+ | //empfaengt und speichert Variable vom Input | ||
+ | char antwortArduino() | ||
+ | { | ||
+ | try | ||
+ | { | ||
+ | char input = ' '; | ||
+ | while (inFromServer.available() != 0) | ||
+ | { | ||
+ | input = (char) inFromServer.read(); | ||
+ | return input; | ||
+ | } | ||
+ | } | ||
+ | catch (Exception e) | ||
+ | { | ||
+ | println(e); | ||
+ | } | ||
+ | return ' '; | ||
+ | } | ||
- | ====Datenverwertung==== | + | //wartet bis der Arduino seine Aktion zu Ende ausgefuehrt hat |
- | In der Datenauswertung werden die, durch Bildauswertung und Eingabe des Nutzers, gesammelten Daten zur Berechnung der Aufgaben des Roboters verwendet. Die beinhaltet insbesondere die Berechnung seines Weges auf Grund seiner aktuellen Position und seines Zieles mitsamt des Wissend, wann der Schwamm gesenkt oder gehoben werden muss. | + | void warten() |
- | ==Aufgaben== | + | { |
- | * Algorithmen zur Berechnung des Weges entwickeln | + | char signal = ' '; |
- | * Den Weg in einer für den Arduino verwertbaren Form ausgeben | + | do { |
- | ==Material== | + | signal = antwortArduino(); |
- | * Eigener Rechner | + | } while (signal != 'f'); |
- | * Daten aus Bildauswertung und Eingabe | + | } |
- | ==Wissen== | + | </file> |
- | Wichtig ist hierbei den möglichst kürzesten Weg zum Abfahren einer Fläche zu finden. Dabei muss aus gegebenen Randkoordinaten ein Rechteck abgefahren werden. | + | |
- | ==Risiken== | + | |
- | Falls bei der Berechnung Fehler vorkommen, könnte es passieren, dass unser Roboter versucht über den Rand der Tafel hinauszufahren und dabei abstürzt. | + | |
- | ====Kommunikation==== | + | Kommentare zum Code: |
- | Dieser Teilbereich beinhaltet sowohl die Kommunikation, also Datenübertragung, zwischen Rechner und Arduino, als ach die Kommunikation zwischen Kamera und Rechner. Hierbei muss die Datenübertragung zum und vom Arduino drahtlos erfolgen. Für die Kamera wäre eine drahtlose Übertragung optimal, um unabhängig von der Position der Kamera den Rechner aufbauen zu können. | + | Wir haben bei der letzten Bearbeitung gesehen, dass "programState" in Zeile 47 mit 1 statt mit 0 initialisiert wurde. Das haben wir nun korrigiert, allerdings könnten hier einer der Gründe für die fehlerhafte Umsetzung liegen. |
- | Die zu Übertragenden Daten sind bei der Kamera das Bild in Echtzeit, und bei dem Arduino der berechnete Weg. | + | |
- | ==Aufgaben== | + | |
- | * Empfangen (und gegebenenfalls Senden) von Daten am Arduino | + | |
- | * Senden (und gegebenenfalls Empfangen) von Daten an der Kamera | + | |
- | * Empfangen und Senden von Daten am Rechner | + | |
- | ==Material== | + | |
- | * Wlan-Modul für den Arduino | + | |
- | * Wlan-Kamera | + | |
- | * Eigener Rechner mit mindestens 2 Wlan-Antennen | + | |
- | ==Wissen== | + | |
- | Hierfür benötigen wir das Wissen, wie der Arduino über ein Wlan-Modul angesprochen werden kann. Dies muss dann in unser Eingabeprogramm eingebunden werden. Das gleiche gilt bei der Kamera, wobei in diesem Fall hauptsächlich Daten empfangen werden müssen. | + | |
- | ==Risiken== | + | |
- | Wenn die Kommunikation nicht funktionieren sollte, dabei ist es egal auf welcher Teilstrecke, kann entweder der Weg nicht berechnet werden oder der Weg nicht übertragen werden. In beiden Fällen würde es zum Stillstand unseres Roboters führen | + | |
- | ====Ansteuerung==== | ||
- | Die Ansteuerung beinhaltet die eigentliche mechanische Arbeit des Roboters. Hier werden die einzelnen Motoren gesteuert. Da gibt es zwei Motoren für die Räder, welche dann für eine Drehung entgegengesetzt gedreht werden müssen und für eine Bewegung entlang einer Geraden gleichgerichtet. Wenn möglich wollen für diesen Teil Schrittmotoren verwenden -falls diese stark genug sind- , da die Steuerung dadurch leichter zu realisieren wäre. Dazu kommt der Motor für die Absenkung des Schwammes und vielleicht auch einer für die Absenkung eines Stiftes. | ||
- | ==Aufgaben== | ||
- | * Grundlegende Funktionen integrieren zur Steuerung des Fahrwerks (Fahren, Drehen) | ||
- | * Funktion zur Hebung oder Senkung | ||
- | ==Material== | ||
- | * Motoren für das Fahrwerk | ||
- | * Servo für das Schwamm-Gelenk | ||
- | ==Wissen== | ||
- | Wichtig ist hier herauszufinden, wie genau man mit den Motoren Drehungen vollziehen und Entfernungen abfahren kann. Dazu muss für den Servo die richtige Position gefunden werden, damit der Schwamm fest genug auf der Tafel aufliegt um zu wischen, aber nicht zu fest, um den Roboter nicht von der Tafel weg zustoßen. | ||
- | ==Risiken== | ||
- | Falls die Schrittmotoren zu ungenau sind und wir zu nah an der Kante versuchen zu wischen, könnte es passieren, dass der Roboter den Tafelbereich verlässt und zu Boden stürzt. Sollte der Servo den Schwamm zu schwach an die Tafel drücken, könnte selbige nicht mehr vollständig gereingt werden. Ist der Druck jedoch zu groß, könnte der Roboter in die Tiefe stürzen. |