archimedische Spirale fahren?

gimate

Member
Hallo, ich bin dabei in meinem Ardumower eine Spiralfahrt zu implementieren. Die Spirale sollte "archimedisch" werden (Bahnen mit gleichem Abstand), genau das bekomme ich mit meiner eigenen Steuerung leider nicht hin. Da diese Funktion bereits im anderen Zusammenhang erwähnt wurde, würde ich mich freuen wenn ich den Code von erfolgreich implementierten Spiralfahrten sehen dürfte.
 
Code:
temp=odotemp/15;
		temp_speed=(abs(log(temp)*2200));
		speedwert=(unsigned short)temp_speed/100;
		if (speedwert>95) speedwert=speedwert-85;
		else speedwert=0;


odotemp: 424 Impulse/Radumdrehung
speedwert: normiert auf 100%, also Geschwindigkeit in %
letzte Zeile: kommt aus der Berechnung raus das einfach 85 abgezogen wird, die erste Umdrehung steht das innere Rad
Excel: ((GANZZAHL(LN(B2)*2200)))/100-85
Meine Berechnungen sind immer in Ganzzahlen da der Atmega diese vielfach schneller als float berechnen kann. Wird dadurch etwas unleserlicher da man zuerst mit 10, 100, oder 1000 multiplizieren muss damit am Ende was sinnvolles rauskommt.

Einfach mal im Excel als Punktdiagramm darstellen.
Finale Werte sind von der Geometrie (Radstand links/rechts (340mm), Raddurchmesser (175mm), usw) abhängig.
Ich hatte früher eine Tabelle ab welchem Weg welche Geschwindigkeit anliegen soll (durch grobe Berechnung und viel probieren ermittelt) und daraus die Formel abgeleitet.
 
Hallo Werner,

danke für die Info. Aktuell habe ich nicht verstanden, wie du die Geschwindigkeit der einzelnen Räder berechnest. Ich sehe nur einen speedwert, kann daraus aber nicht erkennen, welche Geschwindigkeit das innerer und welche das äußere Rad hat. Oder mache ich da eine Denkfehler?
 
Der Speedwert ist in %, 100 = 100% = Höchstgeschwindigkeit.
Äussere Rad dreht mit 100%, nur das innere wird nach dieser Berechnung geregelt.

Funktioniert bis ca 3.5m Spiralen-Durchmesser, ab dann werden die Bahndurchmesser zu groß. Die Berechnung ist halt eine vereinfachte Näherung einer archimedischen Spirale die nur einen bestimmten Bereich abdecken kann, aber in diesem möglichst effektiv ist.
 
Interessant ist deine log Funktion. Muss ich mir mal anschauen was diese bewirkt bzw. wofür diese notwendig ist - habe ich noch nicht simuliert.

Ich wäre nun mit dem aktuellen Wissen folgendermaßen vorgegangen:

Die Polargleichung der Archimedischen Spirale ist: r(t) = at

Wenn man das äußere Rad nun Konstant drehen lässt wie du, und den zurückgelegten Weg l diese Rades von Beginn an misst,
so könnte man die Geschwindigkeit des inneren Rades ggf. folgendermaßen berechnen:

speed_innen = a * l

da das äußere Rad eine konstante Geschwindigkeit hat, müsste das auch über die Zeit gehen, wenn keine Odometrie zur Verfügung steht.:
speed_innen = a * t

a könnte dann empirisch ermittelt werden.

Nachtrag: Habe mir das mal in Excel angeschaut. Scheinbar muss sich am Anfang die Geschwindigkeit schneller ändern, wegen kleinem Radius im Gegensatz zu einem großem Radius. Daher die log Funktion.
 
Ich bin gerade noch auf folgende Seite gestoßen.
http://forums.parallax.com/discussion/154274/the-artist-robot/p5
Dort steht die Formel für die Geschwindigkeit von Rad b wenn Geschwindigkeit von Rad a vorgegeben ist: b=a*(2R-d)/2R+d)

drive_speed_circle.jpg


Weiterhin wird die Zeit für eine Kreisfahrt berechnet:

artist_drive_speed3.jpg


Weiter unten steht dann:
In order to make an Archimedean Spiral, you must draw sequential semicircles and gradually you must increase the radius in a constant amount.
So the 1st semicircle has radius r=5, the 2nd r=10, the 3rd r=15, the 4rth r=20, the 5th r=25, the 6th r=30 and son on ….
So we must calculate the appropriate “a”, “b” parameters for the Drive_speed(a,b) commands, according the formula from post#128. We must also calculate the time for the “pause” command in order to achieve a semicircle, taking into account that every semicircle is an arc with 180 degrees angle. So using the formula from post#129. we have :

spiral1_explain80.jpg


Dann viel Erfolg beim Umsetzten :)
Schön ist hier am Wert b zu sehen, dass dieser am Anfang nicht linear zunimmt, was auch die log Funktion erklärt.
Attachment: https://forum.ardumower.de/data/media/kunena/attachments/2936/drive_speed_circle.jpg/
 
Zuletzt bearbeitet von einem Moderator:
Im Folgenden mein Code, den ich kurz zusammengeschustert habe auf Basis der obigen Formeln. Fährt eine Archimedische Spirale.


Code:
class TCruiseSpiral: public Node    // Each task will be a class (derived from Node of course).
{
private:


    int state;

    double speedOuter;
    double speedInner;

    double waitMsFor180Degree;
    double r;

    double d;
    double addR;

    double k;

    unsigned long startTime;


public:

    TCruiseSpiral ()  {
        d = 35.5;
        addR = 17.25;
        speedOuter = 50;
        k = 0.04566;  //Ausmessen Zeit für 1cm bei drivespeed 50
    }

    virtual void onInitialize(Blackboard& bb) {
        r = addR;
        calcSpeed(r);
        calcTime180(r);
        startTime = millis();
        state = 0;

        bb.motor.L->setSpeed(speedOuter);
        bb.motor.R->setSpeed(speedInner);

    }

    void calcSpeed( double _R) {
        speedInner= speedOuter * (2*_R -d) / (2*_R+d);
    }

    void calcTime180( double _R) {
        waitMsFor180Degree = (3.14 * (2*_R + d) *k ) /2 *1000;
    }

    virtual NodeStatus onUpdate(Blackboard& bb) {

        if (state == 0) {
            unsigned long delta = millis()-startTime;
            if (delta > (unsigned long)waitMsFor180Degree) {
                r+=addR;
                calcSpeed(r);
                calcTime180(r);
                startTime = millis();
                state = 0;
                bb.motor.L->setSpeed(speedOuter);
                bb.motor.R->setSpeed(speedInner);
            }
        }

        return BH_RUNNING;
    }
};
 
Habe damals auch über den Umweg Spirale mit 180° Abschnitten, dann in 36° Abschnitte unterteilt, angefangen. Die Formel hat dann Excel mit Basis der vorigen Werte als Trendlinie erstellt.
Hatte aber immer die Odometrie dahinter.
 
Aktuell ist für mich noch unklar wie du Anfängst. Du sagst:
odotemp: 424 Impulse/Radumdrehung

Sind das die gezählten Ticks des Encoder ab Start? Und wie fängst du mit der Geschwindigkeit an? log(0) ungültig.

Hast du da mal einen etwas größern Codeausschnitt, der die zusammenhänge zeigt?
 
Es gibt 424 Impulse pro Radumdrehung, bei anderen Auflösungen muss man vielleicht die Formel ändern.
Es sind die Ticks ab Start der Spirale.
Der Roboter fängt im fahren mit der Spirale an, dh 1 Rad erst mal stoppen (über speedwert=0).
Aber eine gewissen Anzahl Ticks wird die Spirale als beendet angesehen.

Mehr gibt es nicht zur Regelung, der Rest sind Bumper, Messerdrehzahl, usw
 
Oh man, jetzt habe ich schon wieder Blut geleckt das Umzusetzen, anstatt andere Sachen zu machen.:lol:

Den Code habe ich nochmal abgeändert. Die Berechnung der Zeit ist bei mir nicht notwendig, da ich ja den Weg des Äußeren Rades messen und mit PI * (2*R+d) berechnen kann. Um eine glatte Bewegung zu erhalten, berechen ich nun 8x pro Runde die Geschwindigkeit und die Soll strecke des äußeren Rades - also für 45 Grad.
Der einzige Nachteil dieses Algorithmus ist, dass er diskret ist und nicht kontinuierlich wie Werner seine Formel. Wenn man sich den Code aber anschaut, sieht man, dass nichts getuned werden muss. Das Einzige was gemessen werden muss, ist der Abstand der beiden Räder. Alles andere ergibt sich aus den vom Anwender vorgegebenen Werten: addRadius, speedOuter, r

Ich habe hier mal ein Video von der aktuellen Umsetzung veröffentlicht, um zu zeigen dass es funktioniert. Die quer geklebten Streifen sind ca. 17 cm auseinander.
https://youtu.be/5u9ptWoxU3Q

Code:
//forums.parallax.com/discussion/154274/the-artist-robot/p5
class TCruiseSpiral: public Node    // Each task will be a class (derived from Node of course).
{
private:

    const double PI;

    double speedOuter;
    double speedInner;

    double waitUntilOuterCmReached;

    double r;
    double addRadius;
    double d;


public:

    TCruiseSpiral (): PI(3.141592653589793238463)  {
        d = 35.5;  //distance between the right and left wheel
        addRadius = 17.75/8.0 // Radius that should be increased in one round divided by 8 because every 45° the speed and cm will calculated new. I chose 35.5/2=17.75 in order the cutter disc should overlaps each round. 
        speedOuter = 60; //Speed of outer wheel - constant
    }

    virtual void onInitialize(Blackboard& bb) {
        r = 35.5; // Start with bigger radius in order not to stall the inner wheel
        calcSpeed(r);
        calcOuterCm(r);
        bb.motor.startDistanceMeasurement();
        bb.motor.L->setSpeed(speedOuter);
        bb.motor.R->setSpeed(speedInner);
    }

    void calcSpeed( double _R) {
        speedInner= speedOuter * (2*_R-d) / (2*_R+d);
    }

    void calcOuterCm( double _R) {
        waitUntilOuterCmReached = (PI * (2*_R+d))/8;
    }

    virtual NodeStatus onUpdate(Blackboard& bb) {
        // get the driven distance: bb.motor.getDistanceLInCM() and check if it has driven the calculated way: waitUntilOuterCmReached
        if (bb.motor.getDistanceLInCM() > waitUntilOuterCmReached) {
            bb.motor.startDistanceMeasurement();
            r+=addRadius;
            calcSpeed(r);
            calcOuterCm(r);
            bb.motor.L->setSpeed(speedOuter);
            bb.motor.R->setSpeed(speedInner);
        }
        return BH_RUNNING;
    }

    virtual void onTerminate(NodeStatus status, Blackboard& bb) {

    }

};
 
Hallo Roland,
sehr cool deine Lösung.
Diese Spiralfahrt/Mähmodus kenne ich von meinem einst noch originalen Tianchen Mähroboter. Die hatten es damals so gelöst, dass wenn er auf hohes Gras trifft eine Spiralfahrt startete. Fand ich immer schon sehr nett und effektiv. Leider fehlt mir dies seit dem Ardumowerumbau. Ich habe vor einiger Zeit (letzten Sommer) mal versucht dies in den Ardumower-Code einzubinden. Bin da aber kläglich gescheitert. Software ist leider nicht ganz so meine Sache. Daher verfolge ich dieses Thema hier bei euch mit voller Vorfreude und hoffe auf baldige Aufnahme in den original Ardumower-Code. Klasse was ihr hier alle so drauf habt.
Was mir bei deiner Lösung auch sehr gut gefällt ist dein abfahren entlang der Schleife. Das sieht sehr smooth aus. Ähnlich wie der Landroid das macht. Bei mir sieht das eher wie ein Ententanz entlang der Schleife aus. Ich denke so wie du es gelöst hast, könnte man sogar ein abprallen an der Schleife im Mähmodus schaffen. Also wenn der Mäher auf die Schleife trifft sollte er diese nicht überfahren, zurücksetzen, eindrehen und dann wieder normal weiter mähen, sondern einfach direkt in die richtige Seite einlenken und weitermachen. dies würde bestimmt den Rasen schonen und die Mähzeit verkürzen. Bin gespannt was da noch alles kommt und ob du evtl. dein Projekt irgendwann mal mit einer Anleitung für Softwarelegastheniker wie mich veröffentlichst. Ähnlich dem Ardumower-Team hier. Oder aber das Ardumowerprojekt nimmt deine Ideen mit in ihren Code auf.
Ich hoffe ihr seit alle weiterhin so kreativ.
Gruß
Stephan
 
Ich hatte es auch empirisch mit einer Excel Tabelle und fitten von Trendlinien probiert, kam dabei statt einer log Funktion auf eine Wurzelfunktion die auch am beginn der Spirale schneller ansteigt und dann langsamer wird:

Die Polargleichung sowie die Gleichungen aus dem anderen Forum haben mich auf die Idee gebracht wie man den Verlangsamungsfaktor des Innenrades kontinuierlich berechnen könnte:

a/b = R2/R1

Das äußere Rad soll sich mit der Maximalgeschwindigkeit drehen, also setze ich a = 1

b = R1 / R2

b = R1 / R1 + d

b = 1/(1+d/R1)

Ich habe 1/(1+1/x) geplottet und die Funktion erscheint sinnvoll, die steigt von 0 steil an und geht mit immer geringerer Steigung gegen 1

funktionen1.png


d ist der Radabstand,

Bei R1 = d ist b = 1/2, das heißt das Innenrad ist halb so schnell, bei R1 = 2d ist das Innenrad dann 2/3 so schnell, bei 3d ist es 3/4 usw.

Mit der Polargleichung R1(t) = k * t sollte man jetzt mit der Konstante k bestimmen wie stark die Bahnen überlappen ...

Werde es die nächsten Tage ausprobieren ob das so hinkommt und ich alleine über b = 1 / (1+d/(k*stateTime)) etwas hinbekomme.
Das Zusammensetzen der Spirale aus Kreissegmenten bleibt die Notlösung wenn es mit einer Formel nicht klappen sollte.
Attachment: https://forum.ardumower.de/data/media/kunena/attachments/3847/funktionen1.png/
 
Zuletzt bearbeitet von einem Moderator:
Das scheint zu funktionieren. Ich habe in Azurit 1.0a6 den verweisten STATE_CIRCLE abgeändert und mit der folgendein Zeile zum fahren einer Spirale gebracht:

motorLeftSpeedRpmSet = motorSpeedMaxRpm*(1.0/(1.0+30000.0/(float)stateTime));

Im checkTimeout springe ich dann aus STATE_FORWARD mit einer Zufallszall als Bedingung in den STATE_CIRCLE.

spirale.jpg


Die 30000 als Zeitkonstante sorgen für etwa 70% Überlappung zwischen den einzelnen Bahnen, wobei die Neigung des Unergrunds die Bahnen stark umlenken. Ich vermute, daß die Regelung über motorSpeedRPM ungenau ist und werde mir ansehen wie ich anstatt über die Zeit über die Odometrie regeln kann.

Bei der Spiralfahrt geht es mir in erster Linie um die Optik: die geraden, zufälligen Bahnen hinterlassen kleine Büschel, die bei den nachfolgenden geraden Bahnen selten vollständig runtergemäht werden. Durch die Spiralfahrt habe ich endlich größere, vollständig abgemähte Kreisflächen die stärker überlappen und dann keine Büschel stehen lassen.



Den Hinweis von Werner statt float lieber ganzzahlig zu rechnen werde ich auch noch berücksichtigen.

Ich habe mir in Github einen Fork des Projekts erstellt und will versuchen meine Änderungen in Azurit einzupflegen. Ist der jetzige Ansatz mit einem neuen STATE (dann wohl als STATE_SPIRAL) sinnvoll oder sollte es lieber ein neuer mowPattern werden?
Attachment: https://forum.ardumower.de/data/media/kunena/attachments/3847/spirale.jpg/
 
Zuletzt bearbeitet von einem Moderator:
Das sieht doch gut aus. Der Untergrund macht viel aus. Der Robbi wird einmal kurz umgelenkt, und die ganze Spirale verschiebt sich. Je größer der Durchmesser, je stärker macht sich das bemerkbar.

Wenn du die Odometrie verwenden möchtest, muss da nicht einfach nur stateTime in gefahreneCM ausgetauscht werden und die Konstante neu berechnet werden? Ich meine beide Werte sind doch linear ansteigend?
 
Ich denke auch, dadurch dass dadurch, daß die gefahrene Strecke linear mit der Zeit ansteigt braucht man nur "stateTime" durch die gemessene Strecke ersetzen und die 30000 Konstante entsprechend anzupassen. So weit ich das aber in Azurit sehe, wird in dem aktuellen State gefahrene Strecke nicht gerechnet. Das würde ich dann als nächste Baustelle angehen.

Ich lese aktuell ein Git Tutorial und will meine Änderungen halbwegs sauber und dokumentiert hochladen. Sobald etwas zum testen da ist, gebe ich hier Bescheid.
 
Eine erste Version ist unter https://github.com/ainagtur/ardumower zu finden.
Die Dokumentation werde ich im Wiki pflegen: http://wiki.ardumower.de/index.php?title=Archimedean_spiral
Die Spiralfahrt ist im STATE_FORWARD integriert und wird nach verstreichen von motorSpiralStartTime (6 Sekunden) angefangen. Die Überlappung der Bahnen kann mit motorSpiralFactor reguliert werden. Diese Variablen sind unter Settings/Motor in pfodApp verfügbar.

Ich habe es allerdings nicht geschafft, daß die neuen Variablen auch gespeichert werden. Was muss ich beachten wenn ich die neuen Variablen in settings.h hinzufügen will?

Ich hatte erstmal nach motorForwTimeMax gesucht und nach der Zeile

Code:
eereadwrite(readflag, addr, motorForwTimeMax);



einfach meine neue Variable dazugeschrieben:


Code:
eereadwrite(readflag, addr, motorForwTimeMax); 
      eereadwrite(readflag, addr, motorSpiralStartTime);


Leider hat es so nicht funktioniert...
 
Hi,
danke für deine Arbeit.
Könnte man das nicht so machen, das wenn der Mowerstrom hoch geht, er also hohes Gras hat, anfängt die Spirale zu mähen und das so lange macht bis die Schleife erreicht ist oder der Bumper ausgelöst wird ?
Brauchst du in deinem Archiv die Leiterplatte und die IDE ? Das ist ja riesig 543 MB ?

Gruss
 
Oben