Hallo, ich entwickle momentan einen FlightController für meinen Quadrocopter. Ich verwende für die Winkelbrechnung einen itg3205 und einen adxl345. Der ATmega64 liest diese zwei Bausteine aus und berechnet daraus den aktuellen Winkel. Zusätzlich liest er 4 PWM - Signale vom Empfänger mittels den externen Interrupt's aus. Und schlussendlich erzeugt er 4 PWM - Signale für die 4 ESC's. Ich habe alles einzeln geschrieben und getestet. Dort hat es immer funktioniert, jedoch wenn ich alles gleichzeitig ausführe stimmt der berechnete Winkel nicht mehr. Meine Theorie dazu ist, dass der Interrupt (der von einem TimerOVF ausgelöst wird), durch die zusätzlichen Interrupt's, nicht mehr im gleichen Zeitabstand aufgerufen wird und das verfälscht den berechneten Winkel (den Winkel des Gyro's) sehr. Stimmt diese Theorie bzw. wie kann ich dieses Problem lösen? Würde es reichen, wenn ich die PWM Signale mittels einem externen Baustein's erzeuge, welcher mit I2C angesprochen wird? Danke für eure Hilfe
Marcel D. V. schrieb: > Stimmt diese Theorie Das wissen wir nicht, aber es kann schon sein. > mittels einem externen Baustein's Eher durch besser formulierte Software.
okay danne für deine Hilfe. Ich werde mal versuchen ob ich was aus der Sodtware rausholen kann.
Die PWM Signale sollten die Timer rein in HW erzeugen können. Das sollte also nicht das Problem sein.
Steig beizeiten um auf was mit mehr Rechenleistung. Könnte sein, dass du es rechenzeitmässig noch reingequetscht bekommst, bist aber von Anfang an am oberen Limit - nicht gut. Es kommen ständig neue Sachen hinzu :-) Ich habe sowas mal gemacht, allerdings für einen echten Helikopter (könnte mir vorstellen, dass ein Quadrokopter etwas einfacher zu steuern ist), die erste Version Mit ATMega ging in die Hose... STM32 war prima.
ja es ist aber so: der Winkel stimmt solange die PWM - Signale einen HighPegel von 1.0 ms haben. (19 ms low) erhöhe ich diese Zeit (gebe gas) stimmt er bei weiten nicht mehr. darum dachte ich, dass das irgendwie zusammenhängt. ich habe viele FlightController mit einen Atmega daraug gesehen, daher denke ich, dass es möglich ist. aber sobald ich mehr Funktionen einbauen will, muss ich ziehmlich sicher alles anders aufbauen.
Hallo Marcel, und mit was kodierst Du? Zeige uns bitte deine Hauptprogrammschleife. THX
ich kodiere mit AVR Studio. meine Code besteht aus vielen Teilen. ich kann morgen einiges davon mal posten.
Versuch deinen Code um alles was geht zu optimieren. Das reine benutzen von Hardware sollte da keine Probleme machen. Wenn du dem Timer sagst welchen Wert du haben willst, sollte es alles weitere ohne dein Zutun erledigen. Versuche den Code so zu schreiben, dass du nicht überwachen musst ob du Fehler dinn hast. Solche Überwachungen machen meist sehr viel aus. Ich hab früher auch in jeder Software ebene überwacht ob ich einen falschen Wert habe, wenn es jedoch wirklich darauf ankommt, mache ich das nur noch soweit oben wie möglich. Wieso sollte ich einen Wert der nicht falsch sein kann überprüfen? Zeigt doch mal bitte deine Hauptschleife, vielleicht finden wir da noch n paar Sachen.
Okay ich werde das Mal beachten. Ich werde meine Hauptschleife morgen posten.
Marcel, ich hatte ein sehr ähnliches Problem. Ich würde an deiner Stelle eine extra Platine kaufen, durch serial oder I2C angeschlossen, mit der Du die Motoren ansteuern kannst. Der CPU wird somit entlastet und die PWM Signale der Steurungsplatine delegiert. Durch diese Entkopplung solltest Du das Problem beseitigen können. Gruß
Das habe ich auch zusätzlich dazu geplant zu machen. ich werde aber auch versuchen den Code zu optimieren.
@ Marcel D. V. (dmarcel21) >Das habe ich auch zusätzlich dazu geplant zu machen. >ich werde aber auch versuchen den Code zu optimieren. Das kommt später. Erstmal musst du MESSEN, um deine Vermutung zu untermauern oder zu widerlegen. Das kann man im ersen Schritt im Simulator machen. Aber das könnte schwierig werden, wenn man I2C Zugriffe simulieren will. Einfacher und schneller ist eine Messung am echten System mit einem Testpin, welcher immer dann auf HIGH geht, wenn der Controller aktiv was macht und wieder auf LOW, wenn eine Funktion etc. beendet ist. Damit kann man in Echtzeit die Rechenzeit der einzelnen Funktionen messen. Am Ende deiner Hauptschleife, wenn alle zyklischen Aufgaben erledigt sind, muss immer noch etwas Zeit übrig bleiben. Wenn nicht, ist dein Controller überlastet. Man sollte aber nicht vergessen, dass es auch andere Fehlermöglichkeiten gibt. Z.B. dass deine Motortreiber deine Sensoren stören. Ein Oszi hilft hier auch ungemein.
Das Problem ist das mein ganzer Code in Interrupts ist. Aber ich habe schon eine Idee wie ich das lösen kann. Danke mal für die Hilfe
@ Marcel D. V. (dmarcel21) >Das Problem ist das mein ganzer Code in Interrupts ist. Kann sinnvoll sein. Aber umso einfacher kann man das messen bzw. Prüfen. http://www.mikrocontroller.net/articles/Multitasking#Ein_einfaches_Beispiel_f.C3.BCr_den_AVR 3.Beispiel mit Timer. "Es kann leicht im realen System geprüft werden, ob die Laufzeit der Tasks klein genug ist, um den Anforderungen des Timers zu genügen. "
> Das Problem ist das mein ganzer Code in Interrupts ist.
Schön, dass Du es erkannt hast. Das war der wichtigste Schritt. Nun mach
es besser.
Lies Dich in das Thema "Zustandsautomat" ein und nutze Interrupts
lediglich dazu, Ereignisse zu erfassen und in irgendwelche Variablen
abzulegen, damit sie später in der (einzigen) Programmschleife
abgearbeitet werden.
Sobald Deine Interrupt-Routine aus mehr als 10 Zeilen C besteht,
solltest Du nachdenklich werden. Folgende Dinge haben in
Interrupt-Routinen grundsätzlich nichts zu suchen:
- Display Ausgaben
- Divisionen mit Divisor ungleich 2, 4, 8, 16, ....
- Fließkommazahlen
- Wiederholschleifen
- Sleep Befehle
Lass mich raten: Du hast aon allem was dabei, oder?
Nein ganz so dumm bin jetzt auch wieder nicht. Ich habe keine Displayausgaben und Sleepbefehle in meinen Interrupts. Jedoch habe ich die Berechnungen vom Gyro in einem. Aber wie soll ich eine Berechnung, welche nur benötigt wird, wenn er den Gyro ausliest ausliest dann einbinden?
Marcel D. V. schrieb: > Nein ganz so dumm bin jetzt auch wieder nicht. > Ich habe keine Displayausgaben und Sleepbefehle in meinen Interrupts. > Jedoch habe ich die Berechnungen vom Gyro in einem. > Aber wie soll ich eine Berechnung, welche nur benötigt wird, wenn er den > Gyro ausliest ausliest dann einbinden? So wie immer
1 | volatile uint8_t gyroReady; |
2 | |
3 | IRS( ... ) |
4 | {
|
5 | gyroReady = 1; |
6 | }
|
7 | |
8 | int main() |
9 | {
|
10 | ...
|
11 | |
12 | while( 1 ) { |
13 | |
14 | ...
|
15 | if( gyroReady ) { |
16 | gyroReady = 0; |
17 | |
18 | ..... mach was mit den Gyro Daten |
19 | }
|
20 | |
21 | ....
|
22 | }
|
23 | }
|
und so machst du das mit allem was ein wenig länger dauert. Die ISR kann kleinere Dinge berechnen, solange sie schnell gehen. Ansonsten stösst die nur an, indem sie das Flag setzt. In der Hauptschleife werden nacheinander die einzelnen 'Jobs' überprüft, ob sie wieder mal anstehen und werden abgearbeitet, wenn es wieder mal soweit ist.
>Aber wie soll ich eine Berechnung, welche nur benötigt wird, wenn er den >Gyro ausliest ausliest dann einbinden? Diese Berechnungen da sicher mit Fließkomma dauern ewig aufm Atmega da kein Hardwaremultiplizierer für floats oder doubles exestiert. Eventuell lässt sich da was optimieren, aber wahrscheinlich brauchst du mehr rechen power bzw. andere architektur (32bit). GRuß Jonas
Man kann im übrigen Berechnungen, wenn sie wirklich extrem lang sind (und sich das auch nicht verkürzen lässt) auch in mehrere Abschnitte unterteilen, die sie selbst auf genau die gleiche Art und Weise die Freigabe geben.
1 | while( 1 ) { |
2 | |
3 | .... YYYY |
4 | |
5 | if( performPart4 ) { |
6 | performPart4 = 0; |
7 | |
8 | ... bearbeite Abschnitt 4 |
9 | }
|
10 | |
11 | if( performPart3 ) { |
12 | performPart3 = 0; |
13 | |
14 | .... bearbeite Abschnitt 3 |
15 | |
16 | performPart4 = 1; |
17 | }
|
18 | |
19 | if( performPart2 ) { |
20 | performPart2 = 0; |
21 | |
22 | .... bearbeite Abschnitt 2 |
23 | |
24 | performPart3 = 1; |
25 | }
|
26 | |
27 | if( doTheJob ) { |
28 | doTheJob = 0; |
29 | |
30 | .... bearbeite Abschnitt 1 |
31 | |
32 | performPart2 = 1; |
33 | }
|
34 | |
35 | ... XXXX |
36 | |
37 | }
|
Ziel der Sache ist es, dass die Programmteile XXXX bzw. YYYY während der länger andauernden Berechnung auch eine Chance haben, ihr Ding zu machen. Der Schlüssel zu einem Programm, welches mehrere Dinge 'gleichzeitig' macht besteht darin, dass alles in kleine Happen aufgeteilt wird und sich der Rechner die Happen reihum einzeln vornimmt. D.h. sofern die Rechenleistung grundsätzlich ausreicht. Ein weiterer Schlüssel besteht in der korrekten Verwendung von Datentypen. Wer einfach wahllos mit int oder double um sich schmeisst, darf sich nicht wundern, wenn sein Mega nicht mehr mitkommt. Fleissaufgaben benötigen schliessllich auch Rechenzeit, selbst wenn sie unnütz sind.
Kalman-Filter (wirst du brauchen) schon drin? Die oben gemachten Vorschläge sind alle gut und richtig - hilft aber alles nichts, wenn du es nicht schaffst, innerhalb deiner Zykluszeit alle Berechnungen auszuführen. Dann bleibt dir nur 3 Wege: -die Zykluszeit erhöhen (schlecht für die Qualität der Regelung) -die Berechnungen zu vereinfachten (was bei einer 3D-Lageregelng Grenzen hat) -die Berechnungen auf einer schnelleren Maschine ausführen
Die Winkelberechnung mache ich mittels einem digitalen Hoch- und Tiefpass. Das ist zwar nicht so genau, wie der Kalman Filter, jedoch ist er weniger aufwendig und benötigt nicht so viel rechenpower. Und mit dieser kleinen Ungenauigkeit kann ich leben. Ja ich versuche eben mal meine Codr zu optimieren und so wenig wie möglich in den Interrups zu machen.
Also grundsätzlich kann man bei richtigem Programmierstil schon gut was ausm AVR rausholen. Aber irgendwann ist Schluss. Dann muss man sich halt mehr Rechenleistung gönnen, z.B. einen Cortex M3/M4. So schlimm ist die Einarbeitung nicht. Ganz im Gegenteil, durch die CMSIS ist die Hardware schnell initialisiert und man kann sich dem eigentlichen Problem widmen. Ingo
Ich bleibe vorerst beim AVR Studio, da andere FlightController ja auch mit ATmegas arbeiten, muss es so gehen. Aber trotzdem danke für die super Vorschläge.
Der ggf. kritische Teil ist die PWM-erzeugung. Wenn man das in Software macht, verbraucht es sehr viel Rechenzeit. Auch bei der Erfassung der Pulse kann man mit einem nicht so guten Programm viel verschenken. Der Trick ist es halt vieles direkt von der Hardware machen zu lassen und nicht alles von Hand zu programmieren. Die Datenerfassung von Gyro und Filterung sollte eher das kleinere Problem sein, und ist eher nicht so zeitkritisch. Wenn das Fehler auftreten wenn der Code länger wird, ist es eher ein Problem mit dem Stack oder nicht atomarem Datenzugriff.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.