Benutzer-Werkzeuge

Webseiten-Werkzeuge


Seitenleiste

projektews1415:graffitibot

Der Graffitibot



Meine Damen und Herren, wir freuen uns Ihnen den Graffitibot vorstellen zu dürfen.

Unser Team, bestehend aus Isabel Yasemin Dautz, Jasmin Tamara Pelz und Felix Fürnhammer, hat in den letzten Monaten einen ausgeklügelten Roboter gebaut, der sowohl fahren, als auch an Wänden malen soll!

Wie kamen wir darauf? Im Voraus sollten wir uns Gedanken für die zu bauenden Roboter machen, da Felix die Idee für einen sich im Raum orientierenden Roboter hatte und sowohl Isabel als auch Jasmin von einem malenden Roboter angetan waren, haben wir diese beiden harmonierenden Aspekte miteinander verbunden.

Einleitung

Die Idee des GraffitiBots war folgende:
Wir bauen einen Roboter, der sich im Raum orientieren, Hindernissen ausweichen und eine geeignete Wand finden kann, die er dann bemalt. Das Bild, welches er malen soll, wird auf einer C# Oberfläche an einem Laptop gezeichnet. Die Koordinaten werden dann in Schritte für die Schrittmotoren umgewandelt. Diese Schritte werden dann über einen zweiten, per USB am Laptop angeschlossenen, Arduino per Bluetooth an den Arduino im Roboter gesendet.


Zudem wurden wir von Jasmins Vater unterstützt, dieser hat uns nicht nur eine Werkstatt mit Metallfräse, Metallsäge und einer Drehmaschine zur Verfügung gestellt, sondern auch einen C#-Crashkurs an 4 Wochenenden gegeben. Er hat sich sogar bereit erklärt alles zu bezahlen, da er den fertigen Roboter am Ende des Projektes gerne behalten wollte.
Wir wünschen euch viel Spaß beim weiteren Lesen der Projektdokumentation und hoffen wir konnten unsere Projektarbeit anschaulich darstellen.

Die Mechanik

Hierzu haben wir nach einer gründlichen Einführung in der Handhabung der zu benutzenden Maschinen viele Arbeitsstunden in der Werkstatt von Jasmins Vater verbracht.

Für sämtliche Teile wurde Aluminium benutzt, da es vergleichsweise leicht, aber auch stabil ist. Begonnen haben wir mit dem Zuschneiden des Bodens, der Befestigung des Stützrades, der vorderen beiden Räder und deren Motoren. Nach und nach wurden die grundlegenden Teile, wie Akkus und das Relais, platziert, sowie fixiert. Es wurde eine Art „Schutzbox“ entwickelt, um sehr empfindliche Teile, wie den Arduino Mega, als auch seine Verdrahtung zu schützen. An den Seiten dieser Box befinden sich Schalter, die zwar zur Zeit nicht genutzt werden, aber voll funktionstüchtig sind. Ein Display kann zur Übertragung von Texten genutzt werden und Bohrungen am Rand dienen als Eingänge der Kabel.

Das Malgestell steht orthogonal zum Boden, besitzt eine Art quadratisches „Außenskelett“, das die Achsen hält, somit besitzt der Graffitibot insgesamt eine Höhe von 57 cm und eine Breite von 63 cm. Die Y-Achse wird durch zwei Zahnriemen geführt, von der eine über einen Schrittmotor angetrieben wird. Die X-Achse besteht aus einer Gewindestange die ebenfalls von einem Schrittmotor in Rotation versetzt wird.

Die Elektronik

Die Bluetoothverbindung

Die ursprüngliche Verbindung zwischen PC und Roboter, also die Verbindung zwischen C# und Arduino, bestand aus der standardisierten USB-Schnittstelle, die auch für die Programmübertragung von PC auf Arduino benutzt wird. Da es natürlich nicht praktikabel ist, den Roboter mit einem langen Kabel mit dem Pc zu verbinden ist, stehen zwei Methoden der Funkanbindung zur Verfügung. Und zwar Bluetooth und WLAN. Da das Bluetoothmodul kostengünstiger ist, schon verfügbar war und auch letztendlich die Anbindung einfacher war, haben wir uns für diese entschieden. Aus der Sicht des Programmierers zeigt sich der angenehme Umstand, dass die Bluetoothkopplung keinerlei zusätzlichen Code erfordert, da die Kommunikation zwischen PC und Arduino in der gewohnten Weise funktioniert. Die ursprünglich angedachte Nutzung der im Laptop integrierten Bluetoothschnittstelle war problematisch in der Parametrierung, darum haben wir uns entschlossen einen zweiten Arduino per Kabel direkt an den PC anzuschließen, der nur als Schnittstellenwandler zwischen PC/USB und Arduino/Bluetooth wirkt. Für diese Verschaltung ist keine einzige Codezeile nötig. Innerhalb des C#-Programms wird nur die COM-Schnittstelle des Arduino angegeben. In der C#-Software wird die Treiberfunktionalität der guten RS232 Schnittstelle verwendet, was sich auch in der Limitierung der Datenübertragungsgeschwindigkeit auf 115000baud wiederspiegelt.

Die Ultraschallsensoren

Die Ansteuerung der Ultraschallsensoren erfolgt über die Software. Es ist bekannt, dass die Schallgeschwindigkeit keine eigentliche Naturkonstante ist, sondern im Nennbereich 330 m/s liegt und starken Schwankungen durch Luftfeuchtigkeit und Temperatur ausgesetzt ist. Da aber die beiden in der Software angesprochenen Temperatursensoren identischen Umweltbedingungen ausgesetzt sind, ist ihre Hauptaufgabe, die parallele Ausrichtung des Roboters zur Wand, trotzdem erfüllt.

Die Pins am Arduino

Pins Bauelement
GND(4x) Ultraschallsensoren
GND(3x) Schalter
5+7 Motor (x-Achse)
1+3 Motor (y-Achse)
10+11 Schalter
12+13 Schalter
18+19 Bluetoothsensor
24+25 Display
26+27 Display
28+29 Display
30+31 Display
32+33 Ultraschallsensor(Echo+Trig)
36+37 Ultraschallsensor(Echo+Trig)
40+41 Ultraschallsensor(Echo+Trig)
44+45 Ultraschallsensor(Echo+Trig)
50+52 Rechter Schrittmotor
51+53 Linker Schrittmotor

Der Code

Fahren

Damit der GraffitiBot an eine Wand malen kann, muss er diese natürlich zunächst finden!
Zur Lösung dieser Aufgabe stehen uns zwei Schrittmotoren und die vier Ultraschallsensoren (von denen momentan nur zwei genutzt werden) zur Verfügung. Außerdem haben wir während des Projektes zwei Libraries verwendet, die uns die Arbeit um einiges erleichtert haben. Zum einen <AccelStepper>, eine Library die das gleichzeitige ansteuern mehrerer Schrittmotoren erleichtert und dazu Funktionen beinhaltet, welche die Geschwindigkeit und Beschleunigung regeln. Zum anderen <NewPing>, die ein sequentielles Ablesen der Ultraschallsensoren ohne die pulseIn Funktion ermöglicht. Die pulseIn Funktion, die standardmäßig in der Arduino IDE enthalten ist, blockt beim Ablesen der Ultraschallsensoren die Schrittmotoren und wenn kein Echo empfangen wird, legt sie gerne mal das ganze Programm für ungefähr fünf Sekunden lahm.

Hier ein Zustandsdiagramm zur Fahrweise:

Zur Fahrweise:

Findet der Roboter ein Objekt mit den Ultraschallsensoren, so berechnet er den Abstand zum Objekt, zieht zwei cm Toleranzwert von der Entfernung ab und rechnet das ganze in Microsteps um (bei uns *29, da 1600 Microsteps 360° entsprechen und der Umfang unserer Räder c.a. 55 cm beträgt).

 if(stepper2_on){                    //stepper 2 (analog für Stepper 1)
  if(cm[1] <=100 && cm[1]!=0){      //Wenn der Abstand kleiner als 100 cm ist und nicht 0
   if(stepper2.distanceToGo()==0){
    stepper2.move((cm[1]-2)*29);   //(abstand - 2 cm) um von cm in schritte umzurechnen *29
    stepper2_on=false;
   } 
  }
   else{                          // wenn die Distanz gleich 0, also größer 100 cm dreht sich der Roboter solange bis er etwas findet
    if(stepper2.distanceToGo()==0){
    stepper2.move(200);           // Stepper1 fährt hier 800 Schritte
   }
   stepper2_on=false;
  }

Sobald der Abstand zum Objekt drei cm oder weniger beträgt, also das Rad des Roboters das Objekt berührt, stoppt der jeweilige Motor und der Roboter versucht den anderen Ultraschallsensor ebenfalls direkt vor die Wand zu fahren.(alles für stepper 2 gilt analog auch für stepper 1)

if(cm[1] <=3 && cm[1]!=0){  // stepper 2 fährt nicht weiter wenn ein objekt 3 cm oder weniger vor dem Sensor ist
 
    stepper2_on=false;
  }
  else{
    stepper2_on=true;
  }

Im Optimalfall steht der Roboter jetzt parallel zur Wand und wartet auf die Eingabe des Bildes, welches er malen soll. Sollte der Roboter jedoch auf ein anderes Objekt als eine Wand (Stuhl,Tisch usw.) treffen wird er dennoch versuchen zuerst einen und dann den anderen Ultraschallsensor auf drei cm an das Objekt heranzufahren und wenn besagtes Objekt nicht zufällig der Breite des Roboters entspricht fährt er immer weiter dagegen. Das Programm funktioniert also momentan nur in einem leeren Raum.
Findet der Roboter allerdings kein Objekt im Abstand von maximal 100 cm vor ihm, fährt er eine Spirale, bis er ein Objekt findet. Generell darf das Objekt maximal einen Meter Abstand haben, sonst erkennt er es nicht. Die Ultraschallsensoren fangen ab etwa 100- 150 cm ungenaue Messungen zu liefern, da wir aber für die Berechnung der zu fahrenden Strecke genaue Werte brauchen, haben wir den maximalen zu messenden Abstand auf 100 cm gelegt.
Sollte der Roboter kein Objekt finden, so fängt er an sich langsam im Uhrzeigersinn zu drehen und sucht die Umgebung nach einem Objekt ab.(siehe im ersten Codeblock „else{}“)


Malen

Wir hatten die Idee unseren Roboter etwas an die Wand zeichnen zu lassen, das wir vorher mit der Maus auf einer Programmoberfläche gemalt haben. Dazu haben wir zwei verschiedene Programmiersprachen benutzt. Für das Zeichnen mit der Maus haben wir C# verwendet, für das Ansteuern der Schrittmotoren hingegen Arduino.

Mit der Maus zeichnen (C#)

Man kann sich unser Programm als eine abgespeckte Version von Paint vorstellen. Bewegt man die Maus, solange die linke Maustaste gedrückt ist,
wird eine Linie gezeichnet. Ist die linke Maustaste nicht (mehr) gedrückt, wird auch nicht gezeichnet. So sieht das dann aus:





Im folgenden Beispiel sieht man, dass die Zeichnung nicht (false) ausgeführt werden soll, solange die linke Maustaste nicht gedrückt ist
(MouseUp Event):

void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            meine_Zeichnung = false;
        }


Ist die linke Maustaste nun gedrückt (MouseDown Event), dann kann auch gezeichnet werden (true). Die Punkte (Mauskoordinaten) werden dann zu einer Liste hinzugefügt und darin gespeichert:

void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            Punkte.Add(new List<Point>());
            meine_Zeichnung = true;
        }


Erst wenn man die Maus bewegt (MouseMove Event), kann eine Linie entstehen, da man bekanntlich zwei Punkte braucht, um eine Linie zeichnen zu können. Wenn also die Zeichnung erfolgen soll, wird zu den bisherigen Punkten immer der aktuelle Punkt (Mausposition) hinzugefügt. 'Refresh()' löst das Paint-Ereignis aus, das man braucht, damit eine sichtbare Linie entsteht:

void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            if (meine_Zeichnung)
            {
                Punkte[Punkte.Count - 1].Add(e.Location);
                this.Refresh(); 
            }
        }


Das Paint-Event wird ausgelöst, sobald die Maus bei gedrückter linker Taste bewegt wird. Für alle Punkte der Liste gilt, dass, wenn mehr als
ein Punkt vorhanden ist, eine Linie mit Hilfe der Graphics-Klasse gezeichnet (DrawLines) wird. Um das auf der Oberfläche sichtbar zu machen,
benutzen wir den Stift (Pen) mit der Farbe schwarz und einer Breite von 3 Pixeln. Die Punkte werden dafür in ein Array gespeichert, da immer
der neueste Punkt mit vorherigen Punkt verbunden werden soll:

void Form1_Paint(object sender, PaintEventArgs e)
        {
            foreach (List<Point> points in Punkte)
            {
                if (points.Count > 1)
                    e.Graphics.DrawLines(new Pen(new SolidBrush(Color.Black), 3), points.ToArray());
            }
        }



Die Punktkoordinaten der Zeichnung an Arduino weitergeben



Das Umsetzen der Zeichung in Schrittmotorenbewegungen (Arduino)

Nun können wir also etwas mit der Maus zeichnen und die Punkte der Zeichnung an Arduino senden. Jetzt müssen wir nur noch die Zeichnung in Schrittmotorenbewegungen umwandeln, damit der Roboter unsere Zeichnung an die Wand malen kann. Eine wichtige Voraussetzung ist folgende:

Wir haben uns wieder für die AccelStepper-Library entschieden, da die Schrittmotoren der Achsen gleichzeitig angetrieben werden müssen. Wenn sie das nicht täten, würde zuerst die x-Achse, danach die y-Achse befahren werden, was bedeuten würde, dass zwei Punkte statt durch eine Linie (schwarz) durch zwei Linien (grau) verbunden werden:

Wir haben vorhin den empfangenen x- und y-Wert als 'xNEU' und 'yNEU' deklariert, nun fahren wir folgendermaßen fort:

        schritteX = (xNEU - xALT)*1000;                                                                    // Hier werden die Schritte, die der Stift auf der x-Achse zurücklegen soll, berechnet. Dazu 
                                                                                                           // ziehen wir den alten x-Wert vom aktuellen ab. Diese Differenz wird mit 1000 multipliziert, da 
                                                                                                           // die Stepper eine hohe Impulsrate brauchen, damit ein erkennbarer "Schritt" zu sehen ist.
                                                                                                           // Ist die Zahl negativ, so bewegt sich der Stepper in die andere Richtung.
        schritteY = (yNEU - yALT)*1000;                                                                    // Das gleiche gilt natürlich auch für die y-Achse.                     
 
        if((schritteX>3000 && schritteY>3000) ||(schritteX<-3000 && schritteY<-3000))                      // Wenn die Differenz beider! Achsenwerte größer als 3000 ist (also eigentlich 3), dann wurde die 
                                                                                                           // gezeichnete Linie unterbrochen und womöglich woanders fortgeführt. Da der Roboter dann nicht
                                                                                                           // die Punkte durch eine Linie verbinden soll, muss der Stift absetzen. Gleiches gilt auch, wenn 
                                                                                                           // die Differenz kleiner als -3000 (-3) ist.
        {
          //Stift von der Wand entfernen (zurückfahren)                                                    // Da der Stift nicht "richtig" abgesetzt werden kann, muss der Roboter einen kleinen Schritt
                                                                                                           // zurückfahren.
        }
        if((schritteX>3000 || schritteY>3000) ||(schritteX<-3000 || schritteY<-3000))                      // Auch wenn nur die Differenz der Werte einer! Achse größer als 3000 bzw. kleiner als -3000 ist, 
                                                                                                           // kann die Linie unterbrochen worden sein. Daher muss auch hier der Stift von der Wand abgesetzt 
                                                                                                           // werden.
        {
          //Stift von der Wand entfernen (zurückfahren)
        }
 
        stepperX.move(schritteX);                                                                          // Nun sollen die Schrittmotoren die berechneten Schritte ausführen. Das erfolgt über den 
                                                                                                           // 'move'-Befehl. Das gilt für beide Achsen.
        stepperY.move(schritteY);                                   
 
        if((schritteX>3000 && schritteY>3000) ||(schritteX<-3000 && schritteY<-3000)                       // Da die zu zeichnende Linie im nächsten Schritt weitergeht, muss der Stift wieder an die Wand 
                                                                                                           // angesetzt werden. Weil das nur nötig ist, wenn der Roboter sich vorher ein wenig von der Wand 
                                                                                                           // entfernt hat, gilt hier die gleiche Bedingung wie oben: Die Differenz der Achsenwerte muss  
                                                                                                           // größer als 3000 bzw. kleiner als -3000 sein (sie hat sich im Schritt darüber ja nicht geändert).
        {
          //Stift wieder an die Wand (ranfahren)
        }
 
        if((schritteX>3000 || schritteY>3000) ||(schritteX<-3000 || schritteY<-3000))
        {
          //Stift wieder an die Wand (ranfahren)
        }
 
        xALT = xNEU;                                                                                       // Die Achsenwerte 'xNEU' und 'yNEU' müssen nun 'xALT' und 'yALT' zugewiesen werden, da im nächsten  
                                                                                                           // Schritt eine Linie von dem ehemaligen "neuen" (jetzt "alten") Punkt zum jetzt "neuen" Punkt  
                                                                                                           // (neu eingelesene Werte) gezeichnet werden soll. 
        yALT = yNEU;
 
        Serial.print("Punkt_bitte");                                                                       // Nachdem der Roboter eine Linie gezeichnet hat, schreibt er über die serielle Schnittstelle an
                                                                                                           // C#, dass die nächsten Werte kommen können. Dieser Schritt ist notwendig, da Arduino nicht zu
                                                                                                           // viele Werte auf einmal erhalten und verarbeiten kann.
 
      }       



Die Materialliste
Materialien Anzahl Quellen Kosten(in €)
Schrittmotoren 4 Wantai,Amazon 200
Leistungsteil(Treiber) 4 Wantai,Amazon (im Preis der Motoren inbegriffen)
Relais 1 privat 15
Bluetoothsensor 1 Amazon 8
Ultraschallsensoren 4 Amazon 24
Akkumulatoren 3 Amazon 30
Räder 3 privat (von einem alten Kinderwagen)
Arduino Mega 1 privat 15
Schraubklemmen 30 privat 12
Aluminium ca. 14kg privat 10
Kabel / privat 5
Schrauben / privat 5
Zahnriemen 2 Amazon 20
Gewindestange 1 privat 3
Display 1 privat 2,50
Knöpfe 5 privat 1
Insgesamt ~350.50€

Alle Materialien wurden von Jasmins Vater zur Verfügung gestellt bzw bezahlt.

Ergebnis & Diskussion

Beim Bau des Roboters lief auch alles noch super, allerdings haben wir im Laufe des Projekts festgestellt, dass wir viele unserer Anforderungen an den GraffitiBot vernachlässigen müssen um termingerecht Ergebnisse aufweisen zu können. Der Roboter orientiert sich beispielsweise nicht, er sucht das nächstbeste Objekt und versucht parallel vor diesem stehen zubleiben (siehe Code, Fahren). Nur an C# und der Koordinatenübergabe haben wir bis zuletzt festgehalten(siehe Code, Malen).

Probleme mit denen wir uns befassen mussten, waren unter anderem den richtigen Umgang mit der Fräse zu erlernen, das gleichzeitige Ansteuern der Motoren, das Auslesen der Ultraschallsensoren und die Bluetooth-Kommunikation zwischen PC und Arduino zu etablieren. Momentan haben wir alle diese Probleme gelöst bis auf die Übertragung der Koordinaten von C# an den Arduino. Die einzelnen Programmparts stehen soweit, es muss nur noch alles in ein Programm geschrieben werden wenn die Übertragung funktioniert.

Fazit:

Dem Graffitibot ist es nur möglich sich in einem leeren Raum zu orientieren, da er noch keinen Gegenständen ausweichen kann, bzw an ihnen „hängen bleibt“. Nichts desto trotz ist er in der Lage eine Wand bis auf 3cm genau anzufahren und wenn man dem Ardunio ein festes Bild einprogrammiert kann er dieses auch schon malen. Dass man ihm ein Bild via Bluetooth sendet scheitert leider noch an der Koordinatenübergabe zwischen C# und dem Arduino.

Libraries

Arduino Navigationsprogramm

ino Navigation.ino
#include <AccelStepper.h>  // library für die Stepmotoren
#include <NewPing.h>      // library für die Ultraschallsensoren
 
#define SONAR_NUM     4 // Anzahl der Ultraschallsensoren
#define MAX_DISTANCE 100 // Maximale Entfernung, die von den Sensoren erfasst wird (in cm). Wenn Entfernung größer als 100 cm wird 0 ausgegeben
#define PING_INTERVAL 33 // Zeit zwischen den Messungen der einzelnen Sensoren (min 29 ms)
 
AccelStepper stepper1 (1,51,53); //Linker Stepmotor
AccelStepper stepper2 (1,50,52); //Rechter Stepmotor
AccelStepper stepperX (1,5, 7); // Motor der X-Achse des Malgestells
AccelStepper stepperY (1,3, 1); // Motor der Y-Achse des Malgestells
 
unsigned long pingTimer[SONAR_NUM]; // Array für die sequentielle Auslesung der Ultraschallsensoren
unsigned int cm[SONAR_NUM];         // Array um die Distanz des jeweiligen Sensors zu speichern
uint8_t currentSensor = 0;          
 
NewPing sonar[SONAR_NUM] = {     // Array für die Pins der Ultraschallsensoren
  NewPing(37, 36, MAX_DISTANCE), // z.B. Trigger auf Pin 37, Echo auf Pin 36, maximale Entfernung 100 cm
  NewPing(33, 32, MAX_DISTANCE), // Momentan läuft das Programm nur mit den beiden vorderen Sensoren also cm[0] und cm[1]
  NewPing(41, 40, MAX_DISTANCE),
  NewPing(45, 44, MAX_DISTANCE)
};
 
void setup() {
  Serial.begin(115200);
  pingTimer[0] = millis() + 75;           // kurzer delay bevor die ersten Messungen gemacht werden
  for (uint8_t i = 1; i < SONAR_NUM; i++) // Startintervalle für die Sensoren
   { pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;}
 
  stepper1.setMaxSpeed(5000);   // init für Stepmotor 1
  stepper1.setAcceleration(1000);
  stepper1.setSpeed(5000 );
 
  stepper2.setMaxSpeed(10000);  // init für Stepmotor 2
  stepper2.setAcceleration(1000);
  stepper2.setSpeed(10000); 
 
  stepperX.setMaxSpeed(20000); // init für Stepmotor X
  stepperX.setAcceleration(1000);
  stepperX.setSpeed(20000);
 
  stepperY.setMaxSpeed(20000); // init für Stepmotor Y
  stepperY.setAcceleration(1000);
  stepperY.setSpeed(20000);
}
 
boolean Bewegung=false;
boolean stepper1_on=false;
boolean stepper2_on=false;
 
char cSignal;
char koordinaten;
 
void loop() {
 
  stepper1.run();  // Der Startbefehl für die Motoren muss mindestens einmal in der loop aufgerufen werden damit die Motoren sich bewegen
  stepper2.run();
 
  if(cm[0]<=3 && cm[1] <=3 && cm[0]!=0 && cm[1]!=0){ // Stoppt den Roboter wenn vor unmittelbar vor beiden vorderen Sensoren ein objekt ist (hoffentlich eine wand)
 
    stepper1.stop();
    stepper2.stop();
    Bewegung=false;
 
  }
  else{
    Bewegung=true;
  }
  if(Bewegung){               
    movement ();
  }
  else{
    sketch ();
  }
  for (uint8_t i = 0; i < SONAR_NUM; i++) { // liest alle Sensoren nacheinander ab
    if (millis() >= pingTimer[i]) {         // ist es Zeit den Sensor abzulesen
      pingTimer[i] += PING_INTERVAL * SONAR_NUM;  // speichert die Zeit wann der Sensor das nächste mal abgelesen werden soll
      if (i == 0 && currentSensor == SONAR_NUM - 1) oneSensorCycle(); // Der Sensor kann abgelesen werden
      sonar[currentSensor].timer_stop();          // stoppt den Timer vor der Messung
      currentSensor = i;                          
      cm[currentSensor] = 0;                      // Setzt die Entfernung auf 0 für den Fall dass kein echo empfangen wird
      sonar[currentSensor].ping_timer(echoCheck); // Misst die Entfernung am Sensor i
    }
  }
 
}
 
void echoCheck() { // Wenn ein Echo eingefangen wurde speichert er den Wert unter cm[] und rechnet die gemessene Zeit in cm um ("/US_ROUNDTRIP_CM")
  if (sonar[currentSensor].check_timer())
    cm[currentSensor] = sonar[currentSensor].ping_result / US_ROUNDTRIP_CM;
}
 
void oneSensorCycle() { // Werte wurden abgelesen und gespeichert hier werden sie auf dem Serial Monitor ausgegeben
  for (uint8_t i = 0; i < SONAR_NUM; i++) {
    Serial.print(i);
    Serial.print("=");
    Serial.print(cm[i]);
    Serial.print("cm ");
  }
  Serial.println();                        // Position des Steppers wird auf dem Serial Monitor ausgegeben
  Serial.print(stepper1.currentPosition());
  Serial.print("\t");
  Serial.println(stepper2.currentPosition());
}
void movement(){
    if(cm[1] <=3 && cm[1]!=0){  // stepper 2 fährt nicht weiter wenn ein objekt 3 cm oder weniger vor dem Sensor ist
 
    stepper2_on=false;
  }
  else{
    stepper2_on=true;
  }
  if(cm[0]<=3 && cm[0]!=0){   // stepper 1 fährt nicht weiter wenn ein objekt 3 cm oder weniger vor dem Sensor ist
    stepper1_on=false;
  }
  else{
    stepper1_on=true;
  }
  if(stepper2_on){      //stepper 2
  if(cm[1] <=100 && cm[1]!=0){
   if(stepper2.distanceToGo()==0){
    stepper2.move((cm[1]-2)*29);   //(abstand - 2 cm) um von cm in schritte umzurechnen *29
    stepper2_on=false;
   } 
  }
  else{     // wenn die Distanz gleich 0, also größer 100 cm dreht sich der Roboter solange bis er etwas findet
    if(stepper2.distanceToGo()==0){
    stepper2.move(200);
   }
   stepper2_on=false;
  }
  }
  if(stepper1_on){    //stepper 1
  if(cm[0] <=100 && cm[0]!=0){
  if(stepper1.distanceToGo()==0){
    stepper1.move((cm[0]-2)*29);   //(abstand - 2 cm) um von cm in schritte umzurechnen *29
    stepper1_on=false;
  }
  }
  else{                            // wenn die Distanz gleich 0, also größer 100 cm dreht sich der Roboter solange bis er etwas findet
    if(stepper1.distanceToGo()==0){
    stepper1.move(800);
  }
   stepper1_on=false;
 
  }
  }
} 
void sketch(){
  // hier wird der Malpart implementiert sobald dieser fertig ist
}


C# Malprogramm (Mauszeichnen)

(noch nicht auskommentiert)

sln mauszeichnen.sln
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
 
namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        bool meine_Zeichnung;
        List<List<Point>> Punkte;
        public Form1()
        {
            InitializeComponent();
        }
 
 
        public void Form1_Load(object sender, EventArgs e)
        {
            Punkte = new List<List<Point>>();
            this.DoubleBuffered = true;
            this.MouseDown += new MouseEventHandler(Form1_MouseDown);
            this.MouseUp += new MouseEventHandler(Form1_MouseUp);
            this.MouseMove += new MouseEventHandler(Form1_MouseMove);
            this.Paint += new PaintEventHandler(Form1_Paint);
        }
 
        void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            Punkte.Add(new List<Point>());
            meine_Zeichnung = true;
        }
 
        void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            meine_Zeichnung = false;
        }
 
        void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            if (meine_Zeichnung)
            {
                Punkte[Punkte.Count - 1].Add(e.Location);
                this.Refresh(); //Löst Paint Ereignis aus
            }
        }
 
        void Form1_Paint(object sender, PaintEventArgs e)
        {
            foreach (List<Point> points in Punkte)
            {
                if (points.Count > 1)
                    e.Graphics.DrawLines(new Pen(new SolidBrush(Color.Black), 3), points.ToArray());
            }
        }
 
 
        private void zeichnen_Click(object sender, EventArgs e)
        {
            serialPort1.Open();
            serialPort1.Write("1");
 
            int x = 0;
            int y = 1;
 
            string[] punkt = new string[100000];
 
            string punkte = Convert.ToString(Punkte);
 
            punkt = punkte.Split();
 
 
            string nachricht = serialPort1.ReadLine();
 
 
            if (nachricht == "Punkt_bitte")
            {
                serialPort1.Write(punkt[x]);
                serialPort1.Write(punkt[y]);
 
                x = x + 2;
                y = y + 2;
            }
 
            if (nachricht == "seriellen_Port_schließen")
            {
                serialPort1.Close();
            }
        }
 
 
        private void Anfangsposition_Click(object sender, EventArgs e)
        {
            serialPort1.Open();
            serialPort1.Write("2");
            serialPort1.Close();
        }
 
 
        //private void löschen_Click(object sender, EventArgs e)
        //{
            //Graphics grafik = this.CreateGraphics();
            //grafik.Clear(this.BackColor);
        //}
    }
}


Arduino Malprogramm (Roboter)

ino MALEN.ino
#include <AccelStepper.h>                                                                                  // Wir benutzen die AccelStepper Library, da es so möglich ist die Stepper gleichzeitig zu 
                                                                                                           // betätigen. Das ist wichtig fürs Zeichnen, weil sonst keine gerade Linie gezogen wird, sondern
                                                                                                           // eine "Ecke", also erst eine Linie auf der x-Achse, dann auf der y-Achse.
char cSignal;                                                                                              // Wir deklarieren 'cSignal' als character.
char koordinaten;                                                                                          // Auch 'koordinaten' deklarieren wir als character.
 
AccelStepper stepperX(1,5,7);                                                                              // Hier benennen wir unsere stepper. stepperX = x-Achse, stepperY = y-Achse des Roboters.
                                                                                                           // DRIVER '1' bedeutet eine Schrittmotorsteuerung (mit Schritt- und Richtungs-Pins).
                                                                                                           // stepperX ist an den Pins 5 (steps) und 7 (direction) angeschlossen.
AccelStepper stepperY(1,1,3);                                                                              // stepperY ist an den Pins 1 und 3 angeschlossen.                          
 
 
 
 
void setup()
{
  Serial.begin(38400);                                                                                     // Durch den Befehl 'Serial.begin(38400)' wird die serielle Kommunikation zwischen dem Computer  
                                                                                                           // und dem Arduino-Board gestartet. '38400' ist dabei die Baudrate (Geschwindigkeit).
 
  stepperX.setMaxSpeed(10000);                                                                             // Mit dem Befehl 'setMaxSpeed' legen wie die Höchstgeschwindigkeit der Stepper (X und Y)
                                                                                                           // fest (Schritte pro Sekunde).
  stepperY.setMaxSpeed(10000);                                                                             // Da unser Roboter möglichst genau malen soll, ist unsere Höchstgeschwindigkeit nicht zu hoch.                          
 
  stepperX.setAcceleration(1000);                                                                          // Der Befehl 'setAcceleration' legt die Beschleunigungs- / Verzögerungsrate der Stepper (X und Y)
                                                                                                           // fest (Schritte pro Quadratsekunde).
  stepperY.setAcceleration(1000);                                                                          // Diese Rate ist klein, da die Stepper nur langsam beschleunigen sollen, das sorgt für Präzision.                     
 
  stepperX.setSpeed(1000);                                                                                 // Der Befehl 'setSpeed' legt die gewünschte konstante Geschwindigkeit für die Verwendung vom z.B.
                                                                                                           // 'move'-Befehl fest. Über 1000 werden die Schritte ungenauer. Wir haben 1000 genommen, da
                                                                                                           // der Stift nur einen minimalen, aber nicht zu kleinen, Schritt auf der x-Achse macht. 
  stepperY.setSpeed(1000);                                                                                 // Gleiches gilt auch für den Stepper der y-Achse.
}
 
 
 
 
void loop()
{
  cSignal = Serial.read();                                                                                 // 'Serial.read()' liest einen string ("1" oder "2"), der über die serielle Schnittstelle von C# 
                                                                                                           // gesendet wird. Diesen string weisen wir dem character 'cSignal' zu, da ein C#-string bei 
                                                                                                           // Arduino einem character entspricht.
 
  int xALT = 0;                                                                                            // Hier weisen wir dem integer 'xALT' den Wert Null zu, da der Stift des Malgestells sich anfangs 
                                                                                                           // bei (0|0) befindet (Ecke oben links - genauso wie es bei C# der Fall ist).
  int yALT = 0;                                                                                            // Auch 'yALT' weisen wir den Wert Null zu.  "ALT" deshalb, da es immer der "ältere" Wert der zwei
                                                                                                           // Punkte, die wir zum Zeichnen einer Linie brauchen, ist. 
 
  int schritteX = 0;                                                                                       // Der Integer 'schritteX' bezeichnet die Anzahl der Schritte, die der Stift auf der x-Achse 
                                                                                                           // zurücklegen soll. Wir haben 'schritteX' den Wert Null zugewiesen.
  int schritteY = 0;                                                                                       // Dem Integer 'schritteY' haben wir ebenfalls den Wert Null zugewiesen, er bezeichnet die 
                                                                                                           // Schritte für die y-Achse.
 
  int xNEU = 0;                                                                                            // Auch dem Integer 'xNEU' weisen wir den Wert Null zu.                                           
  int yNEU = 0;                                                                                            // Gleiches gilt für den Integer 'yNEU'. "NEU" deshalb, weil es immer der "neuere" 
                                                                                                           // (über Serial.read() einkommende) Wert ist.
 
  stepperX.run();                                                                                          // Die 'run()'-Funktion benötigen wir, damit der Stepper überhaupt die, z.B. 'move'-, Befehle
  stepperY.run();                                                                                          // ausführen kann. 
 
  switch(cSignal)                                                                                          // Mit 'switch...case' ist es möglich aus einer Menge von Anweisungen eine betsimmte zu wählen.
                                                                                                           // Diese Auswahl wird durch Betätigen eines Buttons der C#-Oberfläche getätigt, daher ist unsere
                                                                                                           // Auswahl das 'cSignal'. Das Programm im case wird nur ausgeführt, wenn in unserem Fall die Zahl
                                                                                                           // stimmt.
  {
    case'1':                                                                                               // Betätigen wir in C# den 'zeichnen'-Button, wird "1" über die serielle Schnittstelle an Arduino
                                                                                                           // gesendet und folgendes Programm ausgeführt:
 
      Serial.print("Punkt_bitte");                                                                         // Arduino sendet durch den Befehl 'Serial.print()' eine Zeichenkette ("Punkt_bitte) über die 
                                                                                                           // serielle Schnittstelle an C#. Da es sich um die in einem string array gespeicherten 
                                                                                                           // Punktkoordinaten handelt, ist wieder das Speichern in einem character notwendig.
 
      if (Serial.available())                                                                              // Wenn Arduino etwas über die serielle Schnittstelle empfängt ('Serial.available()'), soll das 
                                                                                                           // folgende Programm ausgeführt werden: 
      {
        koordinaten = Serial.read();                                                                       // Das, was der Arduino seriell von C# empfangen hat, soll nun durch 'Serial.read()' eingelesen 
                                                                                                           // und dem character 'koordinaten' zugewiesen werden.
        int punkt[2] = {koordinaten};                                                                      // Das array 'punkt', das 3 Elemente (x- und y-Wert, Ende) umfassen soll, soll die von C# 
                                                                                                           // gesendeten und in den character 'koordinaten' gespeicherten Werte sammeln.
                                                                                                           // Ob die Werte dadurch, dass sie in einem character gespeichert werden, auch nur als ein
                                                                                                           // Element angesehen werden und nicht als zwei verschiedene, ist noch unklar.
 
 
        xNEU = punkt[0];                                                                                   // Dem Integer 'xNEU' soll das erste Element des arrays (hoffentlich nur der x-Wert) zugewiesen 
                                                                                                           // werden.
        yNEU = punkt[1];                                                                                   // Dem Integer 'yNEU' soll das zweite Element (hoffentluch der y-Wert) zugewiesen werden. 
 
 
        schritteX = (xNEU - xALT)*1000;                                                                    // Hier werden die Schritte, die der Stift auf der x-Achse zurücklegen soll, berechnet. Dazu 
                                                                                                           // ziehen wir den alten x-Wert vom aktuellen ab. Diese Differenz wird mit 1000 multipliziert, da 
                                                                                                           // die Stepper eine hohe Impulsrate brauchen, damit ein erkennbarer "Schritt" zu sehen ist.
                                                                                                           // Ist die Zahl negativ, so bewegt sich der Stepper in die andere Richtung.
        schritteY = (yNEU - yALT)*1000;                                                                    // Das gleiche gilt natürlich auch für die y-Achse.                     
 
        if((schritteX>3000 && schritteY>3000) ||(schritteX<-3000 && schritteY<-3000))                      // Wenn die Differenz beider! Achsenwerte größer als 3000 ist (also eigentlich 3), dann wurde die 
                                                                                                           // gezeichnete Linie unterbrochen und womöglich woanders fortgeführt. Da der Roboter dann nicht
                                                                                                           // die Punkte durch eine Linie verbinden soll, muss der Stift absetzen. Gleiches gilt auch, wenn 
                                                                                                           // die Differenz kleiner als -3000 (-3) ist.
        {
          //Stift von der Wand entfernen (zurückfahren)                                                    // Da der Stift nicht "richtig" abgesetzt werden kann, muss der Roboter einen kleinen Schritt
                                                                                                           // zurückfahren.
        }
        if((schritteX>3000 || schritteY>3000) ||(schritteX<-3000 || schritteY<-3000))                      // Auch wenn nur die Differenz der Werte einer! Achse größer als 3000 bzw. kleiner als -3000 ist, 
                                                                                                           // kann die Linie unterbrochen worden sein. Daher muss auch hier der Stift von der Wand abgesetzt 
                                                                                                           // werden.
        {
          //Stift von der Wand entfernen (zurückfahren)
        }
 
        stepperX.move(schritteX);                                                                          // Nun sollen die Schrittmotoren die berechneten Schritte ausführen. Das erfolgt über den 
                                                                                                           // 'move'-Befehl. Das gilt für beide Achsen.
        stepperY.move(schritteY);                                   
 
        if((schritteX>3000 && schritteY>3000) ||(schritteX<-3000 && schritteY<-3000)                       // Da die zu zeichnende Linie im nächsten Schritt weitergeht, muss der Stift wieder an die Wand 
                                                                                                           // angesetzt werden. Weil das nur nötig ist, wenn der Roboter sich vorher ein wenig von der Wand 
                                                                                                           // entfernt hat, gilt hier die gleiche Bedingung wie oben: Die Differenz der Achsenwerte muss  
                                                                                                           // größer als 3000 bzw. kleiner als -3000 sein (sie hat sich im Schritt darüber ja nicht geändert).
        {
          //Stift wieder an die Wand (ranfahren)
        }
 
        if((schritteX>3000 || schritteY>3000) ||(schritteX<-3000 || schritteY<-3000))
        {
          //Stift wieder an die Wand (ranfahren)
        }
 
        xALT = xNEU;                                                                                       // Die Achsenwerte 'xNEU' und 'yNEU' müssen nun 'xALT' und 'yALT' zugewiesen werden, da im nächsten  
                                                                                                           // Schritt eine Linie von dem ehemaligen "neuen" (jetzt "alten") Punkt zum jetzt "neuen" Punkt  
                                                                                                           // (neu eingelesene Werte) gezeichnet werden soll. 
        yALT = yNEU;
 
        Serial.print("Punkt_bitte");                                                                       // Nachdem der Roboter eine Linie gezeichnet hat, schreibt er über die serielle Schnittstelle an
                                                                                                           // C#, dass die nächsten Werte kommen können. Dieser Schritt ist notwendig, da Arduino nicht zu
                                                                                                           // viele Werte auf einmal erhalten und verarbeiten kann.
 
      }
      else
      {
        Serial.print("seriellen_Port_schließen");                                                          // Wenn der Arduino keine Werte mehr erhält, dann ist die Zeichnung fertig und die serielle 
                                                                                                           // Schnittstelle kann geschlossen werden, daher sendet er seriell an C# "seriellen_Port_schließen".
      }
 
      break;                                                                                               // Das Schlüsselwort break verläßt die switch-Anweisung.
 
 
    case'2':                                                                                               // Betätigen wir in C# den 'Anfangsposition'-Button, wird "2" über die serielle Schnittstelle an 
                                                                                                           // Arduino gesendet und folgendes Programm ausgeführt:
 
      //Stift von der Wand entfernen (zurückfahren)                                                        // Da der Stift nun wieder zu seiner Anfangsposition gefahren werden soll, muss er vorher von der 
                                                                                                           // Wand abgesetzt werden. 
 
      stepperX.move(-(stepperX.currentPosition()));                                                        // 'move' sagt dem Stepper, dass er sich "bewegen" soll. Durch den Befehl 'currentPosition' 
      stepperY.move(-(stepperY.currentPosition()));                                                        // (aktuelle Motorposition in Schritten) soll der bisher abgefahrene "Weg" wieder zurückgefahren 
                                                                                                           // (negativ) werden.
 
      break;
  }
 
}
projektews1415/graffitibot.txt · Zuletzt geändert: 2016/01/21 12:45 (Externe Bearbeitung)