Archiv verlassen und diese Seite im Standarddesign anzeigen : Projekt: FreeRTos auf RP6
Hallo,
ich möchte FreeRTos auf der Base sowie auf der M32 meines RP6v1 laufen lassen und wüsste gern ob da breiteres Interesse oder gar Mitarbeit an dem Projekt mit AVR Studio 6 besteht. Als Projektziel stelle ich mir auf der Base eine Funktionalität ähnlich wie bei RP6Base_I2CSlave vor, auf der M32 das entsprechende Gegenstück. Allerdings eben nicht interruptgesteuert wie in der RP6Lib sondern mit einem erweiterbaren FreeRTos Multitasking Kernel Unterbau (aktuell 7.1.1). Ich möchte dabei möglichst funktionskompatibel zur RP6lib bleiben damit z.B. einfache Demoprogramme ohne viel umschreiben als Task lauffähig bleiben. Da die RP6lib bereits alle verfügbaren Timer benutzt, gibt es keinen "einfachen Weg" das OS und die RP6Lib zusammen zu bringen. Daher müsste für das FreeRTos einiges angepasst und/oder neu geschrieben werden - wo jedoch auch viele Verbesserungen einfließen können.
Mich störte schon immer, das z.b. Delays die Laufzeiteigenschaften des jeweiligen Programms beeinflussen und meiner Ansicht nach hat eine CPU eben besseres zu tun als in Delayloops fest zu stecken. Auch das daher bestimmte Funktionen blocken obwohl dies nicht immer notwendig wäre, störte mich zunehmend.
Nach umfangreicher Recherche habe ich mich dann zu FreeRTos http://www.freertos.org/RTOS.html entschieden. Auch weil es da schon fertige IP Stacks u.ä. gibt.
Man mag einwenden das den langsamen Mega32 der Base betreffend da mit Kanonen auf Spatzen geschossen wird aber in Verbindung mit anderen Zusatzboards wird das System schnell komplexer.
Ich habe mir das Demoprogramm von FreeRTos für AVR geschnappt und es auf die Hardware vom RP6 angepasst (Betrifft Hardwareinit, LED Funktionen, IRQ).
Das Ganze enthält derzeit ein Task System mit 3 Co-Routinen die 3 LEDs (sl1-3) in unterschiedlichen Zeiten toggeln.
Weiterhin wird ein Errortesttask ausgeführt, ein Mathetask und ein Registerchecktask.
Von den Tasks sieht man nicht viel, sie laufen aber. Der Ursprünglich vorhandene serial-looptask ist abgeschaltet, da man dort für ein erfolgreichen Test pin Rx/Tx brücken müsste... für ein schnellen Test ist das jedoch nicht angebracht. Die Poweron LED geht an wenn der Scheduler startet, startet er nicht (weil z.B. nicht alle tasks eingerichtet werden können), blinkt die Poweron Lampe (dürfte bei euch so nicht vorklommen). Alle 3 Sekunden toggelt zudem die sl6 als Zeichen dafür, das alle Tasks laufen. Würde ein oder mehrere Tasks sterben, blinkt sie nicht mehr. Die ursprüngliche Funktion des Resetcounters per hochgezählter Zelle im EEPROM aus dem Demo ist ebenfals deaktiv.
Der Bot bewegt sich nicht und macht auch sonst nichts anderes...
Anbei also als HEX für die Base mein erster Versuch FreeRTos auf dem RP6 laufen zu lassen.
http://www.freertos.org/a00098.html
und dort
http://www.freertos.org/a00102.html
findet sich etwas Doku dazu.
Die Quellen würde ich bei Interesse veröffentlichen, allerdings muss ich da erst noch aufräumen.
Mein gcc gibt mir übrigends derzeit folgende Werte für das Demo aus.
Program Memory Usage : 10350 bytes 31,6 % Full
Data Memory Usage : 1759 bytes 85,9 % Full
Der "hohe" RAM Verbrauch liegt an einer Speicherveraltung in FreeRTos, die sich in meinem Fall erst mal 1500 Byte reserviert und diese dann den Tasks z.b. als lokalen Stack und Queue bereit stellt. Der Kernel ansich braucht etwa 5k Rom ohne Tests und Geblinke.
Mehr dazu hier: http://www.freertos.org/FAQMem.html
Nachtrag: Curiosity hat es geschafft! http://mars.jpl.nasa.gov/msl/participate/ ... mit einem PowerPC SBC als "Hirn"
Hallöle
ich hab für mich mal sowas wie ne "Entwicklungsumgebung" für FreeRTos auf dem RP6 aufgesetzt.
In dem Zip befindet sich die RP6Lib für Base und M32 sowie FreeRTos 7.1.1 Port für Mega323 (und Mega32) und 3 Demo Projekte.
Das ganze liegt bei mir unter d:\RP6\freeRTOSRP6\ , falls ihr mal irgendwie mit Pfaden Schwierigkeiten habt.
Example_02_UART_01 aus den RP6-Demos kompiliert als "normals" Programm ohne RTOS.
Example_RTOS_pur ist das Teil was ich oben gepostet habe.
Example_05_Move_01 ist ebenfalls aus den RP6-Demos, dies möchte ich als nächstes unter RTOS umsetzen.
Es bindet RTOS noch nicht ein.
Um das jedoch zu vereinfachen, habe ich quasi eine leere "Schatten-Lib" aufgebaut, die von den RP6 Libs includiert wird.
Mein Gedanke dazu ist, die einzelenen Funktionen in der RP6 Lib Stück für Stück aus der alten Lib zu entfernen
und in die neuen Libs zu konvertieren. Also z.b. RP6RobotBaseLib.c -> FreeRTOSRP6RobotBaseLib.c
Vieles ließe sich übernehmen, vieles muss aber auch einfach neu gemacht werden.
UART und TWI Treiber z.B. mit Semaphoren und Queues versehen, die ganze Timerstruktur anpassen usw.
Später nach der Migration müsste man dann die leeren RP6 Libs abtrennen können.
Ein "Mischbetrieb" müsste dann zwischendrin auch möglich sein so lange sich ISRs z.B. nicht in die Quere kommen.
Bei RTOS gibts zu bedenken, das dies normal auf Timer 1 sitzt - dort ist aber bei dem RP6 Base normal das PWM für die Motoren.
Folglich habe ich das RTOS so umgebaut das man es in der FreeRTOSConfig.h einstellen kann. Aktuell steht es auf TIMER2 was
aber die ACS blockiert. Zumindest ist aber damit Bewegung möglich, daher auch mein nächster Versuch Example_05_Move_01
So...es gäbe bei der kompletten Umsetzung der RP6lib massig code umzuschreiben, was ich wohl nicht allein schaffen werde.
Zum anfangen und rumprobieren geht das aber schon mal. Ich hab gesehen, das doch recht viele Leute den ersten Beitrag gelesen
haben, aber keiner mal das Hex file ausprobiert hat. Wer mit der M32 rumprobieren möchte, ist recht ungebunden was Timer usw.
angeht, so lange er auf die RP6Control.c verzichten kann. Aber auch die müsste man dann umbauen...
Vielleicht hat ja jemand Lust auch daran zu arbeiten... daher stell ich es mal zur Verfügung
Zur Info, es gibt auf Sourceforge noch ein recht interessantes Projekt. http://sourceforge.net/projects/avrfreertos/
Dort geht es leider um größere Prozessoren, aber die Libraries dürften kompatibel sein.
Zumindest gibts dann da schon lib_spi, lib_i2c, lib_serial, lib_servo usw. usw... was auch als Modell für Treiber auf dem RP6
dienen kann.
Das RTOS wird im Prinzip über den Timer sowie über vTaskStartScheduler() eingebunden (siehe Beispiel Example_RTOS_pur)
Ich habe dort die Hardwareinit so ein bischen nachgebaut und entsprechend dann io Funktionen angepasst (partest.c)
Die blinden ISRs am Ende von main dienen nur dazu, kein "false Interrupt" zu bekommen da diese ggf. den Prozessor resetten.
Längerfristig müsste sowas natürlich durch echte ISRs behandelt werden.
Ja.. mal sehen ob jemand spass dran hat, Feedback ist willkommen.
Gruß Rolf
Hm ok... dann gibts die eben nich im Anhang....
freeRTOSRP6.zip - Die Datei, die du anhängen möchtest, ist zu groß. Die maximale Dateigröße für diesen Dateityp beträgt 488,3 KB. Deine Datei ist 966,1 KB groß.
Ihr könnt mich ja hier unter Angabe eurer Mailaddy bei Interesse anschreiben .. ich schick sie dann per email
Ich will mal wieder ein Statusbericht zum Thema RTOS auf RP6 geben.
Wie im Thread https://www.roboternetz.de/community/threads/58903-Motor-PWM-19KHz beschrieben, läuft das RTOS nun mit 500Hz Tickrate auf Timer0 und alle RP6Lib Funktionen auf Timer1. Da Letzerer auf 10KHz arbeitet, hört man dank Trägheit auch kaum bis kein fiepen. Das Thema PWM Motor Frequenz ist sehr komplex, siehe aber vor allem http://www.mikrocontroller.net/articles/Motoransteuerung_mit_PWM#Wahl_der_PWM-Frequenz .
Da nun aber der Motorenantrieb bzw. dessen "Regelung" Probleme machte, musste ich mir zunächst die task_motionControl() ansehen, verstehen und anpassen. Mir sind da diverse Dinge aufgefallen. Ich habe z.b. inzwischen einen echten dual (L/R) arbeitenden PID Regler nach dem Vorbild aus der ATMEL Docu "AVR221: Discrete PID controller" bzw. http://www.atmel.com/Images/doc2558.pdf mit Ziegler-Nichols Parameter eingebaut und korrigiere grade die Funktionen in Bezug auf Fahrkommandos im Wertebereich -210 bis 210 pro Kette für Fahrt BWD - 0 - FWD, so das man mit 2 Parametern alle Bewegungen und Richtungsänderungen ausführen kann. (Bei mir ist das mit einer #Define und dem Wert 400 statt verstreuter "200/210" geregelt) Kettenfahrzeuge kennen nun mal generisch garkein Links/Rechts... mit 2 Werten ist alles gesagt und auch später leichter zu programmieren. Alle move_() Calls funktionieren daher Sinngemäß zu moveAtSpeedDirection() Calls. Alles Andere, dann unnütze fliegt raus.
Jetzt möchte ich euch mal zeigen, was eine Überarbeitung der RP6Lib bringen kann. Es geht um das Change-direction Verhalten in der task_motionControl().
Dazu nehmen wir an, beide Ketten drehen sich und es kommt ein Changedirektion Ereignis. Abhängig von den #defines CHANGE_DIRECTION_XXXX werden unterschielich komplexe Codestrukturen mit up/down Rampen gefahren. Wählt man hier falsch, kann man sich seine Getriebe in nullkommanix schrotten. Das Ganze muss aber nur erfolgen wenn sich tatsächlich die Drehrichtung einer Kette ändert.
Das ist alles viel zu kompliziert und beim Bremsen ohne GegenEMK (->) braucht man nun wirklich keine "PWM-Rampe". (http://de.wikipedia.org/wiki/Elektromotorische_Kraft)
Im Gegenteil, beim Stoßweise runtersetzen der PWM belaste ich das Getriebe eher mehr als bei einem einzelnen Bremsvorgang gegen 0. Einfaches Auslaufen sollte das Getriebe aber mitmachen, sonst wäre es konstruktiv unterdimensioniert.
Praktisch gesehen muss dabei also folgendes passieren:
1. (Bot fährt) , Motoren spannungslos machen so das Motordrehzahl abhängig von der Trägheit gegen speed 0 laufen kann, (Bot steht) , Portbits für Richtung ändern , Motoren mit Sollspannung versorgen und auf desired_speed bringen. Es macht dabei keinen Sinn, nur eine Kette zu bremsen da sich der Bot dann unkontrolliert und Gewichtsabhängig (Trägheit) drehen würde.
Auch möglich wäre:
2. (Bot fährt) , Motoren spannungslos machen, beide Portbits entgegen aktueller Drehrichtung ändern , Motoren mit PWM im Bereich 1-30/210tel für die GegenEMK setzen so das Motoren stoppen, (Bot steht und läuft noch nicht in Gegenrichtung an da PWM Werte dafür zu klein) , nun beide Portbits letztlich in gewünschte Drehrichtung ändern, bot mit Sollspannung versorgen und auf desired_speed bringen. Dieses belastet jedoch den Motor und vor allem die Ritzel extrem, ich rate dringend davon ab so lange man keine hochstrapazierbaren Zahnräder hat. Dies belastet zudem auch die Batterie zusätzlich, führt aber zu deutlich besserem Bremsverhalten. Man könnte es vielelicht mal mit sehr kleinen PWM Werten für die GegenEMK versuchen da die Last auf die Ritzel von der erzeugten GegenEMK abhängt - damit nicht nur die reine Rollreibung bremst. Bei Beschleunigungen treten ja auch mehr Kräfte auf als beim Gleichlauf. Beim bremsen sollten die Zahnräder zumindest das Gleiche an Drehmoment aushalten können, welchen man übrigends auch hervorragend am RP6 über die elektrische Last auslesen/messen kann/könnte. Bisher wird da nur ein "Overcurrrent" Ereignis ausgewertet.
Bei mir entfällt der ganze Kladeradatsch mit CHANGE_DIRECTION_XXXX und ich führe einfach nur Folgendes aus dem 1.ten Ablauf aus:
if(mleft_des_dir != mleft_dir || mright_des_dir != mright_dir) { //x_des_dir=neue Richtung der Kette, _dir aktuelle Drehrichtung, wenn unterschiedlich -> Handlungsbedarf
PWM_L = mleft_power = 0; // PWM_L = OCR Regs, wir bremsen sofort durch Strom aus, hier wäre auch eine schwache Motorbemse mit z.B. pwm < 1-30 in Gegenrichtung möglich, wenn die Ritzel das denn auch dauerhaft mitmachen würden...
PWM_R = mright_power = 0; // symetrisch bremsen!
while (mleft_speed != 0 || mright_speed != 0) vTaskDelay( (portTickType ) 100 / portTICK_RATE_MS ); //warten bis Bot sicher steht, erledige so lange anderen Kram
if(mleft_des_dir) PORTC |= DIR_L; else PORTC &= ~DIR_L; // ab hier neue Richtung setzen, das erfolgte hier mal per setMotorDir
if(mright_des_dir) PORTC |= DIR_R; else PORTC &= ~DIR_R;
mleft_dir = mleft_des_dir; // neue Richtung ist angelegt
mright_dir = mright_des_dir;
}//hier gehts dann weiter zum PID Regler oder PWM-Rampe, der auf x_des_speed hoch regelt
So einfach ist das. Man spart Stack-rumgehopse, nutzlose Variablen, ineffizienten Code, es läuft mechanisch "rund"... Ihr werdet den Code nicht 1:1 verwenden können aber es zeigt das es Sinn macht sich das mal anzusehen. Ihr müsstet statt vTaskDelay( (portTickType ) 100 / portTICK_RATE_MS ); zumindest ein kurzen (100-200ms) delay/sleep verwenden können da die Speed Werte beim Ausrollen in der ISR alle 200ms bzw. 5x/sec bei "// Speed measurement timer" weiter berechnet werden. Auch die task_motionControl() wird - per Flag "motor_control" dort gesetzt - nur alle 200ms bzw. 5x/sec tatsächlich komplett ausgeführt - egal wie oft man sie sonst per task_RP6System(); startet. Eine "PWM Software Ramp" ist also in Wirklichkeit eine Treppe mit 5 oder weniger Stufen/sek., je nach dem ob die task_motionControl() auch mindestens 5x pro Sek .. oder eben weniger oft aufgerufen wird. Ich prüfe da letztlich nur alle 100ms ob Speed 0 ist, da Speed eh nicht viel schneller aktualisiert wird. Es wird also je nach "Bremsweg" meist 5-10 (0,5-1 Sec) mal ans RTOS System zurück gegeben, bevor die Schleife beendet ist und die Motoren nur umgepolt wenn sie ruhen.
In der darauf folgenden Motorregelung // Left motor speed control: bzw Gegensinnig // Right motor speed control:
werden z.B. die Parameter für die PWM Rate in der Berechnung von mright_power und mleft_power doppelmoppelartig gegen max Werte geprüft. Tip:210/420/-420
Der ganze Abschnitt lässt sich auf die Hälfte des Codes reduzieren. Da ich aber dort nun einen PID Regler verwende, entfällt das bei mir eh ganz.
Ist mir nur so beim durcharbeiten des Codes aufgefallen...
Der Bot könnte nun in der Zeit wo er bremst auch eine Ultraschallmessung starten und auswerten oder nen Kompas auslesen...seine Lage in einer Map aktualisieren.... usw.
Bei euch wartet er im besten Fall einfach nur bis er steht, ruckelt abhängig vom Aufruf der task_motionControl() zum stehen... oder schredert grade nen Satz Ritzel. :/
Später wird task_motionControl() als vollwertiger RTOS Task komplett Timer- und Aufrufunabhängig bzw. zu festen Tick Zeiten im System mitlaufen und ungenutzte Rechenzeit abgeben so das genügend Rechenzeit für andere zeitkritische Aufgaben bleibt...
Ihr seht... RTOS auf dem RP6 macht Sinn... und es tut sich was.
Gruß Rolf
PS/Nachtrag I: Irgendwo steht bei mir im
// we use 16 bit registers in a 16 bit timer, not 8 bit!
#define PWM_L OCR1B // No.. NOT OCR1BL
#define PWM_R OCR1A // No.. NOT OCR1AL
Macht das Fehlersuchen manchmal einfacher... :)
PS/Nachtrag II: ...manchmal muss ich aber auch nur einfach herzlich lachen... Aus move(); in der RP6 Lib:
...
distanceToMove_L = distance;
distanceToMove_R = distance;
motion_status_tmp = motion_status.byte; // is ja noch ok...
motion_status_tmp = motion_status.byte; // jaaaa.. und noch mal druff, falls es beim ersten Versuch nicht klappte.... :-)
MOTIONCONTROL_stateChangedHandler();
...
Da ist bei copy&paste wohl was verrutscht... und bisher nie aufgefallen? In der rotate(); isses dann richtig.
Naja ich grübel noch darüber, warum der MOTIONCONTROL_stateChangedHandler(); aufgerufen wird bevor der Move tatsächlich beendet wurde... Change_D_ ist schließlich Vergangenheit aber zu dem Zeitpunkt hat sich der RP6 noch kein Millimeter bewegt. Als Callback für "fertig" bei einen ungeblockten Move ist das jedenfalls so ungeeignet.
Neues vom RP6RTOS :)
Hab mal meine Entwicklungsumgebung angehängt, wer mit dem Atmel Studio 6 arbeitet, müsste die Projektfiles aufkriegen und compilieren können. Das Make tuts nicht.
Ebenfalls dabei ist ein Hexfile mit dem ich grade Motorfunktionen teste. Das Ding soll aktuell einen Halbkreis rechts rum mit R=300mm fahren. "movecirc" heist die "neue" Rotate funktion in der RP6Lib. (die Motorbefehle für den RP6 fand ich schon immer grausam.. versucht mal ohne ruckeln eine 8 abzufahren... oberer Radius 300mm, unterer 400mm..)
Warum poste ich das? Nun ich hab ja schon mal versucht die Umgebung zu posten und habs nu mal bissel abgespeckt.. docus raus usw. Jetzt passts.
Warum ist das für euch interssant? Hmmm... weil ich immer noch hoffe, Leute zu finden die Interesse daran haben und mit entwickeln - aber auch um zu zeigen das der RP6 sowas kann.
Wer sich die Unterschiede der bisherigen RP6 Lib und der in dem Zip anschaut, sieht das sich viel im Bereich Motoren, Timer und IRQ getan hat. Es gibt allerdings noch einige Schwierigkeiten so das der Code nicht einfach 1:1 anderweitig verwendbar ist. Starke Änderungen gibt es z.B. bei den Motorbefehlen, es wird letztlich nur noch 2-3 Befehle geben die aber zusammen mehr können als alles was bisher da war.
Es magelt allerdings an Bewegungspräzision - was ich hoffe bald noch deutlich verbessern zu können. Zur Zeit komme ich auch nur auf 50mm/sek Speed, angeblich sollen es bis 250mm/s sein. Das Ganze ist also als große unfertige Baustelle zu verstehen. Leider wird die RTOS Fassung der RP6Lib auch eher nicht quellkompatibel zur alten Version - schon allein weil sich durch Optimierungen neue Möglichkeiten ergeben. Siehe move und movecirc. Dort konnte ich min. 10-20 Variablen und jede Menge code einsparen.
Die Statistik sagt zur Zeit:
Program Memory Usage : 12974 bytes 39,6 % Full
Data Memory Usage : 1815 bytes 88,6 % Full wobei 1,5KB für den RTOS Heap drauf gehen... da wird sich auch noch was tuen.
Darin verbaut ist nun FreeRTOS, RP6Lib_neu mit RP6uart und das kleine main Programm (mit 2 Tasks, prv_task_RP6System wandert später so oder ähnlich in die RP6Lib, task_main ist mit der Funktion main gleich zu setzen, in meiner main wird nur das OS initialisiert).
Also falls Ihr Interesse habt... die Files habt ihr nun.
Schön wäre z.B. wenn jemand das Make File zum laufen bringen kann oder vielleicht jemand das Timersetup durchgeht.. ich vermute das da noch ein (Denk)Fehler sitzt.
Genaue Infos dazu geb ich dann noch.
LG Rolf
Nachtrag I: In movecirc muss uint16_t radius auf int16_t radius geändert werden damit er auch rückwärts Kurven fahren kann. Die Berechnung negativer desired_speed-Werte scheitert sonst an dem unsigned int. Radien werden da wie Distanzen aber grundsätzlich positiv angegeben. Nur Speeds und der Winkel auf dem Radius als ->Bogenmaß, als Ersatzangabe für L/R sind signed.
Nachtrag II: Also die Geschichte mit dem PID Regler ist zwar technisch interssant (Koppelung bzw. Nichtlienarität von ist/soll der Encoder-Counter gegen PWM Pulsweite) aber nicht zielführend und vor allem zu kompliziert. In der nächsten Version wird der aufwändige PID Regler wieder entfernt und ich probiere einen direkten, an die Encoder bzw. Zählimpulse gebundenen Soll-Folger per 1KHz TimerISR. Das ergibt wieder eine Art Rampensteuerung, evtl. aber mit optionaler GegenEMK so das eine Beschleunigung elektrisch gesehen von 0 bis 100% bzw. 0 in materialschonenden 400ms erfolgen kann. Da die PWM dann nur in 1/400stel Schritten per ms geändert wird, sollten die Lastkurven recht gleichmäßig sein und es nicht zu "rütteln" im Zahnsatz kommen. Dies erlaubt dann hoffentlich eine Genauigkeit bis auf 1 bis max. 2 Encodersignale oder 0,25 bis 0,5 Millimeter. Man kennt solche Steuerungen von Hobbyfräsen o.ä. Das sollte den benötigten Code zur Motorsteuerung dann noch mal radikal reduzieren (bis jetzt nur 2 Zeilen in der ISR pro Kette). Ggf. wäre dann sogar eine Berücksichtigung der elektrischen Motorenlast machbar um die leider nicht ganz äquivalente Zahnradbelastung in garantierten Grenzbereichen zu halten - was natürlich wieder etwas Code kostet. Die Notwendigkeit eines Tasks "Motion" entfällt damit. Dann ist alles an "Ungenauigkeiten" nur noch dem Kettenschlupf bzw. Untergrund zu zu schreiben - was ich gern noch mit einem "Schlupfparameter" ähnlich wie bei der Encoderresolution kompensieren möchte, welche man sogar optional verändern könnte wenn z.b. ein Kompass oder ein Maussensor als genaue Messreferenz vorliegt.
Wieder mal eine Woche rum... was hat sich getan?
Eine ganze Menge... Mein Versuch die Geschwindigkeit des RP6 an Hand von Flankenwechselzeiten in den Encodern zu messen (wie im Nachtrag II im letzten Post noch gehofft) schlug leider grandios fehl. Das lag wohl hauptsächlich daran, das durch diverse CLI/SEI Befehle letztlich die Timer nicht mehr für so genaue Zeitmessungen taugen - aber auch weil der Messzyklus selbst widerum alles andere blockiert. Was ich jedoch nicht probiert habe, ist die Zykluszeit durch reines Zählen einer Var und anschließender Berechnung der CPU Zeit zu ermitteln. Letztlich ist ein Antrieb wie im RP6 durch seine Trägheit und Getriebespiel aber auch nicht dafür gemacht, genauer als vielleicht mit 10 oder 20 Encodersignale zu arbeiten. Man stelle sich nur vor, man wolle den RP6 anfahren und exakt 2mm bewegen... also 8 Encodersignale weit.
Genau so Mumpitz ist aber auch dann eine Definition von:
//#define ENCODER_RESOLUTION 0.23
//#define ENCODER_RESOLUTION 0.235
#define ENCODER_RESOLUTION 0.24
//#define ENCODER_RESOLUTION 0.25
denn bei 4000 Encodersignalen auf 1 Meter liegt der mögliche Fehler (0.25-0.23) hier bei 0.02 pro Schritt oder 20 mm auf 1 Meter. Der Kettenschlupf auf glatten Böden ist garantiert größer und bei Radien ist es eh vorbei. Es ist daher vergebene Liebesmühe, an dieser Baustelle weiter Zeit zu investieren. Der Gewinn an Genauigkeit steht nich mal in Relation zum Mehraufwand der daraus folgenden Berechnung. Zum Nachrechnen: 0.02 * 4000 / 4 = 20, also Fehlerrate * Pulseprometer = 80 Pulse .. oder 20 mm... pro Meter!!!
Dabei ist der RP6 sogar nicht mal sooo schlecht mit seiner Opto-Mechanik, alte Mäuse hatten üblicherweise eine Auflösung von ca. 10 Pulse pro mm, statt 4 wie beim RP6. Nur warum hat man im RP6 nicht auch sowas verwendet...? Ok bringen würde es kaum was denn die Stellgenauigkeit an den Motoren würde damit wenig besser. Die Möglichkeit Geschwindigkeiten im Bereich unter 15mm/sec zu fahren würde sich schon verbessern.
Also verrichtet nun wieder der PID Regler seinen Dienst - inzwischen auch zuverlässig und ohne wirre Berechnungen zwischendurch. Die Messzeiten für die Speed und damit auch die Regleraufrufe wurden von 200 ms (5/sec) auf 125ms (8/sec) herrabgesetzt, dies hat die Genauigkeit dann doch auch etwas erhöht. Es gibt nur noch zwei Move und 1 Stop Befehl, die alles erledigen. Sie gehen Threadsave mit der Motorhardware um so das sich 2 Tasks ggf. nicht gegenseitig in ein Movement pfuschen und es gibt noch viele weitere Änderungen. Die Dinge im einzelnen zu beschreiben macht hier wenig Sinn, ich werde bei Gelegenheit mal ein Wiki Beitrag dazu schreiben. Aktuell sieht der Resourcenverbrauch so aus:
Program Memory Usage : 12556 bytes 38,3 % Full
Data Memory Usage : 1785 bytes 87,2 % Full
Besser als bisher gedacht und befürchtet sag ich da...
Was ich mich aber schon die ganze Zeit frage... denn es geht mir ja nicht nur drum, die Lib RTOS fähig zu machen sondern auch zu verbessern...
Überall steht, man muss vor dem Einsatz der Motoren powerON(); aufrufen... es gibt sogar Forenbeiträge, wo es sich als Fehler ergab, das Leute das vergaßen... warum ist aber noch keiner auf die Idee gekommen, powerON(); in den Move Befehl der Lib rein zu setzen und nach Beendigung des Moves mit powerOFF(); wieder abzuschalten?
Mal davon abgesehen das es Strom spart, verhindert es zudem Fehlfunktionen und lange Gesichter...
Naja ich hoffe gegen Ende kommende Woche mal wieder ein Snapshot mit einer Demo der Fahrsteuerung posten zu können. Eine ruckelfreie 8 zu fahren klappt jedenfalls schon wunderbar und der Bot fährt nun auch stabil von 5-10 mm/sec (20-40 Pulse/sec) bis 250mm/sec (1000 Pulse/sec) :)
Wer das hier aufmerksam gelesen hat http://www.rn-wissen.de/index.php/RP6#Encoder , weiss das das garnicht geht...
Etwa 50 Segmente pro Sekunde ist auch die geringste gerade noch regelbare Geschwindigkeit (das variiert aber etwas von Roboter zu Roboter).
2 Pulse pro 1/8 sec bzw. 16/sec reichen dem PID schon. Das sag ich mal mit Galileo Galilei...
Und sie dreht sich doch!
Zumindest bei meinen beiden RP6er.
LG Rolf
Tolles Projekt!
Überall steht, man muss vor dem Einsatz der Motoren powerON(); aufrufen... es gibt sogar Forenbeiträge, wo es sich als Fehler ergab, das Leute das vergaßen... warum ist aber noch keiner auf die Idee gekommen, powerON(); in den Move Befehl der Lib rein zu setzen und nach Beendigung des Moves mit powerOFF(); wieder abzuschalten?
Mal davon abgesehen das es Strom spart, verhindert es zudem Fehlfunktionen und lange Gesichter...
Das kann man machen, aber powerON() schaltet ja auch neben den Encodern noch andere Teile des RP6 (ACS/IRCOMM, Motorstromsensoren). Man muss dann auch in den zugehörigen Lib-Teilen powerON() passend schalten. Für das ACS/IRCOMM und Motorstromsensoren gäbe es, wenn man von powerON() unabhängig werden will, noch die Lösung, auf dem Mainboard diese Teile hardwaremäßig auf Dauerstromversorgung zu legen:
ACS/IRCOMM: http://www.rn-wissen.de/index.php/RP6#ACS_2
... und Motorstromsensoren: http://www.rn-wissen.de/index.php/RP6#Stromsensoren.
(Das kostet dann aber ca. 10mA Dauerstrom zusätzlich.)
Hallo Rolf,
sieht alles schon ganz gut aus - ich hatte nur leider noch keine Zeit mir alles genau anzusehen und zu testen, mache ich aber demnächst mal.
Ist aber auf jeden Fall eine gute Sache da einen PID Regler zu implementieren.
Zu der Frage im letzten Posting:
Im RP6 werden Reflexsensoren verwendet damit es mit der bestehenden Mechanik des CCRP5 Chassis kompatibel blieb.
(neue Pressformen kosten mehrere tausend Euro).
Auflösung != Genauigkeit.
Das der RP6 nicht auf 0.25mm genau fahren kann sollte offensichtlich sein und steht auch in der Anleitung.
Dennoch ist es halt die Auflösung der Drehgeber und kein "Mumpitz". Das wird ja auch für die Umrechnung der Geschwindigkeit in cm/s benötigt - wie willst Du das denn ohne diese Angabe bitte sonst machen?
Das an der Stelle mehrere Werte zwischen 0.23 - 0.25mm angegeben sind, wird in der Anleitung ausführlich erklärt. Die Ketten sind aus Gummi und haben Noppen (d.h. sie können sich unter Last etwas verformen und unterliegen Fertigungstoleranzen). Je nach Gewicht der Roboters, den Fertigungstoleranzen und sogar je nach Untergrund kann sich der Radius der "Räder" also etwas ändern, das muss man testen und dementsprechend einstellen.
Je Offroad tauglicher ein Roboterfahrwerk, desto ungenauer ist die Odometrie ;-)
Ein Kettenfahrwerk ist per Design (hoher Kettenschlupf beim Rotieren) nicht für präzise Fahrmanöver rein basierend auf der Odometrie gedacht. Dazu muss man externe Sensoren wie Gyro+Beschleunigungssensor verwenden.
--> Die Auflösung der Drehgeber wurde wegen der dadurch überhaupt erst möglichen Geschwindigkeitsregelung auf 625 Pulse
pro Umdrehung erhöht - von zuvor beim alten CCRP5 ganzen 4 Pulsen pro Umdrehung ;-)
MfG,
SlyD
Hallo SlyD,
also testen... dafür sind in der Version da oben noch zu viel Macken drin. Ich werde aber demnächst eine Version mit kleinem Testprogramm rausgeben.. allerdings hat die Lib nicht mehr wirklich viel mit der RP6 Lib zu tun.
Thema Odometrie... also Rechenoperationen mit Kommazahlen (x * 0.25) sind immer komplizierter als z. b. ">>2" was hier bei 2 hundertstel Millimeter pro Puls vollkommen ausreicht. Wie du selbst sagst... die Odometrie an einem Kettenfahrzeug ist konstruktionsbedingt nicht sehr genau. Die Steigerung von 4 (RP5) auf 625 (RP6) Pulse macht natürlich schon viel aus. Eine weitere Verbesserung bringt nicht mehr so viel wie schon geschrieben - das dafür das Presswerkzeug geändert werden muss ist mir als Werkzeugmacher im Kunststoff-Formenbau klar. Daher weis ich aber auch, das es dafür keiner neuer Formen sondern nur Änderungen an den vorhandenen Formen bedarf - die nicht so teuer sind. Aber egal, einen echten Nutzen hätte es nur sehr beschränkt.
Mir ging es in der Abhandlung auch eher darum, zu klären was man von dem Antriebssystem im RP6 erwarten kann.. und was nicht. Wie ich auch schon erwähnte, würden elektrische Motorbremsen (GegenEMK) die Präzision auch noch mal deutlich erhöhen, das geht aber schnell zu Lasten der Batterie und des Getriebes. Im Grunde arbeiten Stepperantriebe nämlich auch nur deswegen genauer, weil sie dieses Prinzip exzessiv nutzen und haben meist noch den zusätzlichen Vorteil, das magels Getriebe kein Verschleiß entsteht. Beim RP6 lohnt es aus diesen Gründen einfach nicht auf Hundertstel zu rechnen - selbst wenn es ginge.
@Dirk
Ok aber das Umklemmen auf feste Versorgung macht nur Sinn wenn man den io Pin unbedingt anders nutzen will da es sonst das Gegenteil bewirkt, von dem was mal beabsichtigt war - Strom zu sparen.
Die powerON/OFF Befehle sinnvoll im Code unter zu bringen nutzt sicher mehr und wer bereit ist, den Pin umzufunktionieren kann sicherlich auch die Lib dahingehend "umschreiben". Ein "#define powerON() nop();" bzw gleiches für powerOFF() würd ja dann schon mal langen...
LG Rolf
Hallo,
dafür sind in der Version da oben noch zu viel Macken drin. Ich werde aber demnächst eine Version mit kleinem Testprogramm rausgeben..
OK.
also Rechenoperationen mit Kommazahlen (x * 0.25) sind immer komplizierter
Rolf, das kommt in der RP6Lib doch gar nicht vor! ;-)
Intern arbeitet die Lib rein mit Encoder Ticks, die Makros machen das Programm nur lesbarer.
Und die Makros werden schon vom Präprozessor berechnet und in Integer umgewandelt.
--> wenn man tatsächlich im AVR Programm die Einheiten konvertieren möchte, dann skaliert man das halt zuvor um Faktor 100.
Nachkommastellen hat man auch bei nahezu allen anderen möglichen Drehgeber Auflösungen bei gegebener Übersetzung und Raddurchmesser.
Es wird eher noch schlimmer. Immerhin hat man so exakt 625 Zähler pro Umdrehung und nicht 312,5 oder 208,333333 oder 156,25 oder sowas krummes ;-)
Selbst wenn sich zufällig alles für 1mm pro Tick ergeben würde: weicht der Raddurchmesser leicht ab, haste doch wieder 0,984mm :-/
Daher weis ich aber auch, das es dafür keiner neuer Formen sondern nur Änderungen an den vorhandenen Formen bedarf - die nicht so teuer sind.
Ne um das gescheit einzubauen wären schon noch ein paar weitere Änderungen gemacht worden wo man nicht so einfach die Form für das Chassis hätte ändern können.
was man von dem Antriebssystem im RP6 erwarten kann..
139 Euro sind gar nix im Robotikbereich. Ich hab schon mit professionellen Systemen (zig tausend Euro) gearbeitet und entwickle gerade selbst was in dem Bereich. Auch da hat man mit zig Problemen zu kämpfen und nichts ist genau, da muss sogar sehr viel mit Wahrscheinlichkeiten gearbeitet werden um die wahrscheinlichste Roboterposition zu bestimmen. Niemand verlässt sich dafür auf Odometrie, die ist für die Motorregelung da und als grober Anhaltspunkt, dass sich der Roboter bewegt.
Sobald man offroad fährt ist es damit eh vorbei.
139 Euro zahlt man bei richtigen Plattformen übrigens mal locker für einen einzigen Motor, ohne alles ;-)
Der RP6 ist für Hobbyanwender, Schüler und Studenten gedacht und daher möglichst kostengünstig gestaltet.
> das magels Getriebe kein Verschleiß entsteht.
Nur ist der Energiebedarf meist höher, die Teile sind recht schwer und werden unhandlich groß wenn das Drehmoment ähnlich
wie bei einem kleinen Motor mit Getriebe sein soll.
Der viel höhere Preis kommt auch noch dazu ;-)
Drehgeber braucht man auch da, es können ja Schritte verloren gehen.
Bringt fast nur Nachteile für einen Roboter wie den RP6.
Und sollte doch mal das Getriebe draufgehen (was schon einige Zeit dauern wird): Ersatz kostet bei Conrad 3,95 Euro inkl. Encoder Musterscheiben.
Die Bauteile im RP6 wurden nicht per Zufall ausgewählt ;-)
MfG,
SlyD
Eine recht wichtige Info am Rande... für Leute die mit freeRTOS spielen...
Im Port ATMega323, welchen ich auch hier nutze, fehlt in portmacro.h eine definition für "portPOINTER_SIZE_TYPE"
Diese wird bei nichtvorhandensein einer Definition in FreeRTOS.h mit
#ifndef portPOINTER_SIZE_TYPE
#define portPOINTER_SIZE_TYPE unsigned long
#endif
gesetzt.
Um das zu verhindern (der ATmega32 hat ja nur ein 16 bit Pointer) sollte man daher
#define portPOINTER_SIZE_TYPE uintptr_t
in die portmacro.h und da in der Nähe von den /* Type definitions. */ setzen.
Zum einen beseitigt das 2 Warnings, zum anderen scheint sich das auf die Stackberechnung in pxTopOfStack aus task.c auszuwirken.
@Slyd
139 Euro sind gar nix im Robotikbereich.
Ich weis das man da auch sehr viel mehr ausgeben kann... mir gehts auch nicht um die Qualität des RP6... die ist ok.
Wer stabilere Ritzel braucht, kann sich ja bei nem 3D-Druckdienst im Web Titanzahnräder anfertigen lassen... die kosten inzwischen auch nicht mehr so viel mehr als die Plastikräder vom großen C. :)
--
Dann muss ich ein Fehler in eigener Sache korrigieren... bei den Berechnungen für die ENCODER_RESOLUTION entstehen Fehler im Bereich von 2 10ntel.. nicht 2 100stel pro Enc.-Step. Da dies doch nennenswert mehr als oben berechnet ist, sollte man die Berechnungen entgegen meiner Aussage oben mit ENCODER_RESOLUTION ausführen und nicht nur einfach >>2 rechnen.
Ansonsten arbeite ich weiter an der Geschichte... nachdem ich feststellte, das die Writechar Funktion den Prozessor in der ISR blockt bis der UART frei ist "while (!(UCSRA & (1<<UDRE)));" , hab ich nun erst mal den Ersatz der UART Funktionen durch gequeute ISR Übertragung vorgezogen. Bei der Fehlersuche bin ich auch auf portPOINTER_SIZE_TYPE gestoßen. Der PID Regler tuts... wobei ich da bald am verzweifeln war... aber egal... die move und movecirc Funktion auch nachdem ich da noch paar Fehler fand.
Bis jetzt ist aber noch nichts so Reif, das man es zeigen kann.. daher .. und auch wegen eines PC-Virus den ich mir die Tage einfing.. dauerts noch bissel bis zur nächsten Veröffentlichung des aktuellen Quellcodes.
LG Rolf
Hallo RolfD,
wieder einmal ein klasse Projekt!
Wie ist denn der momentane Stand?
Sehr praktisch wäre ein Versionsverwaltungssystem wie SVN/Git etc: So geht kein Code durch Viren verloren und alle am Projekt Interessierten können jederzeit den aktuellen Code einsehen.
Grüße,
Max
Hallo Max, leider schleift es etwas aber ich bin ansich noch dran.
SVN/Git (warscheinlich über Surgeforge) überleg ich mir nach der ersten wirklich vorzeigbaren Release.
Zur Zeit versuche ich die UART Lib anzupassen und da gibts noch ein Problem .. danach kann das aber mal begutachtet werden.
LG Rolf
Ich werde das Projekt erst mal auf Eis legen, ich hab leider grade zu viel anders um die Ohren als das ich da z.Z. effektiv dran arbeiten könnte. Als Zusammenfassung kann ich jedoch sagen, das RTOS auf der Base läuft und bei 500 Ticks/sec auch eine gute Performance bringt.
Allerdings scheint es da ein Problem mit dem Port für den Atmega32 in Verbindung mit Interrupts zu geben, welches ich seid einiger Zeit erfolglos versuche zu jagen. Da ich z.Z. kein JTAG/ISP Debugger habe, ist es aufwändig und ein Fischen im Trüben.
Für die meisten dürfte freeRTOS auch zu komplex und klobig sein, als das es sich lohnt da so mehr Zeit zu investieren. Es gibt jedenfalls diverse einfache Scheduler, die vermutlich besser (weil kleiner) für den RP6 geeignet, und einfacher zu handhaben sind. Als Empfehlung sei z.b. mal Nano OS genannt. (wo ich auch schon weiter gekommen bin als mit freeRTOS) http://sourceforge.net/projects/nanoos/
Aus einem "Abfallprodukt" der Arbeit mit dem freeRTOS entsteht jedoch z.B. grade ein Eigenbau Gerätetreiber Konzept für den RP6 bzw. ATmega32, welches nicht an freeRTOS oder sonstige OS'e gebunden ist. Es dürfte auch für die RP6Lib Nutzer interssant sein.
Wenn also jemand am freeRTOS weiter machen möchte, nur zu... irgendwann werde ich mich auch wieder dran setzen.
LG Rolf
Hallo Rolf,
schade, dass Du das Projekt nicht weiter verfolgen kannst. Ich bin weiterhin sehr interessiert an Deinem letzten Stand, da ich ebenfalls ein FreeRTOS auf dem RP6 plane - allerdings erst dann, wenn mein RoboBridge-Projekt abgeschlossen ist.
Könntest Du Deinen aktuellen Stand vielleicht irgendwo hochladen?
Grüße,
Max
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.