Regelungsproblem

Hey Ihr,

ich arbeite gerade an einem "Farblimiter" - einer Funktion, welche dynamisch sicherstellen soll, dass Matrixeffekte nicht im Whiteout enden.

Dazu addiere ich einmal alle RGB Werte und vergleiche das mit dem maximal möglichen Wert - Ergebnis ist ein byte welches die momentane Gesamthelligkeit repräsentiert, 0 = alle LEDs aus, 255 = alle LEDs auf maximaler Helligkeit.

In Abhängigkeit dieser ermittelten Helligkeit möchte ich - wenn nötig - die Helligkeit jeder einzelnen LED mehr oder weniger runterskalieren. Ich möchte sozusagen eine durchschnittliche Zielhelligkeit einstellen.

Bedauerlicherweise kann ich das nicht als lineare Gleichung ausdrücken.

Wie könnte ich die Lösung angehen? Lande ich da bei einem PID Regler?

Grüße Helmuth

Du solltest Dein Problem und die Schaltung, die Du dazu verwendest, etwas detailierter beschreiben. Nach dem, was ich verstanden habe, ermittelst Du die Helligkeit, indem Du den Durchschnitt der 3 Farbwerte nimmst und damit die LEDs nachregelst. Das kann es aber nicht sein, denn das wäre, da gehen wir wahrscheinlich einig, nicht wirklich schwierig.

Welche Matrixeffekte meinst Du? Misst Du überhaupt etwas oder rechnest Du "nur" mit den Werten, die Du an die LEDs sendest?

Hallo pylon,

ich messe nichts, ich rechne nur mit den Werten im Bildspeicher. Hier mal ein Entwurf der Funktion:

/*
#define MAX 195840
(in my case 16 x 16 LEDs * 3 colors * 255 maxvalue = 195840)
unsigned long counter;
*/

void AutoDim()  
{
  // calculate average brightness of the screen
  counter = 0;
  for(int i = 0; i < NUM_LEDS; i++) {
    counter += leds[i].r;
    counter += leds[i].g;
    counter += leds[i].b;
  }

  // 0 means all off, 255 means all white
  byte bright = (counter * 255) / MAX;
 
  // scale down depending on actual brightness
  for(int i = 0; i < NUM_LEDS; i++) {
    leds[i].nscale8(255 - bright);
  }
}?

Vielleicht habe ich ich wieder nur eine Denk-/Verständnisblockade, aber so trivial, wie es klingt, ist die Nachregelung nicht. Es geht um diesen Punkt hier:

leds[i].nscale8(255 - bright);

Je nach Effekt(kombination), bewegt sich der Wert, den man nscale8 übergeben muss, in einem sehr engen Fenster, damit genau das passiert, was ich vorhabe - beispielsweise zwischen 125 und 135, in einem anderen Fall zwischen 30 und 32...

Was für Effekte? Anstelle langer Beschreibungen schau doch mal hier, was ich gerade mache: Funky Clouds - YouTube

Ich beabsichtige, 2 Grundprobleme zu lösen: Jeder "schickere" Effekt ist eine Kombination aus mehreren anderen, z.B. einmal im Kreis mit dem Nachbarpixel interpolieren, in einem kleineren sich bewegenden Auschnitt nochmal, "Wind" von der Seite, u.s.w....

Da ich das momentan alles in einem(!) Array mache (wegen endlichem RAM nicht in "Ebenen" in mehreren Arrays), braucht es viel Feintuning und die Helligkeitskontrolle nach jedem einzelnen Schritt. Wäre schön, wenn das "von allein" passiert.

Und Grundproblem B: Momentan stelle ich mir von Hand nach jedem Schritt einen fixen Scalingfaktor ein und das sieht alles wunderschön aus. Wenn das aber jemand anderes auf einer anderen Matrix mit einer anderen Auflösung testet, ist es momentan praktisch unmöglich, den gleichen Effekt zu reproduzieren.

Hier mal noch ein Beispiel, wie der komplette Code für so eine Effektkombination aussieht: Funky Clouds 2014 - Toys For Matrix Effects · GitHub

Beste Grüße

Helmuth

ich arbeite gerade an einem "Farblimiter" - einer Funktion, welche dynamisch sicherstellen soll, dass Matrixeffekte nicht im Whiteout enden.

Mit der nscale8()-Methode kannst Du nie im Whiteout enden, denn sie verdunkelt einen Pixelwert nur, sie macht ihn nie heller. Wenn Du verhindern willst, dass der Pixel ganz schwarz wird, nimm nscale8_video() anstatt nscale8().

In Abhängigkeit dieser ermittelten Helligkeit möchte ich - wenn nötig - die Helligkeit jeder einzelnen LED mehr oder weniger runterskalieren. Ich möchte sozusagen eine durchschnittliche Zielhelligkeit einstellen.

Soll das heissen, Du möchtest mit der Funktion sicherstellen, dass der ermittelte Wert "bright" nach der Ausführung einen konstanten Wert hat? Mit nscale8() kannst Du aber nur runterdimmen, nie rauf, somit kannst Du den konstanten Wert nicht erreichen, falls "bright" zu tief ist. Oder habe ich immer noch etwas falsch verstanden?

Hallo pylon,

offensichtlich habe ich mich immer noch unklar ausgedrückt. Deine Anmerkungen sind alle zutreffend, helfen jedoch noch nicht weiter.

Alle Effekte, die ich bisher geschrieben habe, funktionieren in einem Array: CRGB leds[NUM_LEDS]

Alle Effekte basieren auf der Addition von Farben verschiedener Pixel, z.B.

leds[i] += leds[i + 1];

Was passiert bei der Addition? Das Ergebnis wird heller. Dabei entstehen schöne Mischfarben - aber nur, wenn das Ergebnis "im Rahmen bleibt". Addiert man z.B. sattes gelb zu sattem cyan (weil die nebeneinanderliegenden Pixel gerade zufällig so aussehen) ist das Ergebnis weiß. Soweit kein Problem für diesen einen Pixel, aber:

Diese Additionen laufen in mehreren Iterationen - also werden immer mehr Pixel weiß. Weiß + x = weiß. Bis zu einem gewissen Punkt ist das ok - so lange noch Stuktur/Form im entstehenden Muster erkennbar ist - aber anschließend werden immer mehr und schließlich alle Pixel weiß.

Das gilt es zu verhindern.

Momentan mache ich das, indem ich vor/nach der Addition die betreffenden Pixel ein wenig runterdimme. Und genau das ist ein schmaler Grat. Dimme ich zu viel, "geht Farbe verloren" und das Ergebnis wird zu dunkel. Dimme ich zu wenig, driftet das Ergebnis immer mehr in Richtung weiß.

Wenn diese Balance von Hand eingestellt ist, ist alles schön - füge ich aber z.B. einen weiteren Emitter hinzu, muss ich das Ausmaß der Dimmung erneut anpassen, da plötzlich "mehr Farbe" auf dem Screen ist.

Hinter "durchschnittliche Helligkeit" (bright) feststellen steckt die Idee, dass sich viele weiße Pixel in einem relativ hohen bright Wert wiederspiegeln. Und mein Ansatz ist, in Abhängigkeit von bright (vor/nach dem addieren) mehr oder weniger stark runterzuskalieren, um in Abhängigkeit vom momentanen Bildinhalt und dem zu erwartenden Ergebnis nach der nächsten Addition dynamisch gegenzusteuern - so, dass das Ergebnis nicht zu dunkel (schwache Farben) und nicht zu hell (weiß) wird.

Und dieses Problem erinnerte mich an die Lagestabilisierung des Quadrocopters - wo es auch darum geht, verschiedene unvorhersehbare und wechselwirkende Einflüsse auszugleichen und einen relativ stabilen Gesamtzustand des Systems herbeizuführen.

Jetzt nachvollziehbar?

Gruß Helmuth

P.S. Ich bin mir jedoch nicht sicher, ob die Kenntnis des bright Werts allein hinreichend für die Lösung des Problems ist.

edit: Wenn bright 255 ist, ist auf jeden Fall etwas schiefgelaufen. Wenn bright mal kurz 0 wird, macht das nichts, weil die (pulsierenden) Emitter in Kürze "neue Farben säen".

Soll das heissen, Du möchtest mit der Funktion sicherstellen, dass der ermittelte Wert "bright" nach der Ausführung einen konstanten Wert hat?

Nein, ich möchte einerseits sicherstellen, dass er nicht oder nur kurz 255 erreicht und andererseits nur kurzfristig (z.B.) < 50 wird. Die Untergrenze muss man wohl je nach Effekt festlegen. Wahrscheinlich muss man bright zusätzlich im Zeitverlauf beobachten und je nach Anstiegs-/Abfallgeschwindigkeit gröber oder feiner gegensteuern.

Aber wenn ich mir ansehe, was die Leute hier für Probleme haben, die passenden Parameter für eine PID Regelung zu ermitteln, befürchte ich, dass das das Problem der Handarbeit auch nur verschiebt. Wobei mich grundsätzlich ein leicht schwingendes System nicht wirkich stören würde.

Was ist wenn du aus dem brightWert einen Rückführwert bildest in einem gewissen Rahmen als Regler.
Also ich sag mal der "Regler beginnt bei einem Wert >230 zu arbeiten und gibt dir dann einen Korekkturwert aus.
Ich denke es würde reichen es hier als I-Regler auszuführen schon ausreichend da er Integrierend pro Zeit (am besten Zyklus) abreitet.
Du bekommst aus diesem Regler dann eine Abweichung im Sinne eines Korrektuwertes diesen Wert dann in deinen Faben Logarithmus eingebaut und du dürfest dieses Problem nicht mehr haben.
Die Frage ist aber wenn du einen Regler nutzt willst du ja einen bestimmten Punkt erreichen wenn dieser Brightwert dann auf sagen wir mal 230 geregelt bleibt musst du nur zusehen das dieser Wert dann nicht auf RGB ausgeglichen ist sonst hast du ja nur ein "dunkleres" Weiß.
Wenn man den Korrekturwert auf die unterschiedlichen Faben gibt dürfte es ja kein "dunkleres" Weiß geben.
Gruß
Der Dani

Helmuth:
Alle Effekte, die ich bisher geschrieben habe, funktionieren in einem Array: CRGB leds[NUM_LEDS]

Hast Du schon mal über Effekte nachgedacht, die Du im HSV-Farbraum statt im RGB-Farbraum generierst? Die FastSPI_LED2 Library unterstützt auch modifizierte HSV-Farbräume, bei denen als "V" der Hellwert vorgegeben werden kann:

Und falls Du bei RGB bleiben möchtest:
Wenn ich mich nicht täusche, bringt die FastSPI_LED2 Library auch schnelle Umrechnungsfunktionen zwischen RGB und HSV mit, so dass sich im Zweifelsfall ein bestimmter Farbwert auf eine gewünschte Helligkeit bringen läßt, indem man:

  • den originalen RGB-Wert in einen HSV-Wert umrechnet
  • den Hellwert V im HSV-Wert auf die gewünschte Helligkeit setzt
  • den in der Helligkeit modifizierten HSV-Wert wieder zurück in RGB umwandelt

Helmuth:
Hinter "durchschnittliche Helligkeit" (bright) feststellen steckt die Idee, dass sich viele weiße Pixel in einem relativ hohen bright Wert wiederspiegeln. Und mein Ansatz ist, in Abhängigkeit von bright (vor/nach dem addieren) mehr oder weniger stark runterzuskalieren, um in Abhängigkeit vom momentanen Bildinhalt und dem zu erwartenden Ergebnis nach der nächsten Addition dynamisch gegenzusteuern - so, dass das Ergebnis nicht zu dunkel (schwache Farben) und nicht zu hell (weiß) wird.

Wenn es nur um die Vermeidung übermäßig heller Pixel geht, dann wäre meine Idee: Immer dann, wenn ein Pixel droht weiß zu werden, einfach die Helligkeit halbieren.

  byte r,g,b;
...
  if ((int)r+g+b>612) // Werte als Integer zusammenzählen, 612 ist 80% vom Weißwert 765
  {
    r= r>>1;
    g= g>>1;
    b= b>>1;
  }

@jurs: Ich habe schon damit gespielt, vor der Addition zu prüfen, was passieren wird und es gegebenenfalls zu verhindern, aber dann geht viel Effekt verloren, weil die sich im weißen Bereich ergebende Form sehr spannend ist. Schau nochmal dieses Video ab 0:35 an: https://www.youtube.com/watch?v=0Ehj7sEwOy4

Wenn dieser Ansatz, dann müsste man noch einen Counter einfügen, welcher z.B. nur 2/3 von allen als weiße Pixel erlaubt und dann gegensteuert.

Eine RGB zu HSV Funktion gibt es nach meinem Wissen noch nicht, aber ich schau nochmal. Kann ich mir gerade schwer vorstellen, wie das gehen soll, da HSV auf einer Lookup Tabelle für H basiert und S und V diesen Wert nur in Richtung weiß / schwarz verschiebt = ein Großteil des RGB Farbraums ist in HSV nicht beschreibbar (jedenfalls nicht mit 8 Bit für Hue)?!

@volovodani:

Regler beginnt bei einem Wert >230 zu arbeiten und gibt dir dann einen Korekkturwert aus

Das klingt vielversprechend. Bleibt die Kernfrage, wie man den Korrekturwert beschreiben könnte.

Du bekommst aus diesem Regler dann eine Abweichung im Sinne eines Korrektuwertes diesen Wert dann in deinen Faben Logarithmus eingebaut

Was meinst Du mit Farben Logarithmus? Und ja, genau um die Ermittlung dieses Korrekturwertes geht es mir.

Die Idee mit verschiedenen Reglern /Korrekturen für R, G und B gefällt mir gut, weil das "dunke" (dreckige) weiß tatsächlich auch ein (kleines) Problem ist.

Aber nochmal: Irgendeine Idee, WIE ich den Korrekturwert errechnen könnte?

Nochmal @jurs: Wenn Du selbst damit arbeitest, könnte Dich vielleicht interessieren, dass nach FastSPI_LED2 die FastLED Lib herauskam und wir momentan die Beta von FastLED v2.1 testen - bisher ohne Probleme.

https://github.com/FastLED/FastLED/tree/FastLED2.1

Temporal Dithering bei niedrigen Helligkeiten, neue flotte Wellenfunktionen, Multikanaloutput auf ARM, u.v.m....

Hatte gerade noch die Idee, einen "Autoadjust" Modus zu bauen, wo bei einer Effekt Kombination bright über z.B. 1 Minute lang beobachtet wird und das das dann nacheinander mit 50 Korrekturfaktoren durchgespielt wird wird, um anzuschätzen zu lassen, welcher am besten passt.

Wäre aber nur ein Workarround und keine Echtzeitregelung...

Ich dachte wenn du den Korekkturwert aus dem I-Regler bekommst wenn du den mit einem Einflußfaktor (z.B.30% in einem Zyklus bei z.B. Rot abziehst wenn er wieder hochkommt (der Korekkturwert) dann bei Grün und beim nächsten mal bei Blau. Ich denke dies wird deinem Problem entgegen wirken und bei z.B.10bit bis 15bit "weniger" Helligkeitswert wirst du das mit dem Auge direkt nicht sehen aber du kommst dann halt nicht in das weis.
Diese "Funktion" wird dann in eine eigne Dynamik mitbringen kann mir aber gerade "optisch" nicht vorstellen wie es dann aussieht.

Übrigens ich habe mir die ganzen Videos in Youtube angesehen und muss sagen.......

..... leider Geil.
:smiley: :grin:

Ich stehe ja auf sowas aber die bessere Hälft findet Fabenspiele nur kitschig.

Gruß
DerDani

Edith: Muss nochmal einen Link einfügen

http://www.rn-wissen.de/index.php/Regelungstechnik

esum = esum + e
y = Ki * Ta * esum

Aus dem Tut. Ich meine den Korrekturwert Y den du dann verabeiten musst.

Halt dich bei Reglern fern von "float"s die machen dein Programm nur langsam.

Hier steht viel über Regler und wie man die einfach implementieren kann.
Hier kannst du ja mal sehen welcher Regler zu deinem Projekt passen könnte.

Den Regler z.B. erst aktivieren wenn der bright Wert über 230 springt und den Stellwert wie oben beschrieben verarbeitest.

Mach mal ne neue Antwort:
Ich hatte mich für den I-Regler entschieden weil die Stellgröße keine Sprünge macht d.h. langsam eine Stellgröße produziert und keine Sprünge die dann schwer wahrzunehmen sind.
Gruß
Der Dani

bei z.B.10bit bis 15bit "weniger" Helligkeitswert

Den Satz verstehe ich nicht.

Ja, die eigene Dynamik, welche sich aus den Wechselwirkungen ergibt ist das eigentlich Spannende. Ich bin auch immer wieder überrascht und fasziniert, was alles passiert, wenn man 3 für sich genommen total simple Vorschriften kombiniert - das kann ich mir vorher nicht/nur schwer vorstellen/ausdenken - aber staunen, wenn ich es dann sehe.

Wenn ich o.g. Problem in den Griff bekommen würde, könnte ich z.B. auch ein paar Dutzend Emitter/Stream Funktion via Zufall kombinieren und würde ziemlich lange immer wieder was neues sehen...

Schön, dass Dir mein Zeug gefällt. Und wenn sie es nicht mag, muss sie ja nicht hinschauen... Muss Du ihr halt mal im richtigen Moment im passsenden Bewustseinszustand vorführen... :wink:

Ja, bis jetzt habe ich alles mit integer Rechnungen hinbekommen und das soll auch so bleiben.

Danke für den Link, da lese ich mich mal in Ruhe ein.

Sitze übrigens wie auf Kohlen, bis meine MSGEQ7 endlich kommen und ich Ereignisse synchron zum Sound auslösen kann... Das ist das nächste Level, aber ja...erstmal dieses hier in den Griff bekommen.

Beste Grüße

Helmuth

Da der Sketch hier nicht zu sehen ist denke ich das für jede Farbe den Wert 0-255 bzw 0-1024 gibt je nach auflösung. Wenn du dann von diesem RGB steuerwert die 10-15 Bit abziehst (244-15=239)
So meinte ich das kenne dein Programm ja nicht.
Gruß
DerDani

Ah, so meinst Du das. Es sind übrigens (leider nur) 3 * 8 = 24 Bit Farbwerte. Ja, pro Farbe 10 bis 15 vom Wert abzuziehen ist ein Bereich, der hinkommt.

Falls Interesse, hier der Code von dem Spiralvideo (mit Jurs tollem Timer): Funky Clouds 2014 - Pastebin.com

@jurs: Hier mal noch ein Video, was das Temporal Dithering bei niedrigen Helligkeiten macht: