PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Bewegung anhalten



Stratege993
25.12.2007, 21:13
Hallo,

dies ist mein erster Eintrag in dem Forum, da ich erst letzte Weinachten den RP6 bekommen habe.
Ich habe warscheinlich eine einfache Anfängerfrage. Ich habe meinen Roboter mit dem Befehl moveAtSpeed(100,100) auf die Reise geschickt, jetzt habe ich ein Hinderniss und nun soll er anhalten. Das mit der if-schleife und den Sensoren habe ich schon, ich bin mir auch sicher das er bis dahin kommt, da die Ausgabe geht, aber ich finde keinen Befehl um ihn anzuhalten. Dies habe ich schon ausprobiert:

moveAtSpeed(0,0)
stop()
beides geht aber nicht. Meine Frage ist nun kurzgefasst:

Wie heist der Befehl um meinen Roboter anzuhalten?

Stratege993

sloti
25.12.2007, 21:23
Wenn du die von dir eben beschriebenen funktionen verwendest musst du dazu auch "task_motionControl();" in der hauptschleife aufrufen. Wenn du dann "task_motionControl()"; verwedest musst du auch nach "initRobotBase();" "powerON();" aufrufen um die Encoder anzuschalten. Wenn du das alles so machst solltest du mit der Funktion "stop();" den RP6 anhalten können.

mfg
Erik

Stratege993
25.12.2007, 21:32
Also ich finde nicht das Problem ich stelle mal den ganze Qulltext rein ist ja nicht so interressant:

#include "RP6RobotBaseLib.h"
uint8_t ok=0;

void senkrecht (void)
{
writeString_P("achtung");
stop();
task_motionControl();
while(ok == 0) //nicht wundern das die schleife nie endet
{
statusLEDs.LED5 = obstacle_left; //Hinderniss Links
statusLEDs.LED2 = obstacle_right; //Hinderniss rechts
updateStatusLEDs();
}
}

int main(void)
{
initRobotBase();
powerON();
setACSPwrHigh();

moveAtSpeed(100,150); //erste Bewegung
startStopwatch1(); //Zeitnhemer bis andere Richtung

while(true)
{
task_motionControl();
task_ACS();
if (getStopwatch1() >= 3000) //rechts herum
{
stopStopwatch1();
moveAtSpeed(150,100);
setStopwatch1(0);
startStopwatch2();
}
if (getStopwatch2() >= 3000) //links herum
{
stopStopwatch2();
moveAtSpeed(100,150);
setStopwatch2(0);
startStopwatch1();
}
if (obstacle_left || obstacle_right)
{
senkrecht();
}
}
return 0;
}

Also bitte nicht wundern, dass die eine Schleife nie endet, da wird später noch weiter gemacht, dass der Roboter sich senkrecht zur Wand ausrichtet.
Das Schlengellienienfahren bewirkt das ein größerer Bereich gemessen wird.
Stratege993

sloti
25.12.2007, 21:41
Ich kann jetzt auch keinen Fehler erkennen. Du könntest mal Probieren das task_motionControl aus der senkrecht funktion in die endlosschleife der funktion tun.

mfg
Erik

Stratege993
25.12.2007, 21:47
Habe das Problem. Man musss den Befehl initRobotBase() in jede Funktion einzeln reinschreiben.
Jetzt meine Frage, kann man sagen das es für alle Funktionen gild mit blos einer Zeile, damit man es net bei jeder Funktion reinschreiben muss?
mfg Stratege993

sloti
25.12.2007, 21:49
Also ich habs eben bei mir ausprobiert, wenn ich task_motionControl in die Schleife von senkrecht packe gehts.

mfg
Erik

sloti
25.12.2007, 21:53
also, dass man in jeder Funktion den Controller neu initialisieren muss wäre mir neu aber wenn es so geht is ja in Ordnung :). Aber wie gesagt ich hab nur die motion control verschoben und es geht auch.


#include "RP6RobotBaseLib.h"
uint8_t ok=0;

void senkrecht (void)
{
writeString_P("achtung");
stop();

while(ok == 0) //nicht wundern das die schleife nie endet
{
task_motionControl();
statusLEDs.LED5 = obstacle_left; //Hinderniss Links
statusLEDs.LED2 = obstacle_right; //Hinderniss rechts
updateStatusLEDs();
}
}

int main(void)
{
initRobotBase();
powerON();
setACSPwrHigh();

moveAtSpeed(100,150); //erste Bewegung
startStopwatch1(); //Zeitnhemer bis andere Richtung

while(true)
{
task_motionControl();
task_ACS();
if (getStopwatch1() >= 3000) //rechts herum
{
stopStopwatch1();
moveAtSpeed(150,100);
setStopwatch1(0);
startStopwatch2();
}
if (getStopwatch2() >= 3000) //links herum
{
stopStopwatch2();
moveAtSpeed(100,150);
setStopwatch2(0);
startStopwatch1();
}
if (obstacle_left || obstacle_right)
{
senkrecht();
}
}
return 0;
}

Dirk
26.12.2007, 10:26
Hallo Leute,

die Funktion "initRobotBase();" gehört an den Anfang von Main, sollte sonst nirgendwo mehr aufgerufen werden. Das gleiche gilt für "powerON();", damit die Sensoren Strom kriegen.

Die Funktion "task_motionControl();" muss in der Hauptschleife regelmäßig aufgerufen werden. Wenn man auch Sensoren verwenden will, sollte man stattdessen "task_RP6System();" in der Hauptschleife aufrufen. Darin ist dann auch die Motorenkontrolle enthalten.
Die Hauptschleife sollte nicht durch irgendwelche Funktionen aufgehalten werden! Damit kann sie ja nicht mehr regelmäßig ablaufen und die Sensoren und Motoren funktionieren nicht mehr richtig.
Wenn man Bumper o.ä. abfragen will und man warten muss, bis sich da etwas tut oder nicht tut, sollte man nur ein Flag setzen und darauf in der Hauptschleife reagieren (z.B. RP6 anhalten). Die Hauptschleife muss immer durchlaufen.

Gruß Dirk

Stratege993
26.12.2007, 10:33
Ok, aber das blöde ist das der robby nicht gleich beim ersten task_motionControl() anhält, obwohl ich schon davor stop() sagte, sondern man braucht viele durchläufe bis er reagiert. Kann man das irgendwie besser machen?
Stratege993

Dirk
26.12.2007, 10:42
Hallo Stratege993,

die Hauptschleife in Main muss möglichst schnell durchlaufen. Gut wäre, wenn alle Tasks etwa alle 20-30ms wieder dran kämen. Dann gibt es auch keine Verzögerungen. Also: Nie die Hauptschleife unterbrechen oder zu lange Pausen machen.

Gruß Dirk

sloti
26.12.2007, 10:43
Ich glaub nicht, da alle task befehle beim RP6 im Hintergrund laufen und immer dann ausgeführt werden, wenn der Contoler gerade "Zeit hat". So hab ich es zumidest verstanden :).

mfg
Erik

Stratege993
26.12.2007, 10:46
OK,
wie gesagt ich bin noch Anfänger, also noch eine letzte Frage:
Wie bekomme ich hin, dass die eine Schleife immer durchläuft und parralel andere Schleifen durchlaufen wenn was bestimmtes ist. Ist das überhaupt möglich, oder habe ich was falsch verstanden?
Stratege993

Dirk
26.12.2007, 10:54
Hallo Stratege993,

Beispiel ACS:
Wenn man die Task task_ACS() in die Hauptschleife aufnimmt, hat man jederzeit die Variablen obstacle_left/right zur Verfügung. Man kann sie in der Hauptschleife abfragen und reagieren, wenn eine Variable oder beide True sind.
Dadurch läuft die Hauptschleife weiter und es passiert nur was, wenn ein Hindernis erkannt wird (Roboter stop oder wenden ...).

Man muss die Hauptschleife nicht aufhalten.

Gruß Dirk

sloti
26.12.2007, 11:04
Du kannst nie 2 Sachen gleichzeitig oder paralel ausführen, aber du kannst 2 Schleifen nacheinander aufrufen. Du musst dann nur eine passende Schleifenbedingung benutzen, also keine Endlosschleife.

mfg
Erik

SlyD
26.12.2007, 11:14
... man kann es mit den Stopwatches der RP6Lib allerdings so wirken lassen, als würde es gleichzeitig ausgeführt. Mit den Stopwatches kann man z.B. erreichen, dass ein bestimmter Programmteil alle 50ms ausgeführt wird, ein anderer alle 122ms und ein weiterer alle 3 Sekunden.

Man kann die Stopwatches zusätzlich bedingt starten und stoppen und so einfache abläufe programmieren.

Schau Dir mal die entsprechenden Beispiele in der Anleitung bzw. in den Beispielprogrammen an.

Wenn man dann etwas komplexere Dinge realisieren will, kann man Automaten verwenden.

MfG,
SlyD

roboterheld
26.12.2007, 11:54
auszug........

Der Watchdog

Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns Programmierern, der Watchdog.

So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut perfekte und fehlerfreie Programm zu entwickeln.

Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in's Nirwana verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers ausgelöst wird.

Betrachten wir doch einmal folgende Codesequenz:

uint8_t x;

x = 10;

while (x >= 0)
{
// tu was

x--;
}

Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als unsigned deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben). Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen. Und hier genau kommt der Watchdog zum Zug.
[bearbeiten]
Wie funktioniert nun der Watchdog?

Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Einige Controller haben einen eigenen Watchdog Oszillator, z.B. der Tiny2313 mit 128kHz. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde, beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern, müssen wir den Watchdog regelmäßig wieder neu starten bzw. rücksetzen (Watchdog Reset). Dies sollte innerhalb unserer Hauptschleife passieren.

Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern, muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten. Es müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.

Das Watchdog Control Register:
WDTCR Watchog Timer Control Register

In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.

Das Register ist wie folgt aufgebaut:
Bit 7 6 5 4 3 2 1 0
Name - - - WDTOE WDE WDP2 WDP1 WDP0
R/W R R R R/W R/W R/W R/W R/W
Initialwert 0 0 0 0 0 0 0 0

WDTOE (Watchdog Turn Off Enable)

Dieses Bit muss gesetzt sein, wenn das Bit WDE gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.
Wenn das Bit einmal gesetzt ist, wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.

WDE (Watchdog Enable)

Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.
Das Bit kann nur gelöscht werden, solange das Bit WDTOE auf 1 steht.

WDP2, WDP1, WDP0 (Watchdog Timer Prescaler Bits)

Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:

WDP2 WDP1 WDP0 Anzahl Zyklen Typ. Timeoutzeit bei Vcc = 3V Typ. Timeoutzeit bei Vcc = 5V
0 0 0 16K 47ms 15ms
0 0 1 32K 94ms 30ms
0 1 0 64K 0.19s 60ms
0 1 1 128K 0.38s 0.12s
1 0 0 256K 0.75s 0.24s
1 0 1 512K 1.5s 0.49s
1 1 0 1024K 3s 0.97s
1 1 1 2048K 6s 1.9s

Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei wdt.h (#include <avr/wdt.h>) in die Quelldatei eingebunden werden. Danach können die folgenden Funktionen verwendet werden:

* wdt_enable(uint8_t timeout)

Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert
Mögliche Timeoutwerte:

Konstante Wert TimeOut
WDTO_15MS 0 15 ms
WDTO_30MS 1 30 ms
WDTO_60MS 2 60 ms
WDTO_120MS 3 120 ms
WDTO_250MS 4 250 ms
WDTO_500MS 5 500 ms
WDTO_1S 6 1 S
WDTO_2S 7 2s

* wdt_disable()

Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.

* wdt_reset()

Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.

Selbstverständlich kann das WDTCR-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.
[bearbeiten]
Watchdog-Anwendungshinweise

Ob nun der Watchdog als Schutzfunktion überhaupt verwendet werden soll, hängt stark von der Anwendung, der genutzten Peripherie und dem Umfang und der Qualitätssicherung des Codes ab. Will man sicher gehen, dass ein Programm sich nicht in einer Endlosschleife verfängt, ist der Wachdog das geeignete Mittel dies zu verhindern. Weiterhin kann bei geschickter Programmierung der Watchdog dazu genutzt werden, bestimmte Stromsparfunktionen zu implementieren. Bei einigen neueren AVRs (z.B. dem ATTiny13) kann der Watchdog auch direkt als Timer genutzt werden, der den Controller aus einem Schlafmodus aufweckt. Auch dies kann im WDTCR-Register eingestellt werden. Außerdem bietet der WD die einzige Möglichkeit einen beabsichtigten System-Reset (ein "richtiger Reset", kein "jmp 0x0000") ohne externe Beschaltung auszulösen, was z.B. bei der Implementierung eines Bootloaders nützlich ist. Bei bestimmten Anwendungen kann die Nutzung des WD als "ultimative Deadlock-Sicherung für nicht bedachte Zustände" natürlich immer als zusätzliche Sicherung dienen.

Es besteht die Möglichkeit herauszufinden, ob ein Reset durch den Watchdog ausgelöst wurde (beim ATmega16 z.B. Bit WDRF in MCUCSR). Diese Information sollte auch genutzt werden, falls ein WD-Reset in der Anwendung nicht planmäßig implementiert wurde. Zum Beispiel kann man eine LED an einen freien Pin hängen, die nur bei einem Reset durch den WD aufleuchtet oder aber das "Ereignis WD-Reset" im internen EEPROM des AVR absichern, um die Information später z.B. über UART oder ein Display auszugeben (oder einfach den EEPROM-Inhalt über die ISP/JTAG-Schnittstelle auslesen).

SlyD
26.12.2007, 12:08
Und das hat mit dem Thema des Threads was genau zu tun?
Genau - nichts.

Ein Link auf den Mikrocontroller.net Artikel hätte übrigens auch gereicht:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Der_Watchdog