B&R Steuerung Mittelwert von Analogwerten bilden (Strukturierter Text)

Bastud

Level-1
Beiträge
2
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo zusammen,

ich habe folgendes Problem.

Ich lese einen analogen Messtaster über den Analogeingang meiner B&R Steuerung (X20 CP 1382) aus. Dazu habe ich dem Eingang eine Variable zugewiesen, die den aktuellen Analogwert speichert. Dieser wird dann mit Hilfe einer VC4 Visualisierung, die man auch in Automation Studio erstellen kann, ausgegeben. Jedoch ist der umgerechnete Wert inkonstant, d.h. er springt immer etwas hin und her da der Analogwert unstetig ist.

Jetzt wollte ich, um den Wert konstanter zu machen, den Mittelwert bilden. Wie mache ich das am besten? Ich brauche ja eine Art "Data Buffer" (z.B. ein INT Array) um mehrere Analogwerte in einem Zyklus zu speichern, aber ich weiß nicht wie genau ich das umsetzten soll (Bin noch ein Anfänger). Vor allem wie schaffe ich es, dass ich sagen wir mal 15 Werte in diesem Buffer abspeichere. Denke mal dass eine for-Schleife auch gebraucht wird. Der Ansatz ist da, aber ich weiß nicht wie ich das am besten bei einer Steuerung umsetzten soll.

Jede Hilfe ist willkommen und falls ihr mehr Infos braucht grad schreiben, ist mein erster Beitrag hier. :D

Gruß
Bastud
 

Anhänge

  • Quellcode.jpg
    Quellcode.jpg
    622,1 KB · Aufrufe: 63
Jetzt wollte ich, um den Wert konstanter zu machen, den Mittelwert bilden. (...) Ich brauche ja eine Art "Data Buffer" (z.B. ein INT Array) um mehrere Analogwerte in einem Zyklus zu speichern
Einen Mittelwert erhält man nicht, indem man einen Wert mehrmals im Zyklus speichert, sondern man muß in mehreren Zyklen den Wert in einen Puffer speichern. Der Puffer muß so groß sein über wieviele Zyklen man den Mittelwert bilden will.

Eine Schleife braucht man nicht für das Speichern, nur einen Ringpuffer zum Einspeichern je eines Wertes in jedem Zyklus. Über die Werte des Ringpuffers kann dann der Mittelwert berechnet werden - hier bietet sich eine Schleife an, um die Summe über alle Elemente des Puffers zu bilden.
Code:
buff : ARRAY[0..9] OF REAL;  //Ringpuffer für 10 Werte
buffindex : UINT;  // Ringpuffer Füllzeiger
i : UINT;
sum : REAL;
avg : REAL;  // Mittelwert

//neuen Wert in Ringpuffer schreiben, ältester Wert verschwindet dabei (wird überschrieben)
buffindex := (buffindex + 1) MOD 10;  // Ringpuffer-Füllzeiger auf das nächste Element setzen
buff[buffindex] := New_value;         // aktuellen Wert in Ringpuffer schreiben

//Mittelwert über Pufferwerte berechnen
sum := 0.0;
FOR i := 0 TO 9 DO
  sum := sum + buff[i];
END_FOR;

avg := sum / 10.0;

Eine Glättung kann man auch ohne Schleife erreichen, einen Beispielcode für einen gleitenden Mittelwert kannst Du bei OSCAT finden im FUNCTION_BLOCK FT_AVG
(und Beispiel Ringpuffer)

Einen Glättung kann man auch ohne Puffer-Array für das Merken der vorherigen Werte erreichen, indem man z.B. 10 Werte addiert und beim Hinzufügen eines neuen Wertes den bisherigen Mittelwert (10% der Summe) abzieht und den neuen Wert dazu addiert. Allerdings werden bei dieser Glättung große Wertesprünge nur langsam abgebaut. Das Prinzip kann man vereinfacht implementieren mit dieser Formel:

Smooth_value := Smooth_value * 0.9 + New_value * 0.1;


PS:
IF resValue < 0.0 OR resValue = 0.0 THEN resValue := 0.0;
kann man einfacher schreiben
IF resValue <= 0.0 THEN ...
oder hier reicht auch
IF resValue < 0.0 THEN ...

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Harald,

erstmal danke für die schnelle Antwort. Den Code habe ich schonmal verstanden.

Ich bin mir nicht sicher in welchen Programmabschnitt ich den Code packen muss. Der Teil mit dem Ringpuffer hätte ich in den Cyclic.st geschrieben, aber wenn ich die For-schleife für die Mittelwertbildung da auch rein schreibe klappt es nicht so wie es soll.

Ich muss der Steuerung ja auch irgendwie sagen, dass Sie nach 10 Zyklen den Mittelwert bilden soll oder?

Gruß
Bastud
 
Der Mittelwert kann und sollte immer dann neu berechnet werden wenn ein neuer Wert in den Puffer geschrieben wurde (also in jedem Zyklus). Das Speichern in den Ringpuffer und das Mittelwert berechnen also am besten direkt nacheinander machen. Es sei denn, der Mittelwert wird seltener abgefragt und verarbeitet - dann braucht er natürlich nur vor der Verarbeitung neu berechnet werden.

Wenn Du von mehreren Signalen dessen Mittelwert haben willst, dann braucht jedes Signal seinen eigenen Ringpuffer. Am besten einen FB mit Ringpuffer schreiben und für jedes Signal eine eigene Instanz des FB anlegen.

Harald
 
Ich muss der Steuerung ja auch irgendwie sagen, dass Sie nach 10 Zyklen den Mittelwert bilden soll oder?
Das darfst Du doch selbst entscheiden, ob Du z.B.
- jedesmal, wenn ein neuer Wert hinzugekommen ist, den Mittelwert über die jeweils letzten 10 Werte bildest oder
- erst dann, wenn 10 neue Werte hinzugekommen sind, den Mittelwert über die letzten 10 - noch nicht bereits zum Teil verwursteten - Werte bilden möchtest.

Wenn Du allerdings nach der Methode "neuer_Mittelwert := (n * letzter_Mittelwert + neuer_Wert)/(n+1)" mittelst, stellt sich Deine Frage eigentlich gar nicht!?
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Bastud,

B&R bietet für solche Anwendungen auch fix-fertige Bibliotheken an. Schau dir doch mal den FUB MTFilterMovingAverage aus der Bibliothek MTFilter an. Der Einsatz dieser Bibliothek ist gratis.

MTFilterMovingAverage.jpg

Sollte genau das bieten, was du benötigst.

Liebe Grüsse
Ulumulu1510
 
Wenn Du allerdings nach der Methode "neuer_Mittelwert := (n * letzter_Mittelwert + neuer_Wert)/(n+1)" mittelst, stellt sich Deine Frage eigentlich gar nicht!?

Dieser Code ist leider kein korrekter gleitender Mittelwert. Eher entspricht dies einem Tiefpass.

Folgendes Beispiel macht dies deutlich:
  1. Initialisiere n := 2 soll Mittelwert über 3 Werte ergeben, neuer_Mittelwert := 0
  2. neuer_Wert := 9'000 -> neuer_Mittelwert = 3'000 -> Vergleichsrechnung: (0 + 0 + 9'000) / 3 = 3'000 -> stimmt
  3. neuer_Wert := 0 -> neuer_Mittelwert = 2'000 -> Vergleichsrechnung: (0 + 9000 + 0) / 3 = 3'000 -> stimmt nicht
  4. neuer_Wert := 0 -> neuer_Mittelwert = 1'333 -> Vergleichsrechnung: (9000 + 0 + 0) / 3 = 3'000 -> stimmt nicht
  5. neuer_Wert := 0 -> neuer_Mittelwert = 888 -> Vergleichsrechnung: (0 + 0 + 0) / 3 = 0 -> stimmt nicht

Liebe Grüsse
Ulumulu1510
 
@Ulumulu:
wenn du das Beispiel von Heinrich korrekt angewendet hättest dann hätte es auch die korrekten Werte erbracht ...
Sorry ... kein Foto für dich ... ;)
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Dieser Code ist leider kein korrekter gleitender Mittelwert. Eher entspricht dies einem Tiefpass.
Korrekt! Das ist eine "Glättung mit sparsamen Mitteln", ohne Array, ohne FIFO. Für viele Zwecke gut geeignet, aber kein Mittelwert über eine gegebene Anzahl von Messungen.
Dafür ist MehrAufwand nicht vermeidbar.

Man kann ein wenig vereinfachen, wenn ...
- die Anzahl der Werte, über die gemittelt werden soll, immer gleich bleibt und
- man die Summe der Messwerte in den nächsten Zyklus rettet,
folgendermassen rechnen:
Code:
i := (i+1) mod n ; // n: Anzahl der zu mittelnden Werte; i: Index 0..(n-1)
WertZuvor := Wert[i] ;
Wert[i] := WertAktuell ;
Summe := Summe - WertZuvor + WertAktuell ;
MittelWert := Summe / n ;
D.h. man muss nicht beim Eintreffen eines neuen Wertes alle Werte des Array in einer Schleife neu aufsummieren.
Allerdings hat diese Abkürzung einen Haken: Zu Beginn muss man doch einmalig die Summe aller ArrayElemente bilden [SUP]1[/SUP]), um den Wert in Summe zu "synchronisieren".
Vorteilhaft ist das Verfahren, wenn über eine hohe Anzahl von Werten gemittelt werden muss und sich die Anzahl SchleifenDurchläufe störend auf die ZyklusZeit auswirken würde.

PS:
[SUP]1[/SUP]) Initialisieren aller ArrayElemente und von Summe mit 0 tut's natürlich auch.
 
Zuletzt bearbeitet:
Du kannst auch in der Konfiguration des Analogeingangs die Filterstufe einstellen.
Beschreibung siehe Seite 37.
https://download.br-automation.com/...1be0b0fb949234434e9d31999f&px-time=1602926852

Das entspricht auch der obengenannten Formel. Nur wird die CPU durch die Berechnung nicht belastet.

Code:
Formel für die Berechnung des Eingangswerts: Wert neu = Wert alt - Wert alt/Filterstufe + Eingangswert/Filterstufe
 
Zuletzt bearbeitet:
Zurück
Oben