Hallo zusammen, ich verwende in einem ferngesteuerten Modellauto einen ATMEGA2560 der die (Servo-)PWMs des Empfängers auswertet und damit LEDs steuert. Allerdings "lese" ich die PWMs nicht einfach mit, sondern gebe sie dann wieder verändert an den Motor aus. Damit kann ich beispielsweise einen "Kindermodus" realisieren, mit dem das Auto dann einfach langsamer fährt. Bisher habe ich die Lenk-Servo PWM nicht verändert, was ich nun gerne tun würde da das Auto brutal schnell lenkt. Ich würde gerne das Lenkverhalten beliebig verändern, z.B. mit einer Lookup Table in der ich für eine gemessene Pulsweite dann eine alternative Pulsweite erzeuge. Der Zusammenhang kann nicht-linear sein, weshalb mir gerade nur eine Lookup Table einfällt. Da in meinem ATMEGA2560 fast alles interruptbasiert abläuft, frage ich mich, ob eine Lookup Table eine gute Idee ist und wie ich sie realisieren soll. Kann man das bspw. mit SWITCH CASE implementieren, oder dauert das in einer ISR zu lange? Schönen Dank für die Tipps!
Sef C. schrieb: > Kann man das bspw. mit SWITCH CASE implementieren, > oder dauert das in einer ISR zu lange? Eine Look-Up Tabelle wird schnell, wenn du den empfangenen Wert als Index für die Tabelle verwendest.
Sef C. schrieb: > Kann man das bspw. mit SWITCH CASE implementieren, > oder dauert das in einer ISR zu lange? ein guter Compiler wird selbst entscheiden ob er eine switch Anweisung in if /else wandelt oder per Tabelle macht. Generell ist es so, dass wenige eng zusammenliegende Werte per Tabelle efektiver sind. In anderen Fällen wird ein binärer Baum aus if/else gebildet.
Sef C. schrieb: > ob eine Lookup Table eine gute Idee ist ... ist meistens die schnellste Methode, etwas zu berechnen, eben weil man nichts rechnen muss. Oft eine gute Idee, kostet eben Speicherplatz. > Kann man das bspw. mit SWITCH CASE implementieren, Eigentlich nicht. Lookup-Tabellen gehen so, dass Du ein Array mit den Zielwerten anlegst, wo Du dann nur noch mit einem Index hineingehst: | ausgangs_pwm_wert = tabelle[ eingangs_pulsweite_in_mikrosekunden ]; Der 2560 hat ja sogar hinreichend RAM um das direkt so zu machen, wenn man geschickt skaliert. Normalerweise legt man aber die Tabelle eher im reichlich vorhandenen PROGMEM ab und greift per "pgm_read_word()" drauf zu. Dauert etwas länger, aber da die Servos sowieso nur alle 20ms einen neuen Steuerpuls kriegen hat man reichlich Zeitbudget. hth (re)
Wenn du die Reaktionszeit des Lenkservo verändern willst, musst du Änderung limitieren. Ein typisches Servo braucht für einen Sprung von 1000µs auf 2000µs ca. 300ms. Wenn du mit 50Hz arbeitest kannst z.b. nur 100µs Schritte pro 20ms zulassen. Allerdings will man idR dass das Servo dem Steuerbefehl so rasch wie möglich folgt... Wenn du hingegen eine Expo-Funktion nachrüsten willst, da der Sender dies nicht beherrscht, ist eine LUT sicher eine gute Wahl. Ich sehe allerdings nicht, warum die Berechnung des neuen PWM-Wertes in einen Interrupt muss.
ich würde einfach eine Änderung nur zulassen wenn eine bestimmte Zeit rum ist. Und diese Änderung auf kleine Werte begrenzen. Angenommen Momentanwert ist 20, nun trudelt ein Wert von 255 ein. Ich schaue nun ob die Zählvariante einen bestimmten Wert erreicht hat, setze diese zurück und erhöhe um den Wert um 5 statt direkt auf 255. Diese Zählvariante kannst du ja in einer vorhanden Interruptroutine einbauen die sowieso regelmäßig angesprungen wird. Außerdem könntest du den Lenkwinkel auch generell oder geschwindigkeitsabhängig begrenzen. Je nachdem welche Werte du alles auswertest.
... schrieb: > Wenn du hingegen eine Expo-Funktion nachrüsten willst, da der Sender > dies nicht beherrscht, ist eine LUT sicher eine gute Wahl. Genau sowas ist der Plan. ... schrieb: > Ich sehe > allerdings nicht, warum die Berechnung des neuen PWM-Wertes in einen > Interrupt muss. Ich messe die tatsächliche Pulsbreite mit einem Timer overflow counter, der nach einem externen Interrupt gestartet wurde. Der Timer overflow kommt nach 16 us, sprich ich bekomme 62 bis 125 Timer overflows für Pulse zwischen 1 und 2 ms. Der Timer muss noch andere Dinge tun und meine anderen Timer sind auch schon gut ausgelastet, von daher löse ich das so. re schrieb: > | ausgangs_pwm_wert = tabelle[ eingangs_pulsweite_in_mikrosekunden ]; Wenn meine Eingangsindizes eben 62 - 125 sind, müsste ich 62 Werte speichern die ich gar nicht brauche. Geht problemlos?
Sef C. schrieb: > Wenn meine Eingangsindizes eben 62 - 125 sind, müsste ich 62 Werte > speichern die ich gar nicht brauche. Geht problemlos? Warum denn nicht? Platz hat der 2560 dafür reichlich. Die Tabelle interessiert es nicht, ob die Werte jemals brauchen wirst, oder nicht, das macht keinen Unterschied. Um Speicherplatz zu sparen, würde man aber eher vorher vom Index eben diese 62 subtrahieren (Bereichsprüfung ist dann nützlich). (re)
re schrieb: > Um Speicherplatz zu sparen, würde man aber eher vorher vom Index eben > diese 62 subtrahieren (Bereichsprüfung ist dann nützlich). Lieber 62 unnötige Werte oder regelmäßig eine Subtraktion? Mir erscheinen die unnötigen Werte besser.
:
Bearbeitet durch User
Sef C. schrieb: >> Um Speicherplatz zu sparen, würde man aber eher vorher vom Index eben >> diese 62 subtrahieren (Bereichsprüfung ist dann nützlich). > > Lieber 62 unnötige Werte oder regelmäßig eine Subtraktion? Mir > erscheinen die unnötigen Werte besser. Je nachdem ob du genug Speicherplatz hast oder nicht. Letztlich hast du den Speicherplatz mit dem Kauf des Prozessors bezahlt - also nutze ihn ruhig aus.
Baendiger schrieb: > Je nachdem ob du genug Speicherplatz hast oder nicht. Letztlich hast du > den Speicherplatz mit dem Kauf des Prozessors bezahlt - also nutze ihn > ruhig aus. Top, danke!
Thomas Z. schrieb: > ein guter Compiler wird selbst entscheiden ob er eine switch Anweisung > in if /else wandelt oder per Tabelle macht. Generell ist es so, dass > wenige eng zusammenliegende Werte per Tabelle efektiver sind. In anderen > Fällen wird ein binärer Baum aus if/else gebildet. Der Compiler kann zwar entscheiden, wie er ein switch/case umsetzt. Er kann aber nicht erkennen, dass in diesem Fall die Geschwindigkeit essentiell ist. Wenn LUT, dann auch wirklich als LUT anlegen und nicht auf den Compiler verlassen. Sef C. schrieb: > Lieber 62 unnötige Werte oder regelmäßig eine Subtraktion? Mir > erscheinen die unnötigen Werte besser. Düfte keine Rolle spielen, da Du eh eine Bereichsprüfung brauchst. Und je nach Art der Erzeugung der LUT kann ein kleinerer Bereich auch sinnvoll sein.
hmm. ich halte ja auch nicht viel von 12bit Servoauflösung, aber 6bit dürfte etwas wenig sein. Je nach Geometrie der Anlenkung bist du da ja bei ca. 1.5° Schrittwsiten. Die typischen Trimmungsschritte älterer Anlagen lagen eher bei 2-5us. Bist du wirklich sicher dass du Expo brauchst, und nicht einfach nur eine unzureichende Neutralposition wegen zu geringer Auflösung hast? 8bit können selbst Billigfabrikate idR sauber stellen. Aber mit 3-4 Trim-Klicks Schrittweite, kannst du ev. gar nicht erst geradeaus fahren... Wenn irgendwie möglich, würde ich eher per input capture die Impulsdauer zumindest auf 10bit messen, anschließend die Umrechnungen durchführen und die Impulserzeugung aktualisieren. Die paar us Latenzzeit dürften zu vernachlässigen sein, sonst hätte das multi-wii projekt auch nie funktioniert.
Ich hatte mal anhand eines R/C Autos ausprobiert ob 8-bit Timer für Modellbau-Servos reichen, weil es mir aus dem Bauch heraus zu wenig vorkam. Es hat sehr gut funktioniert, sowohl die Lenkung als auch der Antrieb. Ich habe die Abstufungen nicht gespürt. Ich muss dazu sagen, dass ich mit meinem 8-bit Timer allerdings 5 Servos Round-Robin angesteuert habe. Die Zyklus-Zeit für einen Timer-Durchlauf war daher nicht 20ms sondern 8ms, was die Auflösung für jeden einzelnen Servo um Faktor 5 verbesserte. Wenn mehr als 8-bit zur Verfügung stehen, würde ich das nutzen. Aber wenn nicht, ist es keine Schande.
Sef C. schrieb: > Allerdings "lese" ich die PWMs nicht einfach mit, sondern gebe sie dann > wieder verändert an den Motor aus. > z.B. mit einer Lookup Table in der ich für eine gemessene Pulsweite dann > eine alternative Pulsweite erzeuge. Habe sowas vor ein paar Jahren schon mal auf einem Atmega328 gemacht. Allerdings habe ich den Ausgabewert nicht in der ISR berechnet, sondern im Hauptprogramm. Wenn ich mich recht erinnere wurde die Routine zur LUT dort im 1ms Zeitraster aufgerufen. Hat damals wunderbar funktioniert. Code war in Assembler, weshalb er dir wohl wenig nützt.
Wenig Zeit für die Interrupt-Routine und genug Reserven im SRAM: Dann ist die LUT genau richtig! Wenig SRAM und genug Zeit: Im Interupt ausrechnen. Notfalls kann man auch in der Interrupt-Routine, vor der Ausrechnerei den Interrupt für zeitkritische Routinen wieder freigeben. Oder das Ausrechnen per Flag-Benachrichtigung im Hauptprogramm nachziehen. Oder, oder ... Entweder man programmiert gern klug und resourcensparend, oder man nimmt gleich was Überdimensioniertes (Raspi o.b.) ....
Jakob schrieb: > Wenig SRAM und genug Zeit: Im Interupt ausrechnen. Eigentlich gar nicht im Interrupt
Input Capture für die µs und Flags setzen im Interrupt. Ausrechnen, Schalten und Walten (ua. Flags zurücksetzen), dann in der Main. LUT im Flash. Wie schaut denn deine SW aus? Sowas haben hier schon viele programmiert. Vllt. kann man da was "optimieren". Mega88 oder AtMega16L; etwas von der Größe war damals(tm) mit das gebräuchlichste für "sowas". Niemand wäre auf die Idee gekommen, dafür einen AtMega2560 zu verwenden ...
Sef C. schrieb: > Wenn meine Eingangsindizes eben 62 - 125 sind, müsste ich 62 Werte > speichern die ich gar nicht brauche. Geht problemlos? In C geht doch jede Sauerei... Leg dir die 125-62+1 = 64 Werte in ein Feld:
1 | // oder anderer Typ statt uint16_t, je nachdem was du brauchst
|
2 | const uint16_t meineLUT_0_bis_63[64] = |
3 | {
|
4 | 100, // lookup-Wert für Index 62 |
5 | 101, // lookup-Wert für Index 63 |
6 | ...
|
7 | 166, // lookup-Wert für Index 124 |
8 | 157, // lookup-Wert für Index 125 |
9 | };
|
Dann einen Zeiger, der 62 Elemente unter das Feld zeigt:
1 | const uint16_t * const meineLUT_62_bis_125 = meineLUT_0_bis_63 - 62; |
Jetzt kannst du mit dem Zeiger auf meineLUT_62_bis_125[62] bis meineLUT_62_bis_125[125] zugreifen. Die Elemente daneben sind natürlich tabu... Der Zeiger meineLUT_62_bis_125 zeigt jetzt zwar auf Speicher, der dich nichts angeht. Solange du aber den richtigen Index nimmst, passt alles und du hast keinen Speicher vergeudet (außer den paar Byte für den Zeiger, wenn der Compiler das nicht beim Optimieren wegputzt).
:
Bearbeitet durch User
re schrieb: > Normalerweise legt man aber die Tabelle eher im reichlich vorhandenen > PROGMEM ab und greift per "pgm_read_word()" drauf zu. Man kann auch mehrere Tabellen zur Compilezeit berechnen lassen und im Flash ablegen. Zur Laufzeit schaltet man dann nur bequem zwischen den Tabellen um, indem man einen Pointer auf die Startadresse setzt. Man muß dann nichts mehr neu berechnen. Das Laden einer Pointervariablen kostet kaum Zeit. Ob man das im Main oder im Interrupt macht, ist daher egal. Die Berechnung kann man z.B. in Excel machen lassen und dann includen.
Sef C. schrieb: > Bisher habe ich die Lenk-Servo PWM nicht verändert, was ich nun gerne > tun würde da das Auto brutal schnell lenkt. Ich würde gerne das > Lenkverhalten beliebig verändern, z.B. mit einer Lookup Table in der ich > für eine gemessene Pulsweite dann eine alternative Pulsweite erzeuge. > Der Zusammenhang kann nicht-linear sein, weshalb mir gerade nur eine > Lookup Table einfällt. Es gibt auch Fernbedienungen die das schon können und sich der Lookup an der Fernbedienung online umstellen lässt.
A. S. (achs) schrieb
> Eigentlich gar nicht im Interrupt
Warum nicht? Weil es die Mama verboten hat?
Es gibt Programme, die bestehen aus einer leeren Main-Schleife und
einer Interruptroutine. Im 10 ms - Interrupt kann ich doch Berechnungen
z.B. über <= 9 ms machen lassen...
Klar, wer nicht weiß, wie man Resourcen handhabt, hält sich besser
an simple Faustregeln. Wer allerdings weiß, was er/sie macht, kommt
damit schneller, höher, weiter!
Peter D. schrieb: > re schrieb: >> Normalerweise legt man aber die Tabelle eher im reichlich vorhandenen >> PROGMEM ab und greift per "pgm_read_word()" drauf zu. > > Man kann auch mehrere Tabellen zur Compilezeit berechnen lassen und im > Flash ablegen. [...] Die Berechnung kann man z.B. in Excel machen > lassen und dann includen. D'accord, das ist für µController sowieso ein universell brauchbares Mantra, alles, was irgendwie zur Compilezeit erledigt werden könnte, auch tatsächlich zur Compilezeit zu erledigen und nicht erst zur Laufzeit. Kostet fast nix und spart oft Ärger. Auch wenn es im hier vorliegenden Fall von lumpigen 62 Werten ziemlich egal ist und der 2560 ziemlicher Overkill ist, wenn er nicht noch was anderes zu tun hat. Jakob schrieb: > A. S. (achs) schrieb >> Eigentlich gar nicht im Interrupt > > Warum nicht? Weil es die Mama verboten hat? [...] > Im 10 ms-Interrupt kann ich doch Berechnungen z.B. über <= 9 ms machen Im Prinzip ja, aber ... das ist imho nur sinnvoll, wenn man ausschließlich für das hier und jetzt programmiert und spätere Programmerweiterungen kategorisch ausschließt. Wo dann plötzlich doch mehrere Interrupts gebraucht werden, oder das Latenz-Budget doch geringer ist, weil die Berechnungen doch umfangreicher werden oder die Interrupts doch mit höherer Frequenz kommen, oder ... Ohne jetzt "Cargo-Cult-Programming" propagieren zu wollen, aber wenn man vorher auf Mama gehört hat und alles aus Gewohnheit schön sauber in Top-Halves und Bottom-Halves getrennt hat, dann hat man später ein Problem weniger. (re)
Jakob schrieb: > Wer allerdings weiß, was er/sie macht, kommt > damit schneller, höher, weiter! Der TO wollte die LUT als Switch/Case anlegen. Wenn Du Dir sicher bist, dass er weiß, was er/sie macht, dann OK.
A. S. schrieb: > Der TO wollte die LUT als Switch/Case anlegen. ... ein Indiz dafür, dass wir alle irgendwie aneinander vorbei reden. Vielleicht hatte der TO gar keine wirkliche Lookup-Table im Sinn, sondern eher eine Tabelle mit Stützstellen für eine Interpolation und bloss ein falshces Wort benutzt. Dann würden auch die ganzen Bemerkungen wegen der Rechenzeit einen Sinn ergeben, die bei einer Lookup-Table (im Sinne des etablierten Begriffs) ja eigentlich sowas von gegenstandslos sind. @TO? (re) (Wie auch immer: Es hat schon seinen guten Grund, dass kaum wer auf die Idee kommt, hier etwas anderes als eine echte Lookup-Table nehmen zu wollen.)
A. S. schrieb: > Der TO wollte die LUT als Switch/Case anlegen. re schrieb: > ... ein Indiz dafür, dass wir alle irgendwie aneinander vorbei reden. Wieso das? Sef ist in mehreren Beiträgen auf den Ansatz mit dem Array eingegangen. Von switch/case hat er hingegen nichts mehr geschrieben. Damit ist die Sache für mich klar: switch/case ist vom Tisch.
Sef C. schrieb: > Bisher habe ich die Lenk-Servo PWM nicht verändert, was ich nun gerne > tun würde da das Auto brutal schnell lenkt. Ich würde gerne das > Lenkverhalten beliebig verändern, z.B. mit einer Lookup Table in der ich > für eine gemessene Pulsweite dann eine alternative Pulsweite erzeuge. Ist eigentlich immer noch Overkill. Schreib doch mal, wie die Fernsteuerung mit dem Auto überhaupt aussieht. Wie du das schreibst, könnte da für die Lenkung überhaupt kein "richtiges" Servo drin sein. Die ganz billigen "Fernsteuerdinge" haben da wirklich nur hau-ruck-rechts-links drin, aber eben kein Servo. Auf der anderen Seite kann ich mir natürlich nur schwer vorstellen, dass du in so ein "Primitivding" einen gestandenen Controller reinbaust. Danke und Gruß, Rainer
Ich habe mal für meinen Sohn (als er noch sehr klein war) bei einem R/C Auto 2 von 6 Akkus durch ein Stück Draht ersetzt, damit es langsamer fährt und lenkt.
hallo könnte man nicht mit map(); arbeiten? file:///C:/Program%20Files/WindowsApps/ArduinoLLC.ArduinoIDE_1.8.49.0_x8 6__mdqgnx93n4wtt/reference/www.arduino.cc/en/Reference/Map.html
Gib mal bitte deine Festplatte C:\ frei, damit wir das Lesen können...
Stefan ⛄ F. schrieb: > Ich habe mal... Naja.. ich habe mal (als ich noch klein war) mit Teddybär und handgeschobenem Holzauto gespielt. Ob nun der Nachwuchs mit ferngesteuerten Spielzeug-Autos klüger wird, wage ich zu bezweifeln. W.S.
W.S. schrieb: > Naja.. ich habe mal (als ich noch klein war) mit Teddybär und > handgeschobenem Holzauto gespielt. Ob nun der Nachwuchs mit > ferngesteuerten Spielzeug-Autos klüger wird, wage ich zu bezweifeln. Darum ging es nicht. Es ging um den Tipp, dass manche R/C Autos wie gewünscht langsamer werden und trotzdem gut funktionieren, wenn man einfach die Batteriespannung senkt.
max123 schrieb: > könnte man nicht mit map() arbeiten? Nein, weil Sef C. schrieb: > Der Zusammenhang kann nicht-linear sein, weshalb mir gerade nur eine > Lookup Table einfällt.
Stefan ⛄ F. schrieb: > Es ging um den Tipp, dass manche R/C Autos wie > gewünscht langsamer werden und trotzdem gut funktionieren, wenn man > einfach die Batteriespannung senkt. Und das wird hier sicher auch die Lösung sein. Denn es kann sich ja nur um ein "Primitivding" handeln. Ein gutes Auto mit Steuerung hätte das alles als Einstellmöglichkeit schon drin. Kauf ein neues Ding zum Spielen... Gruß Rainer
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.