PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : RP6 - Schulprojekt: Ultraschall und Labyrinth



Tim Hansen
25.01.2012, 09:53
Hallo Leute.

Wir sollen in der Schule ein technisches Projekt machen und bekommen dafür sogar ein kleines Budget.

Ich habe vor ein paar Tagen durch Zufall meinen RP6 gefunden, den ich vor 3 oder 4 Jahren mal bekommen habe. Ich habe mir nun gedacht, dass man damit ja ein tolles Projekt realisieren könnte.

Ich möchte den RP6 dazu bringen, ein Labyrinth zu erkennen und auch zu lösen. Hierfür dachte ich lediglich an ein paar Arrays, die einmal die X- und Y-Positionen speichern und eventuell noch sagen, wo es einen Abzweig gab. Das Konzept ist noch nicht ganz ausgereift :)


Es geht jetzt darum: Wie soll der RP6 sich überhaupt selbst im Raum wiederfinden? Ich dachte hierbei eventuell an Ultraschallsensoren, mit denen er "vorne", "links" und "rechts" immer nach Hindernissen oder eben Öffnungen im Labyrinth sucht.

Ich müsste mich erstmal wieder in den Roboter und seine Programmierung hineinfinden, aber ich beherrsche mittlerweile C# ausgesprochen sicher und ich denke, wenn ich mir die Struktur und Programmierweise ein wenig angucke, sollte das wieder gehen. Habe ihn auch gestern schon ein wenig herumfahren lassen.

Jedoch: Wie baue ich so etwas auf den RP6? Wie spreche ich es dann an? Ich habe meinen noch nie erweitert. Vielleicht könnte mir das jemand kurz anhand eines Summers erklären, davon hab ich nämlich grade einen rumliegen.

Und dann: Reicht der Arbeitsspeicher überhaupt für ein größeres Koordinatensystem? Der hat ja nur 32KB. Ich wollte ihn gerne in einem 2x2 Meter-Labyrinth fahren lassen. Wie sollte ich dort die Abstände setzen?


Danke schonmal im vorraus!

masasibe
25.01.2012, 13:53
lHallo,
für die Ultraschallsensoren gibt es hier eine gute Schaltung:
http://www.rn-wissen.de/index.php/Ultraschall-Interface
Ich würde das ganze noch mit Odometriesensoren kombinieren, dann könntest du dich schon recht gut im Raum orientieren.

Dein 2x2m Labyrinth kannst du in Raster zu ca. 10x10 cm einteilen also insgesamt 400 Rasterteile.
Im Atmega kannst du dann eine Rasterkarte anlegen, sagen wir für jeden Rasterteil ein Byte (geht sicher auch mit weniger um noch mehr Platz zu sparen) das wären dann also rund 400Bytes; ich denke das geht schon.
Schlimmstenfalls mannst du ja noch mit einem exgernen Ram nachhelfen.
In die Bytes kommen dann Informationen, wie z.b hier war ich schon oder hier ist eine Wand.

Dass sich der Atmega dann wirklich im Labyrinth zurechtfindet ist dann sicher noch ein Aufwand, aber ich denke mit genug Experimentierfreude sicher machbar. ;-)

PS: Der Ram ist übrigens nur 2kb groß. 32kb isr der Flash also dort wo das Programm gespeichert wird!

Calis007
25.01.2012, 14:02
Ich wuerde mir bei sowas zuerst ueberlegen, wie das Labyrinth aufgebaut ist, und dann erst, welche Sensoren dafuer geeignet sind.
Wenn Du z.B. auf 2x2m nur ein 6x6-Feld aus quadratischen Elementen aufbaust, sollte es recht einfach werden (Elementlaenge 33.33cm). Weisse Waende, dunkler Boden und es reichen vermutlich 3 LEDs und 3 LDRs. Da spielen auch kleine Odometriefehler keine Rolle, wenn alles rechtwinkelig ist und die Distanzen bekannt sind (Vielfache von einem Drittel Meter). Speicherprobleme gibt's da sicher auch keine bei nur 36 Feldern..
Das ist wohl der einfachste Fall. Das Schwierigste ist dabei wohl der Aufbau des Labyrinths :p

Die Komplexitaet steigt aber sofort, wenn du unregelmaessige Abstaende zulaesst oder sogar von rechten Winkeln abgehst..

PICture
25.01.2012, 14:08
Hallo!

Um aus Labirynth raus zu kommen (bisher das Ziel des RP6 ist leider nicht genau definiert), reicht sich anliegend an einer Wand mit (z.B. virtuellen) Bumper zu bewegen aus. ;)

Tim Hansen
25.01.2012, 14:15
PS: Der Ram ist übrigens nur 2kb groß. 32kb isr der Flash also dort wo das Programm gespeichert wird!

Oh. Noch schlimmer :D


Die Idee, das Feld symmetrisch zu halten ist recht gut. Und das mit den LEDs und LDRs ist auch eine klasse Idee. Wie baut man so etwas auf? Tut mir leid, aber da habe ich wirklich keine Ahnung... Wir sollten aber bei dem Projekt auch etwas wählen, wo wir komplett von neuem lernen müssen.

//Nachtrag:

Stimmt, jetzt wo ich drüber nachdenke... Einfach an der Wand lang...

Ja und das Ziel ist tatsächlich ein Problem, ich dachte einfach, dass ich ihn dann halt weiterfahren lasse und aufhebe, so als alter Techniker :D

Calis007
25.01.2012, 14:58
Also ich kenn den PR6 nicht so gut, aber die Mikrokontroller haben ueblicherweise Analogeingaenge zum Messen von Spannungen.
Also ein Spannungsteiler aus festem Widerstand und dem LDR (oder noch besser Fototransistor, der hat kleineren Lichteinfallswinkel) und die Spannung messen. Funktioniert aber nur gut bei wenig (oder gleichmaessigem) Fremdlichteinfall.
(Also besser hohe Waende im Labyrinth und von oben gleichmaessig beleuchtet, Ultrahelle LEDs und Gangbreite nicht zu gross)

Das Ziel ist auch kein Problem, wenn es nach 'draussen' geht. Da erkennst Du ja auch, dass Du das 6x6-Feld verlaesst ;)
Evtl. den Schach-Trick anwenden und intern eine 8x8-Matrix (also zwei Reihen und zwei Spalten groesser als das eigentliche Spielfeld) bauen, wo alle 'Randfelder' als 'ausserhalb' = Ziel markiert sind.

Tim Hansen
25.01.2012, 15:24
Wie kann ich denn eigentlich einfach so etwas wie einen Summer da anbauen?

Ich will einfach nur, dass der "Mööp" macht, wenn er irgendwo gegen fährt. Wie geht so etwas?

avrrobot
25.01.2012, 15:41
Du nimmst einen Piezo Summer und schließt ihn an zwei Ports an, diese schaltest du einfach abwechselnd im Gegentakt ein/aus.

PICture
25.01.2012, 15:53
Einfacher wäre ein Piezosummer mit integrierter Elektronik durch ein Pin schalten. ;)

Calis007
25.01.2012, 15:54
ADC0 und ADC1 sind meines Wissens noch unbelegt am originalen RP6, die sollte man auch als Ausgaenge benutzen koennen fuer kleine Verbraucher (20mA, 5V), kann das ein Kenner bestaetigen?
Was waere normalerweise sonst noch wo rausgefuehrt und unbenutzt?

fabqu
25.01.2012, 16:03
Oder aber, du nimmst einen fertigen Summer (also einen, der schon summt wenn man Spannung draufgibt) und schaltest ihn mit nem I/O ein :D
Hab hier so ein kleines Teil rumliegen. Ist nicht viel größer, als andere Summer, aber hat schon die Hardware zum Summen drin...

Klingt nach einem lustigen Projekt ;) Würd ich gern auch mal machen, wenn ich Platz für 2x2m Labyrinthe hätte!!!

Torrentula
25.01.2012, 16:15
für die Ultraschallsensoren gibt es hier eine gute Schaltung:
http://www.rn-wissen.de/index.php/Ultraschall-Interface

Ohne ein Oszilloskop gleicht das Abstimmen einem Blindflug. Lieber ein einfach anzusteuerndes fertiges US-Modul verwenden, wie z.B. den SRF05

Um aus dem Labyrinth zu kommen, kann man einen "Left hand on the wall"-Algorithmus verwenden (wobei sich da Algorithmus schon wieder sehr komplex anhört). Das ist ziemlich einfach man muss nur prüfen in welche Richtung man fahren kann und sich diese Richtung merken, in die man Abgebogen ist (eben als ob man die linke Hand ständig an der Wand behält):

1. Kann ich links abbiegen, biege ich links ab --> L
2. Kann ich geradeaus fahren, fahre ich geradeaus --> S
3. Kann ich rechts abbiegen, biege ich ab --> R
4. Bin ich in einer Sackgasse, drehe ich um --> B

Man muss sich nun den Pfad anschauen, z.B. LBL. Dieses lässt sich ganz einfach durch S ersetzen. Hier ist die ganze Liste:

LBR = B
LBS = R
RBL = B
SBL = R
SBS = B
LBL = S

Das kann man z.B. verwenden um den Pfad zu optimieren und ihn das Labyrinth ein zweites Mal auf dem optimalen Pfad entlangfahren zu lassen.

( Quelle: Let's Make Robots (http://letsmakerobots.com/node/26420))

MfG

Torrentula

masasibe
25.01.2012, 16:39
Nachteil von Ultraschallmodulen, die eine Entfernungsmessung durch
die Stärke des empfangenen Signals ermitteln (z.b. das Ultraschallmodule von Conrad), ist, dass sie sehr
von der reflektierenden Oberfläche abhängig sind.
Besser geht das, wenn man stattdessen die benötigte Zeit des
Schalls misst.

Allerdings ist das mit der Oberfläche für das Labyrinth wahrscheinlich
kein so großes Problem, da sie ja im ganzen Labyrinth die gleich sein wird
und man das dann kalibrieren könnte.

avrrobot
25.01.2012, 16:43
Außerdem kann man die Ultraschall-Module ja auch steckbar machen, und dann später für andere Zwecke verwenden.
Ich finde, es lohnt sich allein schon wegen der Reichtweite (bei Conrad Modul eher gering) ein gutes US-Modul zu kaufen.
Sooooooo teuer sind die ja auch nicht.

Tim Hansen
25.01.2012, 16:53
Also, wenn ich den beschriebenen Algorithmus nutze, dann sollte das ja eigentlich wirklich nicht besonders schwer sein.

Die andere Sache mit dem Summer.

Ich habe hier einen Summer, wenn man den an 'ne Stromquelle hält, dann macht der "Mööp". Hat 2 Drähte und ist rund :D
Das Ding soll jetzt ohne höheren Aufwand und für Laien verständlich auf meinen RP6 gefriemelt werden.


Wenn ich das Labyrinth aber wirklich mit dem beschriebenen Algorithmus lösen lasse, dann brauche ich ja an und für sich keine Ultraschallsensoren sondern einfach 2 Seitenbumper, die nunmal auf "false" springen, sobald links oder rechts eine Öffnung ist. Obwohl es dann schwierig wäre, den RP6 dann zu drehen, es sei denn, er würde durch die Bumper praktisch kreisförmig gemacht, wenn ihr versteht, was ich meine...

fabqu
25.01.2012, 16:58
Jap. So einen Summer meine ich!
Die beiden Kabel, das sind + und -.
+ Schließt du an deine VCC, das ist die 5V-Betriebsspannung des RP6 (vielleicht vorher noch rausfinden, ob der Summer 5V verträgt, oder weniger, oder ob er mehr braucht. Aber 5V sind denke ich gut).
- schließt du über einen Transistor (bc547 zum Beispiel) an GND und an den I/O, den du verwenden möchtest. Bei der Base kann man z.B. wunderschön die I/Os 2 und 5 verwenden, das sind die mittleren LEDs in den 3er-Reihen (SL2 und SL5). Die werden nirgends gebraucht. Wenn du da Hilfe brauchst, sag bescheid.
Deine zusätzlichen Bumper kann man an die LEDs SL1 und SL4 anschließen, an SL3 und SL6 sitzen schon die Standard-Bumper.
SL4 ist glaube ich auch noch frei, SL1 ist die Blinke-LED, ist aber an sonsten auch verfügbar.

Grüße

PICture
25.01.2012, 17:16
@ Tim Hansen

Wenn dein Summer nur ein paar mA (Strom) braucht, kannst du ihn direkt anschliessen (ohne Transistor, wie LED, aber ohne Vorwiderstand). ;)

masasibe
25.01.2012, 18:56
Mit Bumper ist das sicher einfacher und billiger zu lösen als mit Ultraschall, aber eleganter sieht es natürlich mit Ultraschall aus, weil der Roboter dann nicht gegen eine Wand fährt wenn er in eine Sackgasse fährt.
Aber man kann es ja zuerst mit Bumper und später als Erweiterung vielleicht mit Ultraschall realisieren.

Tim Hansen
26.01.2012, 15:54
Also, meine Überlegung ist jetzt momentan, ihn das ganze mit dem ACS machen zu lassen. Er soll sich auf jedem Feldchen einmal nach links umschauen und bei Möglichkeit abbiegen. Wenn vorne, links und rechts blockiert ist, soll er umdrehen.

Jetzt habe ich aber ein Problem mit meinem Code.

Wieso wird immer das aus "ELSE {}" gemacht?



// Includes:

#include "RP6RobotBaseLib.h"

uint8_t acs_bool = 0;

// Main:

void acsStateChanged(void)
{
//Nix.
}

int main(void)
{
initRobotBase();
setLEDs(0b111111);
mSleep(2500);

powerON(); // Strom für alle Systeme wie ACS, Bumper und die LEDs

ACS_setStateChangedHandler(acsStateChanged);

// Main loop
while(true)
{
setLEDs(0b100100);

//LEDs setzen
setLEDs(0b111001);
updateStatusLEDs();

// Prüfe, ob links eine Öffnung ist
if (obstacle_left && obstacle_right)
{
// LEDs setzen
setLEDs(0b001111);
updateStatusLEDs();

// Rotiere wieder zurück in gerade Position
rotate(40, RIGHT, 90, BLOCKING);

//LEDs setzen
setLEDs(0b001001);
updateStatusLEDs();

// Prüfe, ob vorne ein Hindernis ist
if (obstacle_left && obstacle_right)
{
// LEDs setzen
setLEDs(0b001111);
updateStatusLEDs();

// Rotiere rechts
rotate(40, RIGHT, 90, BLOCKING);

//LEDs setzen
setLEDs(0b111001);
updateStatusLEDs();

// Prüfe, ob rechts ein Hindernis ist
if (obstacle_left && obstacle_right)
{
// LEDs setzen
setLEDs(0b001111);
updateStatusLEDs();

// Rotiere rechts
rotate(40, RIGHT, 90, BLOCKING);

//LEDs setzen
setLEDs(0b111001);
updateStatusLEDs();
} else { move(50, FWD, DIST_MM(250), BLOCKING);}
} else { move(50, FWD, DIST_MM(250), BLOCKING);}
} else { move(50, FWD, DIST_MM(250), BLOCKING);}



}
return 0;
}

Das verstehe ich nicht :O

Danke schonmal!

masasibe
26.01.2012, 16:14
Wieso wird immer das aus "ELSE {}" gemacht?
Was genau meinst du? Was soll aus dem else gemacht werden?
Das else sieht doch ganz normal aus.
Edit: Ah jetzt verstehe ich, du hast gemeint, dass immer der else-Block ausgeführt wird.
Sorry war ein missverständnis ;-)

Tim Hansen
26.01.2012, 16:39
Die IF Abfrage scheint immer "false" zu sein, da immer das ELSE durchgeführt wird. Ich hab da meine Hand, 'ne Zeitung, etc. vorgehalten, aber der Roboter fährt ungeniert weiter. Der erkennt offensichtlich das Hindernis nicht.

radbruch
26.01.2012, 16:40
Hallo

Es wird immer der else-Zweig ausgeführt, weil obstacle_left und obstacle_right vom Tasksystem gesetzt werden, wenn etwas im Blickfeld erscheint:


// Prüfe, ob xxxx eine Öffnung ist
task_ACS();
if (obstacle_left && obstacle_right)
(ungetestet)

Tasksystem direkt vor der Abfrage der Variablen könnte funktionieren. Das ACS muss man auch noch initialisieren:


enableACS();
//setACSPwrLow();
setACSPwrMed();
//setACSPwrHigh();
Das sollte noch irgendwo vor der While()-Schleife erledigt werden.

Gruß

mic

[Edit]
Das wird so auch nicht funktionieren, weil der ACS-Task mehrere Aufrufe benötigt. Der Task funktioniert wie eine Schrittkette:

#define ACS_STATE_IDLE 0
#define ACS_STATE_IRCOMM_DELAY 1
#define ACS_STATE_SEND_LEFT 2
#define ACS_STATE_WAIT_LEFT 3
#define ACS_STATE_SEND_RIGHT 5
#define ACS_STATE_WAIT_RIGHT 6

Vermutlich mußt du auf das Blocking verzichten und die Bewegungen mit dem Tasksystem ausführen:
https://www.roboternetz.de/community/threads/49623-Bumper-bet%C3%A4tigt-RP6-ignoriert-moveAtSpee-Befehl

Tim Hansen
08.02.2012, 17:43
Also, ich habe jetzt etwas mit Tasksystem, was erstmal die Basisbewegung darstellt.

In meinem Beispiel wird Bumper links für "Hindernis" und Bumper rechts für "Kein Hindernis" verwendet. Da sollen dann irgendwann mal die ACS-Tasks hin, da ich da aber ja im Moment hänge habe ich das erstmal mit den Bumpern gemacht, damit ich die in der Testumgebung eben fix drücken kann.



#include "RP6RobotBaseLib.h"

#define STATE_START 0
#define STATE_LEFT_OBSTACLE 1
#define STATE_FRONT_OBSTACLE 2
#define STATE_RIGHT_OBSTACLE 3
#define STATE_ALL_OBSTACLE 4
#define STATE_MOVE_FORWARD 5

uint8_t move_state = STATE_START;

void move_stateMachine(void)
{
switch(move_state)
{
case STATE_START:
setLEDs(0b010000);
move_state = STATE_LEFT_OBSTACLE;
rotate(50, LEFT, 90, true);
break;

case STATE_LEFT_OBSTACLE:
if(isMovementComplete())
{
if(getStopwatch1() > 500)
{
writeString_P("Blinke links");
statusLEDs.LED5 = !statusLEDs.LED5;
updateStatusLEDs();
setStopwatch1(0);
}
if(bumper_left)
{
writeString_P("Bumper links getroffen");
writeString_P("HINDERNIS");
setLEDs(0b011001);
rotate(50, RIGHT, 90, true);
move_state = STATE_FRONT_OBSTACLE;
}
if(bumper_right)
{
writeString_P("Bumper rechts getroffen");
writeString_P("KEIN HINDERNIS");
setLEDs(0b011001);
move_state = STATE_MOVE_FORWARD;
}
}
break;
case STATE_FRONT_OBSTACLE:
if(isMovementComplete())
{
if(getStopwatch1() > 500)
{
writeString_P("Blinke links");
statusLEDs.LED5 = !statusLEDs.LED5;
updateStatusLEDs();
setStopwatch1(0);
}
if(bumper_left)
{
writeString_P("Bumper links getroffen");
writeString_P("HINDERNIS");
setLEDs(0b011001);
rotate(50, RIGHT, 90, true);
move_state = STATE_RIGHT_OBSTACLE;
}
if(bumper_right)
{
writeString_P("Bumper rechts getroffen");
writeString_P("KEIN HINDERNIS");
setLEDs(0b011001);
move_state = STATE_MOVE_FORWARD;
}
}
break;

case STATE_RIGHT_OBSTACLE:
if(isMovementComplete())
{
if(getStopwatch1() > 500)
{
writeString_P("Blinke links");
statusLEDs.LED5 = !statusLEDs.LED5;
updateStatusLEDs();
setStopwatch1(0);
}
if(bumper_left)
{
writeString_P("Bumper links getroffen");
writeString_P("HINDERNIS");
setLEDs(0b011001);
rotate(50, RIGHT, 90, true);
move_state = STATE_ALL_OBSTACLE;
}
if(bumper_right)
{
writeString_P("Bumper rechts getroffen");
writeString_P("KEIN HINDERNIS");
setLEDs(0b011001);
move_state = STATE_MOVE_FORWARD;
}
}
break;

case STATE_ALL_OBSTACLE:
if(isMovementComplete())
{
if(getStopwatch1() > 500)
{
writeString_P("Blinke links");
statusLEDs.LED5 = !statusLEDs.LED5;
updateStatusLEDs();
setStopwatch1(0);
}
if(bumper_left)
{
writeString_P("Bumper links getroffen");
writeString_P("ERROR! GEFANGEN!");
setLEDs(0b011001);
rotate(50, RIGHT, 90, true);
}
if(bumper_right)
{
writeString_P("Bumper rechts getroffen");
writeString_P("KEIN HINDERNIS");
setLEDs(0b011001);
move_state = STATE_MOVE_FORWARD;
}
}
break;

case STATE_MOVE_FORWARD:
if(isMovementComplete())
{
move(50, FWD, DIST_MM(250), true);
move_state = STATE_START;
}
break;
}
}

int main(void)
{
initRobotBase();
setLEDs(0b111111);
mSleep(1000);

powerON();

startStopwatch1();

while(true)
{
move_stateMachine();
task_RP6System();
}
return 0;
}



Meine Frage ist nun, wie spreche ich denn nun die ACS-Tasks an. Geht das nicht einfach so wie die Bumper!?

Und noch etwas zu meiner Peinlichkeit: Was genau meint eigentlich das Blocking? Habe es mir einfach so zusammengeschnipselt.