-
Kurve fahren
hallo
ich möchte ein älteres thema wieder aufgreifen:
und zwar habe ich versucht den asuro eine kurve fahern zu lassen. dabei habe ich mich an GoTurn orientiert, aber leider klappt es nicht so wie ich wollte. vl kann mir jemand helfen.
hier einmal mein code:
Code:
#define BREITE 100 //spurbreite in mm
#define PI 3.141592
void kurve(int radius, int winkel, int speed) //radius in mm, winkel in Grad
{
unsigned long count_a_soll;
int count_a=0, count_i=0;
int speed_a, speed_i;
int radius_a, radius_i;
float quot, teiler;
MotorSpeed(0,0);
MotorDir(FWD,FWD);
radius_a=radius+BREITE/2;
radius_i=radius-BREITE/2;
teiler=(float)360/winkel; //Bruchteil des Kreises
quot=((float)radius_a)/((float)radius_i);
//berechnen der notwendigen tics am außenrad
count_a_soll=2*radius_a*(PI*10000L);
count_a_soll/=MY_GO_ENC_COUNT_VALUE;
//anfangsgeschwindigkeiten berechnen
speed_a=speed;
speed_i=speed/quot;
//Motoren starten
MotorSpeed(speed_i,speed_a);
while(count_a<count_a_soll)
{
count_a+=encoder[RIGHT];
count_i+=encoder[LEFT];
if(count_a<(count_i*quot)) speed_a+=5; //zu großer radius
if(count_a>(count_i*quot)) speed_a-=5; //zu kleiner radius
//reset encoder
EncoderSet(0,0);
MotorSpeed(speed_i,speed_a);
Msleep(1);
}
MotorDir(BREAK,BREAK);
Msleep(200);
}
kurz zur erklärung:
-erstmal kann nur eine kurve gegen den uhrzeigersinn gefahren werden (andere richtung möchte ich erst einbauen wenn diese funktioniert)
- _a bedeutet immer außen(rad)
- _i bedeutet immer innen(rad)
wenn ich diese funktion in ein programm einbaue und den asuro nach erfolgreichem flashen einschlate, dann fährt er zwar im kreis und regelt nach, jedoch hält er nicht nach dem vorgegebenen winkel an, und der radius stimmt leider auch nicht ganz
ebenso ist mir beim experimentieren aufgefallen, dass es bei mir auch bei der GoTurn funktion probleme gibt (er hält nich nach vorgegebenem weg an). daher vermute ich, dass es bei mir bei der odometrie ein problem gibt und würde euch ersuchen diese funktion einmal aus zu probieren, damit ich weiß, ober es nur an meinem asuro bzw. den werten in myasuro.h liegt, oder doch am oben angeführten code.
ich bedanke mich jetzt schon mal für eure hilfe und werde selber auch noch weiter experimentieren
[edit]
sorry, ich habe vergessen zu erwähnen, dass sich der radius auf die mitte der hinteren achse bezieht (daher jeweils die korrektur um BREITE / 2 )
[/edit]
-
Hai hai1991,
wie ich dass sehe, wird dein speed_i immer kleiner als speed_a
Über:
-- radius_a=radius+BREITE/2;
-- radius_i=radius-BREITE/2;
bekommst du also .._a ist immer größer als .._i , und dann mit:
-- quot=((float)radius_a)/((float)radius_i);
immer einen Wert in quot > 1, und dann mit:
-- speed_i=speed/quot;
immer speed_i = speed / (größer 1) also speed_i < speed.
So weit so gut.
Auch count_a_soll ist scheinbar auf dem richtig Weg berechnet.
Nun brichst du deine While()-Loop aber nur bzw. schon dann ab, wenn count_a den count_a_soll-Wert erreicht hat.
Du macht keine Prüfung, ob ein count_i_soll-Wert erreicht wurde.
Ich würde vorschlagen, dass du auch diesen count_i_soll-Wert berechnest, und dann deine while()-Loop so umbaust, dass sie erst dann abbricht, wenn beide Soll-Werte erreicht wurden.
Ich selber nutze einen etwas anderen Ansatz.
So wie du, berechne ich auch das Verhältnis zwischen Innen und Außen.
Allerdings nutze ich die vorher ermittelten Tik’s als Verhältnis. Um sowohl Rechtskurve, als auch Linkskurve zu berücksichtigen, dividiere ich in meine quote-Variable so, dass immer ein Wert > 1 berechnet wird. (Also immer Außen-Soll-Tick's / Innen-Soll-Tik's)
Dann aber nutze ich beide gezählten Tik’s aus der Variablen encoder[] und berechne aus dem inneren encoder[]-Wert mit dem quot multipliziert einen 'getürkten' Wert. Da mein quote ja auch immer größer 1 ist, wird es als eine größere Tik-Anzahl vom Innenweg berechnet.
Dieser getürkte Wert stellt nun den Weg dar, den die Innenseite gefahren wäre, wenn sie genau so weit wie die Außenseite gefahren wäre.
Also müssen beide Werte, Außen-Soll-Tik und Innen-Türk-Tik, einfach nur gleich viele Tiks haben.
Das erledigt dann die bekannte GoTurn-Funktionalität beim Go-Teil.
Viel Erfolg
Gruß Sternthaler
P.S.: Ich habe dein Programm nicht getestet, da ich sicher bin, dass eben die Berücksichtigung/Kontrolle der Innen-Seite fehlt.
-
hallo sternthaler und aller anderen
ich glaube einen fehler habe ich schon gefunden:
und zwar habe ich im main() vorm aufruf von kurve() vergessen ein EncoderInit() zu schreiben. das konntet ihr allerdings nicht wissen. jetzt bleibt er wenigstens stehen, wenn auch nicht mit dem gewünschten radius und winkel.
@ sternthaler:
sry, aber ich kann mir gerade nicht vorstellen, wie ich die abbruchbedingung umschreibe soll, damit sie deinem rat entspricht .
denn wenn ich nur abbreche wenn beide couter die richtige anzahl haben, also so:
Code:
while(count_a!=count_a_soll && (count_i*quot)!=count_a_soll))
{
//Programm siehe oben
}
werd er nie stehen bleiben.
oder wenn ich es so schreibe:
Code:
while(count_i*quot<count_a_soll)
bleibt er nur stehen wenn count_i den wert erreicht hat, also wieder das selbe wie mein erster versuch
das ist aber nicht das einzige problem! denn der radius stimmt auch nicht
hast du dazu vl. auch noch eine idee was es sein kann?
ich habe mir schon gedacht, dass man am anfang die startgeschwindigkeit vl. nich einfach so ausrechnen kann, der er doch erst ab etwa einem wert von 60 losfährt.
das komisch ist nur, dass er sich auf einen radius regelt, der überhaupt nicht stimmt. auch wenn ich ihn einen größeren winkel fahren lassen
ich weiß derzeit wirklich nicht mehr weiter, und bin daher dankbar für jeden tipp
-
Hallo hai1991,
ich nutze eine Version zum Abbrechen, ähnlich der, die du als erstes aufgeführt hast.
Aber nicht mit dem !=, sondern auch mit einem <, wie in deinem ersten Programm. (Das != ist ja schnell vorbei, wenn das Rad noch etwas weiterdreht. Und schon arbeitet die Loop wieder weiter.)
Ich würde folgendes machen:
Code:
while (count_a < count_a_soll || count_i < count_i_soll)
{
// Innenleben
}
Ich habe nochmal meine Antwort und dein erstes Programm verglichen.
Du machst eigentlich auch 'meinen' Ansatz, dass du das Verhältnis der Tik's nutzt. Mir ist nicht klar gewesen, dass beide Verhältnisse (ich Tiks; du Radius) identisch sind. (Also kannst du auch sagen, dass ich deinen Ansatz benutze ;-))
OK, somit muss das Problem doch eher in der Loop zu suchen sein.
Dein Hinweis, dass du in deinem main() vergessen hattest, EncoderInit() aufzurufen, führt mich zu folgender Überlegung:
Code:
while(count_a<count_a_soll) // Egal, wie abgebrochen wird
{
// Interruptsystem ist aktiv.
// In encoder[] werden einzelne Tiks gesammelt.
// Nun werden die aktuell ermittelten Tiks gelesen und lokal addiert.
// ---> encoder[] kann nun z.B. [0, 1] enthalten, da RECHTS ja
// schneller gefahren wird.
count_a+=encoder[RIGHT];
count_i+=encoder[LEFT];
// count_a ist nun um eins vergrößert,
// count_i hat sich nicht geaendert.
// Das Interruptsystem erzeugt nun genau an dieser Stelle eine
// Aenderung in encoder [LEFT] auf 1.
// Dieser Tik ist NICHT in count_i abgelegt worden.
if(count_a<(count_i*quot)) speed_a+=5; //zu großer radius
if(count_a>(count_i*quot)) speed_a-=5; //zu kleiner radius
// Nun werden die encoder[]-Inhalte beide auf 0 gesetzt.
//reset encoder
EncoderSet(0,0);
MotorSpeed(speed_i,speed_a);
Msleep(1);
// Hier nun der 2.te Durchlauf der Loop
// Das Interruptsystem ist natürlich weiterhin aktiv.
// In encoder[] werden wieder einzelne Tiks gesammelt.
// Nun wieder die ermittelten Tiks lesen und lokal addieren.
// ---> encoder[] kann nun schon wieder [0, 1] enthalten
// da ja das rechte Rad immer noch schneller ist.
count_a+=encoder[RIGHT];
count_i+=encoder[LEFT];
// Jetzt ist count_a nun schon um zwei vergrößert,
// und in count_i hat sich immer noch nichts geaendert.
// Auch jetzt liefert das Interruptsystem wieder an dieser Stelle eine
// Aenderung in encoder [LEFT] auf 1.
// Auch dieser Tik ist NICHT in count_i abgelegt worden.
// Durch diese if's reduziert du nun permanent speed_a, da ja in count_a
// die Tikzahl waechst, aber count_i 'immer' bei 0 bleibt.
if(count_a<(count_i*quot)) speed_a+=5; //zu großer radius
if(count_a>(count_i*quot)) speed_a-=5; //zu kleiner radius
// Und schon wieder den einen Tik "verlieren".
//reset encoder
EncoderSet(0,0);
MotorSpeed(speed_i,speed_a);
Msleep(1);
// usw.
}
Folgende Änderung würde ich mal probieren:
Code:
//reset encoder VOR DIE SCHLEIFE
EncoderSet(0,0);
while(count_a<count_a_soll) // Egal, wie abgebrochen wird
{
// NICHT addieren, SONDERN zuweisen
count_a = encoder[RIGHT];
count_i = encoder[LEFT];
if(count_a<(count_i*quot)) speed_a+=5; //zu großer radius
if(count_a>(count_i*quot)) speed_a-=5; //zu kleiner radius
MotorSpeed(speed_i,speed_a);
Msleep(1);
// WEG MIT DEM "reset encoder"
MotorSpeed(speed_i,speed_a);
Msleep(1);
}
Mit der Konstruktion, solltest du keine Tiks mehr verlieren.
Ob es tatsächlich hilft?
Zumindest kommt es nun meiner Konstruktion ein Stückchen näher, und ich kann Kurven fahren. O:)
Zu deiner Bemerkung über die Startgeschwindigkeit noch ein Wort:
Code:
...
quot=((float)radius_a)/((float)radius_i);
...
//anfangsgeschwindigkeiten berechnen
speed_a=speed;
speed_i=speed/quot;
if (speed_i < MIN_SPEED)
{
speed_i = MIN_SPEED;
speed_a = MIN_SPEED * quot;
if (speed_a > 255)
speed_a = 255;
}
...
Ich halte den Ansatz von dir für sinnvoll, auch den Speed anhand des quot vorzubelegen.
Das Anfahren aber mit MIN_SPEED sicherstellen. Der Rest sollte von deinem 'Regler' erledigt werden. Wobei bei dir aber noch keine Grenzbetrachtungen vorhanden sind.
Frohes testen wünscht
Sternthaler
-
hallo sternthaler
danke für deine ausführliche antwort!
ich werde mich sofort daran machen alles in meine funktion ein zu bauen und zu testen.
ehrlich gesagt, auf die idee mit den verlorenen ticks wäre ich selber nie gekommen. mal schauen obs daran lag und melde mich wieder so bald ich etwas weiß
edit:
ich habe bei mir noch einen fehler gefunden:
bei der berücksichtigung des teilers habe ich *teiler gerechnet, anstatt zu dividieren.
nun sieht meine funktion so aus:
Code:
#define BREITE 103 //spurbreite in mm
#define PI 3.141592
void kurve(int radius, int winkel, int speed)
{
unsigned long count_a_soll,count_i_soll;
int count_a=0, count_i=0;
int speed_a, speed_i;
int radius_a, radius_i;
float quot, teiler;
MotorSpeed(0,0);
MotorDir(FWD,FWD);
radius_a=radius+BREITE/2;
radius_i=radius-BREITE/2;
teiler=(float)360/winkel; //Bruchteil des Kreises
quot=((float)radius_a)/((float)radius_i);
//berechnen der notwendigen tics am außen- und innenrad
count_a_soll=2*radius_a*(PI*10000L/teiler);
count_a_soll/=MY_GO_ENC_COUNT_VALUE;
count_i_soll=count_a_soll/quot;
//anfangsgeschwindigkeiten berechnen
speed_a=speed;
speed_i=speed/quot;
//speed_i=speed;
//Motoren starten
MotorSpeed(speed_i,speed_a);
while(count_a<count_a_soll || count_i<count_i_soll)
{
count_a=encoder[RIGHT];
count_i=encoder[LEFT];
if(count_a<(count_i*quot)) speed_a+=10; //zu großer radius
if(count_a>(count_i*quot)) speed_a-=10; //zu kleiner radius
//reset encoder
//EncoderSet(0,0);
MotorSpeed(speed_i,speed_a);
Msleep(1);
}
MotorDir(BREAK,BREAK);
Msleep(200);
}
jetzt bleibt er zwar stehen, aber erst nach dem doppelten winkel, aber dem halben radius
ich könnte es zwar vor der berechnung der tiks korrigiern, aber trotzdem wüsste ich zu gerne woher das kommt
-
Moin hai1991,
nur ganz kurz diesmal.
- Peinlich: In deinem ersten Beitrag ist mir nicht aufgefallen, dass teiler zwar berechnet, aber nicht benutzt wird.
- EncoderSet() finde ich nicht vor der Schleife in deinem letzten Beitrag.
- Doppelter Winkel heißt mehr Weg; halber Radius heißt weniger Weg.
Gleicht sich das mathematisch in Summe aus, so dass zumindest die Länge des Wegs in Tik’s bzw. in 'Geometrie' passt?
Wenn ja, dann würde ich auf ein Problem mit deinem Wert für MY_GO_ENC_COUNT_VALUE tippen. -> Anderer Wert soll dein Problem dann ausgleichen. (Ist im Moment meine Hoffnung. Hängt vom Ergebnis der Tik/Geometrie-Frage ab. Manchmal sollte ich ein Bild malen um zu sagen was ich sagen will ;-))
Kennst du ASURO ermittelt Werte für Lib V2.70 myasuro.h selber? Da wird auch MY_GO_ENC_COUNT_VALUE vom Asuro selber bestimmt. (Mit freundlicher Unterstützung von dir.)
Dann ist da natürlich jetzt auch die Frage nach der Anzahl der Schwarz-/Weißflächen auf deinen ODO-Scheiben. Die aktuell in der Asuro-LIB angegebene Zahl von 19363 für MY_GO_ENC_COUNT_VALUE ist für ODO-Scheiben mit jeweils 8 mal Schwarz- und Weiß-Flächen.
Wenn du die 4+4-Scheibe drauf hast, könnte das ja schon diesen Fehlerfaktor 2 ausmachen. Die Hoffnung stirbt zuletzt.
Schönes Wochenende, und viele Goldmetall-ien für die Sieger.
Gruß Sternthaler
-
hallo sternthaler und natülich auch alle anderen, die sich dafür interessieren oder mir helfen wollen
die werte in MY_GO_ENC_COUNT_VALUE habe ich schon an meinen Asuro angepasst (ich habe extra noch mit Go() geteste, alles so wie es sein sollte)
trotzdem zur info: ich habe scheiben mit 6 schwarz- weiß-flächen montiert
bei mir hat zwar noch nicht alles gepasst, trotzdem habe ich die geschwindigkeitsanpassung ähnlich der von GoTurn umgeändert, da ich mir dachte, dass es vl an der geschwindigkeitsbegrenzung bei 255 liegen kann. leider hat es auch nichts geholfen.
zusätzlich habe ich noch die zweite richtung ein zu bauen versucht ( positives vorzeichen beim winkel sollte eine kurve im gegenuhrzeigersinn sein)
wenn ich jetzt meine fkt wie folgt aufrufe
passten zwar radius und winkel nicht, aber die richtung.
wenn ich jetzt aber schreibe
, also eine geschwindigkeitsänderung, dann fährt mein asuro auf einmal in die falsche richtung. ich habe aber keine ahnung woher das kommen könnte.
hier einmal meine funktion:
Code:
void kurve(int radius, int winkel, int speed)
{
unsigned long count_a_soll,count_i_soll;
int count_a=0, count_i=0;
int speed_a, speed_i;
int radius_a, radius_i;
float quot, teiler;
MotorSpeed(0,0);
MotorDir(FWD,FWD);
radius_a=radius+BREITE/2;
radius_i=radius-BREITE/2;
teiler=(float)360/abs(winkel); //Bruchteil des Kreises
quot=((float)radius_a)/((float)radius_i);
//berechnen der notwendigen tics am außen- und innenrad
count_a_soll=2*radius_a*(PI*10000L/teiler);
count_a_soll/=MY_GO_ENC_COUNT_VALUE;
count_i_soll=count_a_soll/quot;
//mindestgeschwindigkeit=70
if(speed<70) speed=70;
//anfangsgeschwindigkeiten berechnen
speed_a=speed;
speed_i=speed_a/quot;
//speed_i=speed;
//Motoren starten
if(winkel>0)
MotorSpeed(speed_i,speed_a);
else
MotorSpeed(speed_a,speed_i);
EncoderSet(0,0);
while (count_a<count_a_soll)
{
//Odometrie einlesen
if(winkel>0)
{
count_a=encoder[RIGHT];
count_i=encoder[LEFT];
}
else
{
count_a=encoder[LEFT];
count_i=encoder[RIGHT];
}
//Geschwindigkeiten anpassen
if (count_a <(count_i*quot))
{ /*außen zu langsam */
if ((speed_a > speed) || (speed_a > 244))
speed_i -= 10;
else
speed_a += 10;
}
if (count_a >(count_i*quot))
{ /* außen zu schnell */
if ((speed_a > speed))
speed_a -= 10;
else
speed_i += 10;
}
/* reset encoder */
//EncoderSet (0, 0);
if(winkel>0)
MotorSpeed(speed_i,speed_a);
else
MotorSpeed(speed_a,speed_i);
//Msleep (1);
}
ich bedanke mich jetzt schon für jeden tipp
-
Hallo hai1991,
kannst du bitte deinen Wert für MY_GO_ENC_COUNT_VALUE angeben.
Ich bin dabei deine beiden Parameter-Aufrufe mit dem Taschenrechner nachzurechnen.
Gestolpert war ich bei
"count_a_soll = 2 * radius_a * (PI * 10000L / teiler);"
dass dort der Wertebereich mit int zu klein wäre. Aber da hast du ja schon aufgepasst und ein "unsigned long" vergeben.
Bis nachher
Gruß Sternthaler
[EDIT]
Mit einem angenommenen Wert von 15000 kann ich aber so auch nichts falsches feststellen.
Eine Stelle "while (count_a < count_a_soll)" ist natürlich noch ein Vergleich zwischen "int" und "unsigned long". Das solltes es aber keinesfalls sein, da davon ja nicht die Speed-Berechnung abhängt.
Es wird wohl Zeit, dass ich dein Programm mal bei mir laufe lassen.
Kannst du den aktuellen Stand bitte mal posten. Mit Angabe, welche LIB-Version du nutzt.
-
hallo sternthaler
ich hatte jetzt nie wirklich zeit um dir zu antworten!
hier einmal mein code:
Code:
void kurve(int radius, int winkel, int speed)
{
unsigned long count_a_soll,count_i_soll;
int count_a=0, count_i=0;
int speed_a, speed_i;
int radius_a, radius_i;
float quot, teiler;
MotorSpeed(0,0);
MotorDir(FWD,FWD);
radius_a=radius+BREITE/2;
radius_i=radius-BREITE/2;
teiler=(float)360/abs(winkel); //Bruchteil des Kreises
quot=((float)radius_a)/((float)radius_i);
//berechnen der notwendigen tics am außen- und innenrad
count_a_soll=2*radius_a*(PI*10000L/teiler);
count_a_soll/=MY_GO_ENC_COUNT_VALUE;
count_i_soll=count_a_soll/quot;
//mindestgeschwindigkeit=70
if(speed<70) speed=70;
//anfangsgeschwindigkeiten berechnen
speed_a=speed;
speed_i=speed_a/quot;
//speed_i=speed;
//Motoren starten
if(winkel>0)
MotorSpeed(speed_i,speed_a);
else
MotorSpeed(speed_a,speed_i);
EncoderSet(0,0);
while (count_a<count_a_soll)
{
//Odometrie einlesen
if(winkel>0)
{
count_a=encoder[RIGHT];
count_i=encoder[LEFT];
}
else
{
count_a=encoder[LEFT];
count_i=encoder[RIGHT];
}
//Geschwindigkeiten anpassen
if (count_a <(count_i*quot))
{ /*außen zu langsam */
if ((speed_a > speed) || (speed_a > 244))
speed_i -= 10;
else
speed_a += 10;
}
if (count_a >(count_i*quot))
{ /* außen zu schnell */
if ((speed_a > speed))
speed_a -= 10;
else
speed_i += 10;
}
/* reset encoder */
//EncoderSet (0, 0);
if(winkel>0)
MotorSpeed(speed_i,speed_a);
else
MotorSpeed(speed_a,speed_i);
//Msleep (1);
}
MotorDir(BREAK,BREAK);
Msleep(200);
}
kleine Änderung am 16.8.2008, 9:36
diese infos hatte ich vorher versehentlich zu meinem code hinzu gefügt:
ich verwende die lib 2.71
mein wert von MY_GO_ENC_COUNT_VALUE beträgt 21240L
und zum prog. verwende ich "eierlegende Wollmilchsau" (Asuroflash von Osser), falls das auch noch wichtig ist
ich hoffe du kannst damit was anfange
-
hallo asuroianer
kann das problem vl darin liegen, dass ich radius und winkel als int definiert habe, aber werte hineinschreibe die größer als 255 sind.
könnte es was helfen, wenn ich radis als unsigned int nehme und bei winkel ein long ( das ist doch der "größere int", oder teusche ich mich da gerade???)