Ich will mal wieder ein Statusbericht zum Thema RTOS auf RP6 geben.
Wie im Thread https://www.roboternetz.de/community...otor-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/artic...r_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:
Code:
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
Code:
// 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:
Code:
...
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.
Lesezeichen