Edison - Kann, mutt, löppt!

roland

Active member
Ich kann es fast kaum glauben, aber nach nun endlich 1,5 Jahren, hat sich der Kreis geschlossen und ich kann meinen Rasenmähroboter welchen ich nun Edison getauft habe in Betrieb nehmen. Gestern habe ich ca. 230m Kabel verlegt.

Das Projekt begann mit zwei Due und einem selbstgebauten Chassis. Bin dann aber auf das Ardumower Chassis umgestiegen. Als der DUE auf der Arduino Webseite als retired gemarkt wurde, bin ich dann auf STM Nucleo umgestiegen - retired wurde zurückgenommen nachdem die beiden Arduino Fraktionen sich geeinigt habe.

Der Mower hat folgende Eigenschaften:

Chassis und Motoren vom Ardumower
STM32 NUCLEO-F446RE als Perimeter Empfänger
STM32 NUCLEO-F411RE als Steuerplatine
Zwei Sabertooth 2X25 V2 regenerative dual motor driver
Zwei Perimeterempfänger vorne
Ein Perimeterempfänger hinten
Ein Rundumbumper mit Druckwellenschlauch
Ein Ultraschallmodul SRF08
Steuerung über Bluetooth
Encodersignale gefiltert und über SchmittTrigger an die MCU angeschlossen
Perimeter Sender vom Ardumower. Allerdings hat das von mir verwendete Signal 60 Zeichen (Ardumower hat 19)
Ladestation durchfahrend

Software:
NUCLEO-F446RE mit System Workbench for STM32 und STM32Cube HAL umgesetzt. 8-faches Oversampling. Jede Spule hat eigenen AD Wandler und DMA Channel
NUCLEO-F411RE mit mbed programmiert.
Zwei Mähverhalten: Gardena Mode, Workx Mode
AI mit Behaviour Tree umgesetzt
5 Verhaltensmodi: Charging, GoToArea, Perimeter Tracking, FindPerimeter, Mowing
Fahrgeschwindigkeit 1,3km/h.
Fahrgeschwindigkeit wird gesenkt, wenn Leistung des Mähmotors eine Schwelle überschreitet
Fahrgeschwindigkeit wird gesenkt, wenn kurz vor Perimeterüberfahren oder nahe Hinderniss
Erkennung von Dreiecken im Perimeterkabel für die schnelle Rückkehr
One Bounce Algorithmus für Perimetertracking.

Besondere Herausforderung im Garten:
Geoboarder trennt Rasen von Beet. Ziel ist, dass diese Kante gemäht wird, daher benötige ich große Räder vorne.
Eine enge Durchfahrt ca. 1m zwischen Kabel
Eine enge Passage ca. 1,5m zwischen Kabel, die auch gemäht werden soll
Diverse Hindernisse nicht ausgegrenzt
Abstand Hindernis - Perimeter < 1m

Perimeterschleife:
Länge ca. 230m. Keine Inseln.
Der Sender wurde auf 10V eingestellt. Die Perimeterschleife hat einen Widerstand von 4,57 Ohm. Ich musste noch einen 24 Ohm Widerstand in Reihe dazuschalten, ansonsten haben die Verstärker am Kabel übersteuert. Somit habe ich aktuell ca. 0,35A Perimeterstrom.

Das Outdoortesten kann nun endlich beginnen. Leider habe ich die nächsten 3 Wochen kaum Zeit.
Anbei ein paar Bilder und den BHT als PDF.
Wenn ich wieder Zeit finde, werde ich dann noch paar Videos reinstellen. Bis dahin frohes Robbi bauen :woohoo:

EdisonBehaviourtree.pdf


IMG_0400.JPG

IMG_0401.JPG

IMG_0397.JPG

IMG_0399.JPG
 
Toll Roland dass der Edison nun den Praxis-Einsatz bekommt! :) Dein Projekt hat auch mit dazu beigetragen dass der Standard-Ardumower weiter voran kommt und die vielen Details und Vorschläge konkret in die Praxis umgesetzt werden können (Optimierungen für Arduino Due uvm.).
 
Hallo Roland,
Ich finde das sieht sehr gut aus. Es scheint als hättest du keinen Zufallsdrehwinkel nach dem auftreffen beim Perimeter. Er dreht scheinbar immer im selben Winkel. Somit sind dann auch die engen Durchfahrten kein Problem mehr.
Ich freue mich schon auf die nächsten Videos von dir.
Gruß
Stephan
 
Nicht ganz, wenn er das Ende der Gasse entdeckt hat (maximale Fahrstrecke 20cm), drehe ich in festen Winkeln. Ansonsten verwende ich Zufallswinkel.


Code:
/**********************
        * Drehwinkel berechnen
        **********************/
        if(bb.flagForceSmallRotAngle > 0) {
            randAngle = myRandom(30, 60);
            bb.flagForceSmallRotAngle--;
            errorHandler.setInfo("!05,ForceSmallRotAnglern");

        } else if(distanceDrive[0] > 150 ) { //cm 150

            if(bb.flagCoilOutsideAfterOverrun  == CO_BOTH) {
                randAngle = myRandom(80, 115); // Wenn beide Spulen draußen sind, kann Drehwinkel größer sein 80 115
            } else {
                randAngle = myRandom(60, 80); // Wenn eine Spule draußen ist, nicht soweit drehen
            }
            errorHandler.setInfo("!05,FreeArearn");

        } else if(distanceDrive[0] < 50 && distanceDrive[1] < 50) {
            // Im schmalem Korridor nicht so stark drehen
            if(bb.flagCoilOutsideAfterOverrun  == CO_BOTH) {
                randAngle = myRandom(50, 70);   // Welchen Winkel soll robbi drehen wenn zu oft ein kurzer weg gefahrebn wurde. 50/90
            } else {
                randAngle = myRandom(40, 50); // Wenn eine Spule draußen ist, nicht soweit drehen 40/60
            }
            errorHandler.setInfo("!05,Schmaler Korridornr");

        } else { // Korridor

            // Im Korridor nicht so stark drehen
            if(bb.flagCoilOutsideAfterOverrun  == CO_BOTH) {
                randAngle = myRandom(50, 90);   // Welchen Winkel soll robbi drehen wenn zu oft ein kurzer weg gefahrebn wurde. 50/90
            } else {
                randAngle = myRandom(40, 60); // Wenn eine Spule draußen ist, nicht soweit drehen 40/60
            }
            errorHandler.setInfo("!05,Korridornr");
        }

        bb.cruiseSpeed = bb.CRUISE_SPEED_LOW;



        /**********************
        * Oszilliert Roboter an Perimeter?
        **********************/
        // Wenn eine Strecke weniger als 20 cm gefahren wurde, dann wird angenommen, dass Ende Korridor Erreicht ist und es muss gedreht werden
        if(  (distanceDrive[0] <20) && (state == 0) && (bb.flagCoilOutsideAfterOverrun == CO_BOTH) ) {

            if(showValuesOnConsole) {
                errorHandler.setInfo("!05,distanceDrive[0] <20rn");
            }
            //randAngle = 160-lastAngle[0] ;

            randAngle = 90;

            if(rotateDirection[0]== DD_ROTATECC) {
                // Weiter CC drehen wenn vorher CC gedreht wurde
                bb.flagForceRotateDirection = FRD_CC;
                if(showValuesOnConsole) {
                    errorHandler.setInfo("!05,state 0 rotateDirection[0]== DD_ROTATECCnr");
                }
                state = 1;
            } else {
                // Weiter CW drehen wenn vorher CW gedreht wurde
                bb.flagForceRotateDirection = FRD_CW;
                if(showValuesOnConsole) {
                    errorHandler.setInfo("!05,state 0 rotateDirection[0]== DD_ROTATECWnr");
                }
                state = 2;
            }
        } else if(  state == 1) { //CC gedreht
            if(distanceDrive[0] > 40  || bb.flagCoilOutsideAfterOverrun  != CO_BOTH) {
                //Do nothing
                if(showValuesOnConsole) {
                    errorHandler.setInfo("!05,state 1 nothingnr");
                }
                state = 0;
            } else {
                // Weiter CC drehen
                randAngle = 90;
                bb.flagForceRotateDirection = FRD_CC;
                if(showValuesOnConsole) {
                    errorHandler.setInfo("!05,state 1 FRD_CCnr");
                }
                state = 3;
            }


        } else if(  state == 2) { //CW gedreht
            if(distanceDrive[0] > 40  || bb.flagCoilOutsideAfterOverrun  != CO_BOTH) {
                //Do nothing
                if(showValuesOnConsole) {
                    errorHandler.setInfo("!05,state 2 nothingnr");
                }
                state = 0;
            } else {
                // Weiter CW drehen
                randAngle = 90;
                bb.flagForceRotateDirection = FRD_CW;
                if(showValuesOnConsole) {
                    errorHandler.setInfo("!05,state 2 FRD_CWnr");
                }
                state = 4;
            }



        } else if(  state == 3) { //Im State 1 wurde CC gedreht und nun ist er auf die gegenüberliegende Seite gefahren aber Korridor ist eng. Daher nach CW drehen - nicht das er wieder in Korridor fährt
            if(distanceDrive[0] < 30  &&  bb.flagCoilFirstOutside == CO_LEFT) {
                randAngle = 50;
                bb.flagForceRotateDirection = FRD_CW;
                if(showValuesOnConsole) {
                    errorHandler.setInfo("!05,state 3 FRD_CWnr");
                }
            } else {
                //Do nothing
                if(showValuesOnConsole) {
                    errorHandler.setInfo("!05,state 3 nothingnr");
                }
            }
            state = 0;

        } else if(  state == 4) { // //Im State 2 wurde CW gedreht und nun ist er auf die gegenüberliegende Seite gefahren aber Korridor ist eng. Daher nach CC drehen  - nicht das er wieder in Korridor fährt
            if(distanceDrive[0] < 30  &&  bb.flagCoilFirstOutside == CO_RIGHT) {
                randAngle = 50;
                bb.flagForceRotateDirection = FRD_CC;
                if(showValuesOnConsole) {
                    errorHandler.setInfo("!05,state 4 FRD_CCnr");
                }
            } else {
                //Do nothing
                if(showValuesOnConsole) {
                    errorHandler.setInfo("!05,state 4 nothingnr");
                }
            }
            state = 0;
        }
 
Hi Roland,

schön zu sehen wie es bei Dir Vorwärts geht. Mich würde mal Deine Bereifung interessieren. Wie hast Du das gelößt?

VG
Rajiva
 
Hallo Rajiva,

ich habe um jedes Rad einen Cross Mantel vom Kinderfahrrad angebracht. Zum Befestigen habe ich keine Spax Schrauben verwendet. 10 oder 12 mm.

http://www.ardumower.de/index.php/de/forum/antrieb/284-traktion?start=20#10600
 
@Rajiva
Die Reifen waren relative klein. Wie groß genau, weiß ich nicht mehr. Sie waren auf jeden Fall etwas Größer als die Ardumower Räder. Die Reifen habe ich von EDEKA. Habe ich durch Zufall entdeckt. Die Seiten habe ich mit einer Schere soweit wie nötig abgeschnitten.

Der Mäher hat gestern 2,5h und heute 2,5h am Stück gelaufen. Wirklich cool. Die Rückfahrt am Perimeter habe ich gesten getestet. Ca. 200m. Angefangen mit 27,42V Batteriespannung aufgehört mit 27,34V Batteriespannung. Mähmotor war ausgeschaltet.
Heute abgewartet bis die Batterie auf 23,7V war. Dann fährt er automatisch zurück. Ich schätze mal 180m waren es heute. Die Batteriespannung ging auf 23,77V hoch nach Abschalten des Mähmotors. Bei Ankunft in der Dockingstation hatte er 23,8V. Messfehler hin oder her, er hat für die Strecke ca. 12 Minuten benötigt und nur sehr wenig Energie. Echt super.

@Entwicklerteam
Was stört ist, dass das Gehäuse und der Ultraschallsensor nicht Regendicht sind. Beim zweiten Lauf heute hat es angefangen zu Regnen und zu Hageln. Wenn Ihr ein neues Chassis entwickelt, solltet Ihr auf jeden Fall darauf achten, dass man bei jedem Wetter mähen kann. Weiterhin solltet ihr meiner Meinung nach Wasserdichte Ultrasonic Sensoren anbieten. Das ist echt eine Marktlücke so etwas günstig zu bekommen. Ich hätte sowas selber entwickelt, doch ich habe gerade andere Zielsetzungen. Bezüglich Wasserdichter Ultraschallsensoren habe ich bereits mit einem Billigprodukt getestet. Zu viel Gedöns und Kabel. Ich hätte auch 3 Stück benötigt für eine Breite von 60 cm in 70 cm Entfernung. Das macht ein SRF08 ganz locker alleine.

Als nächstes mein Bumper Test als Video. Damit habe ich - dank gutem Wetter abends - alle Test abgeschlossen und muss abwarten wie sich Edison in wilder Prärie verhält.:lol: :woohoo: :lol:

https://youtu.be/ViE6-uYFE3c
 
Es könnte jemand versuchen die Software zu portieren so dass es eine weitere Software-Alternative für die Ardumower-Hardware gibt. Mit dem zukünftigen PCB 1.3 und dem Due haben wir eigentlich genug Power (siehe aktuelle Entwicklung von Ardumower Sunray Firmware), allerdings muss man alles für eine bestimmte Hardware optimieren damit man das Maximum herausholt (DMA-Controller des Due nutzen usw.). Die Hardware-Optimierung könnte man sich bei Ardumower Sunray abschauen... (wir wollen diese immer stärker für den Arduino Due optimieren, z.B. auch hinsichtlich eines optimierten Perimeter-Senders mit kurzen Pausen von 90ms um die Sende-Leistung zu reduzieren)
 
nero76 schrieb:
Es könnte jemand versuchen die Software zu portieren so dass es eine weitere Software-Alternative für die Ardumower-Hardware gibt.
Ist die Version denn Open Source?
 
Zuletzt bearbeitet von einem Moderator:
Es wäre schön wenn Edison diesen Weg gehen würde (ich gehe davon aus), denn Closed-Source Lösungen gibt es schon genug am Markt (für "Closed" braucht man sich nicht die Mühe machen einen Roboter zu bauen, da kann man stattdessen auch was Fertiges um die Ecke im Baumarkt kaufen).
 
Ich habe den Code eingentlich so verfasst, das ich ihn einfach auf die Version V1.3 umsetzten kann, unter der Annahme folgender Voraussetungen, da ich angenommen habe, dass ich der einzige bin, der den Code verwenden möchte:

Pro Motor können beide Encoderkanäle A und B eingelesen werden (in v1.3 nicht vorgesehen)
Das Perimetersignal wird von einer externen CPU ausgewertet und per serieller Schnittstelle an die Steuerungs CPU geschickt.
Es werden zwei Spulen vorne und eine hinten verwendet. (Die hintere Spule habe ich ursprünglich eingebaut, da theorietisch die Möglichkeit besteht, dass
der Robbi rückwärts aus der Schleife fährt. Nun verwende ich diese aber auch zum Perimeter Tracking.) Die hintere wegzulassen bedeutet, die linetracking Algorithmen neu- bzw. umzuschreiben.
Sicherheitsrelevante Sachen wie Motor Stall und Überstrom müssen noch abgefangen werden. Das machen aktuell meine beide Sabertooth Treiber.

Mit Lizenzen habe ich mich noch nie beschäftigt. Daher würde ich den Code unter der gleichen Lizenz die der Ardumower verwendet zu Verfügung stellen.

Noch etwas zum aktuellen Mähalgorithmus;
Mein Mähalgorithmus ist nun ein Husquvarna/Workx Mix. Die flaumige Rasenoberfläche scheint nur dann zu kommen, wenn man tatsächlich von allen Seiten auf die Stelle fährt. Daher der Husquvarna Algorithmus. Wenn es dann zu Engstellen/Sackgassen kommt, gehe ich mehr in Richtung Workx. Edison fährt aktuell 2,5h pro Lauf. Ich schätze, er muss 3x pro Tag laufen, um 900qm vernünftig abzudecken - gerade wenn ich gedüngt habe und der Rasen schnell wächst. Aus Husquvarna Bedienanleitung: "Die maximale Leistung, 1.500 m2 beim Automower® 315 und 1.000 m2 beim Automower® 310, wird nur dann erreicht, wenn der Mähroboter an sieben Tagen die Woche rund um die Uhr mäht. " Die haben fast die gleiche Schnittbreite wie der Ardumowerteller.
 
Wäre eine Möglichkeit. Habe ich mich aber noch nicht mit beschäftigt. Weiterhin weiß ich nicht wie die Rechtevergabe dort läuft.

Vielleicht wäre es grundsätzlich nicht schlecht, wenn man Optionen hat seinen Code unter z.B. andere Projekte zu posten. Dann kann jeder, der seinen Code veröffentlichen möchte, diesen reinstellen. Letztendlich programmierst du auch was und al_ohr3 hat ja auch Teilcodes für seine Motorsteuerung veröffentlicht. Da kann man dann mal reingucken, wie andere sowas machen. Im Forum ist sowas immer schlecht zu finden.

Das könnte man dann auch auf 3D Teile erweitern. Wenn man etwas drucken möchte kann man sich die Dateien aus dem Gtihub holen.

Aber wie gesagt, ich kenne die Rechteverteilung nicht und letztendlich müssen das die Administratoren entscheiden.
 
So weit ich Github verstanden habe, machst du mit "Fork" eine eigene Kopie in der man selber der admin ist und jeder andere die Änderungen sehen kann. Wenn man dann Änderungen hat die man ins ursprüngliche Repository zurückschreiben will, dann meldet man es als "pull request" an und die Admins vom Hauptrepository genehmigen es.

Sollte ich es falsch verstanden haben, bitte ich um einen Hinweis. Ich hatte schon vor den spiralfahrt Mod auf diesem Weg ins Ardumower Repository einzupflegen sobald es ausreichend getestet wurde.
 
Oben