# Mittelwertbildung aus REAL Zahlen



## Waelder (4 Januar 2006)

Hallo Kollegen,

* erstmal ein gutes neues 2005 *

ich habe mich mal durch das Forum gesucht mit "Mittelwert" finde aber nicht das richtige raus.
Es gibt zwar einige Links zu diesem Thema aber die verwenden die Mittelwertbildung aus INT siehe auch das verlinkte beispiel zur mittelwertbildung (ich bin der meinung der code ist von dort).
http://www.sps-forum.de/phpBB2/viewtopic.php?t=88&highlight=mittelwert
(PS: der Link ist tot? 03.01.2005)
geht  http://www.sps-net.de/ nicht mehr ??

Ich hab folgendes Problemchen.

Vorgaben :
Ich muss eine Wert (Real) Zahl x mal (max100) einlesen und in einen Datenbaustein speichern und daraus den Mittwelwert bilden. Gib es da keine fertigen Bausteine ?
Die gefundenen behandeln lediglich Mittelwerte aus fertigen DBs
Ich bin leider nicht fähig den beschriebenden Baustein auf Real umzutopfen.

MfG
Michael
Anhang der Baustein als Quellcode aus dem Link (ich hoffe ich hab keine rechte verletzt.) SFC20&SFC21 sind auch von nöten 


```
FUNCTION_BLOCK "Mittelwert"
TITLE =Störauslösung über Mittelwertbildung
//a) IN_Wert 
//b) IN_Anzahl
//c) IN_Grenzwert_Alarm
//d) M_IN_CPU_Neustart
//e) M_IN_Frg_Eintrag
//f) M_IN_Quitt_Alarm
//g) OUT_Letzter_Mittelwert
//h) M_OUT_Alarm
//
//Bei Freigabe e) wird ein Wert a) in ein Datenfeld eingetragen. Die 
//Datenfeldgrösse wird über b) bestimmt.
//Es wird ein Mittelwert aus dem Datenfeld ermittelt, wenn die minimale Anzahl 
//Daten b) erreicht worden ist.
//Bei Abweichung bei +/- zur Eingabe c) wird ein Alarm h) ausgegeben und das 
//Datenfeld wird gelöscht. Nach Reset des Alarms über f) können wieder Werte in 
//das Datenfeld eingetragen werden.
//Bei einem neuen Eintrag werden alle alten Daten ein Feld nach unten gerückt und 
//der neue Wert wird in dem ersten Feld eingetragen. Sollte der Alarmwert bei 
//erreichen von b) nicht erreicht werden, 'fällt' der letzte Wert aus dem 
//Datenfeld raus und der Mittelwert wird aus den neuen Einträgen gebildet.
//Weiterhin wird das Datenfeld gelöscht wenn die CPU neu anläuft d) oder sich b) 
//ändert. Der letzte ermittelte Mittelwert g) wird ausgegeben.
AUTHOR : DU
FAMILY : Multi_FB
NAME : Mittel
VERSION : 0.1


VAR_INPUT
  IN_Wert : INT ;	//Wert, der bei Frg Eintrag in das Datenfeld aufgenommen wird
  IN_Anzahl : INT ;	//Anzahl Einträge für Mittelwertbestimmung
  IN_Grenzwert_Alarm : REAL ;	//Grenzwert für Alarm
  M_IN_CPU_Neustart : BOOL ;	//M :CPU Neustart (Merker von OB 100 oder OB 101)
  M_IN_Frg_Eintrag : BOOL ;	//M :Freigabe Wert in Datenfeld eintragen
  M_IN_Quitt_Alarm : BOOL ;	//M :Quittierung Alarmmeldung
END_VAR
VAR_IN_OUT
  OUT_Letzter_Mittelwert : REAL ;	//Letzter Mittelwert zur Anzeige
  M_OUT_Alarm : BOOL ;	//M :Maschine stop und Alarm
END_VAR
VAR
  M_Null : BOOL ;	//M :"0" Merker
  M_Eins : BOOL  := TRUE;	//M :"1" Merker
  M_RF_Wert_eintragen : BOOL ;	//M :Reset Flanke Wert eintragen
  M_Flanke_Wert_eintragen : BOOL ;	//M :Flanke Wert eintragen
  M_Kein_Mittelwert_bilden : BOOL ;	//M :Kein Mittelwert bilden da Anzahl noch nicht erreicht
  Speicher_HR_Soll : INT ;	//Speicher Sollwert für Hauptrüttler
  Speicher_Anzahl_Werte : INT ;	//Speicher Anzahl Werte für Mittelwertbildung
  Eintraege : INT ;	//Anzahl Eintrage im Datenfeld
  LOOP : INT ;	//Anzahl Schleifenwiederholung
  Summe : REAL ;	//Summe aller Einträge als 'REAL' Zahl
  Akt_Mittelwert : REAL ;	//Aktueller Mittelwert als 'REAL' Zahl
  Datenbereich : ARRAY  [1 .. 100 ] OF //Mittelwert aus max. 100 Daten bilden
  INT ;	
  Speicher_Daten : ARRAY  [1 .. 100 ] OF //Speicher Daten
  INT ;	
END_VAR
VAR_TEMP
  Wert_0 : INT ;	//Wert "0"
  Nicht_genutzt : INT ;	//Nicht genutzter Bereich
  Daten_ANY : ANY ;	//Daten ANY Pointer
  Offset_ANY : ANY ;	//Offset ANY Pointer
  Speicher_ANY : ANY ;	//Speicher ANY Pointer
  Letzter_Wert : DINT ;	//Letzter Wert im Datenfeld
  Zeiger : DINT ;	//Zeiger für aktuellen Wert
END_VAR
BEGIN
NETWORK
TITLE =Null- und Einsmerker erzeugen

      CLR   ; 
      =     #M_Null; 
      SET   ; 
      =     #M_Eins; 
NETWORK
TITLE =Reset Auswertung falls Funktion aus oder Sollwertänderung
//Initialisierung falls sich das Datenfeld geändert hat, die CPU neu angelaufen 
//ist oder der Alarmlevel erreicht worden ist
      O(    ; // ODER
      L     #Speicher_Anzahl_Werte; // Hat sich die Anzahl der Werte geändert ?
      L     #IN_Anzahl; // 
      <>I   ; // 
      )     ; // 
      O(    ; // ODER
      L     #Speicher_Anzahl_Werte; // Ist die Anzahl der Werte kleiner/gleich 0 ?
      L     0; // 
      <=I   ; // 
      )     ; // 
      O     #M_IN_CPU_Neustart; // oder Neustart 
      O     #M_OUT_Alarm; // oder Alarmlevel wurde erreicht
      SPBN  RUN; // 
      L     0; // Initialisiere Datenfeld
      T     #Wert_0; // 
      T     #Eintraege; // 
      T     #Akt_Mittelwert; // 

      CALL "FILL" (// Belege den Datenbereich mit '0'
           BVAL                     := #Wert_0,// 
           RET_VAL                  := #Nicht_genutzt,// 
           BLK                      := #Datenbereich);// 
      CALL "FILL" (// Belege den Speicherbereich mit '0'
           BVAL                     := #Wert_0,// 
           RET_VAL                  := #Nicht_genutzt,// 
           BLK                      := #Speicher_Daten);// 

NETWORK
TITLE =Schreibe Wert in Datenfeld 
//Datenbereich um 1 Feld nach unten verschieben:
//Es wird über den 'MOVE' Befehl der Datenbereich um 1 Word verschoben in den 
//Speicherbereich geschoben der dann zurück in den Datenbereich geschoben wird.
//
//Datenbereich          Speicherbereich       Datenbereich
//DW 1 = 1              DW 1 = 0              DW 1 = 0
//DW 2 = 2    MOVE >    DW 2 = 1    MOVE >    DW 2 = 1
//DW 3 = 3              DW 3 = 2              DW 3 = 2
//DW 4 = 4              DW 4 = 3              DW 4 = 3
RUN:  U     #M_IN_Frg_Eintrag; // Flankenbildung bei Freigabe Eintrag
      FP    #M_RF_Wert_eintragen; // 
      =     #M_Flanke_Wert_eintragen; // *** Flanke Wert eintragen
      U     #M_Flanke_Wert_eintragen; // 
      UN    #M_OUT_Alarm; // 
      SPBN  ENDE; // 

      L     #Eintraege; // Anzahl Einträge in Datenfeld
      +     1; // addiere 1
      T     #Eintraege; // 
      L     #IN_Anzahl; // Anzahl Worte
      <I    ; // kleiner ?
      =     #M_Kein_Mittelwert_bilden; // dann kein Mittelwert bilden
      U     #M_Kein_Mittelwert_bilden; // 
      SPB   ANY; // 
      T     #Eintraege; // sonst Eintagszähler auf 'Anzahl' begrenzen

ANY:  LAR1  P##Daten_ANY; // Anfangsadresse der Datenquelle
      L     B#16#10; // Syntax-ID in den Pointer laden  
      T     LB [AR1,P#0.0]; // 
      L     B#16#4; // Bereichstype 'Word' in den Pointer laden
      T     LB [AR1,P#1.0]; // 
      L     #IN_Anzahl; // Anzahl Worte in den Pointer laden
      T     LW [AR1,P#2.0]; //  
      L     0; // DI als Quell-DB in den Pointer laden
      T     LW [AR1,P#4.0]; // 
      L     P##Datenbereich; // Pointer auf Array 'Datenbereich' setzen
      T     LD [AR1,P#6.0]; // 

      LAR1  P##Offset_ANY; // Anfangsadresse der Offsetquelle
      L     B#16#10; // Syntax-ID in den Pointer laden  
      T     LB [AR1,P#0.0]; // 
      L     B#16#4; // Bereichstype 'Word' in den Pointer laden
      T     LB [AR1,P#1.0]; // 
      L     #IN_Anzahl; // Anzahl Worte in den Pointer laden
      T     LW [AR1,P#2.0]; //  
      L     0; // DI als Quell-DB in den Pointer laden
      T     LW [AR1,P#4.0]; // 
      L     P##Speicher_Daten; // Pointer auf Array 'Speicher_Daten' setzen
      +     16; // Offset 1 Word weiter
      T     LD [AR1,P#6.0]; // 

      LAR1  P##Speicher_ANY; // Anfangsadresse der Speicherquelle
      L     B#16#10; // Syntax-ID in den Pointer laden  
      T     LB [AR1,P#0.0]; // 
      L     B#16#4; // Bereichstype 'Word' in den Pointer laden
      T     LB [AR1,P#1.0]; // 
      L     #IN_Anzahl; // Anzahl Worte in den Pointer laden
      T     LW [AR1,P#2.0]; //  
      L     0; // DI als Quell-DB in den Pointer laden
      T     LW [AR1,P#4.0]; // 
      L     P##Speicher_Daten; // Pointer auf Array 'Speicher_Daten' setzen
      T     LD [AR1,P#6.0]; // 


      CALL "BLKMOV" (// Kopiere Datenbereich in Speicherbereich,
           SRCBLK                   := #Daten_ANY,// ein Datenword tiefer (Offset = 1 Word)
           RET_VAL                  := #Nicht_genutzt,// 
           DSTBLK                   := #Offset_ANY);// 
      CALL "BLKMOV" (// Kopiere Speicherbereich zurück in Datenbereich
           SRCBLK                   := #Speicher_ANY,// 
           RET_VAL                  := #Nicht_genutzt,// 
           DSTBLK                   := #Daten_ANY);// 

      LAR1  P##Datenbereich; // Aktuellen Wert in 1. Datenstelle eintragen
      L     #IN_Wert; // 
      T     DIW [AR1,P#0.0]; // 

      L     P##Datenbereich; // Zeiger auf Pointer erste Adresse im Datenfeld
      T     #Zeiger; // 

      U     #M_Kein_Mittelwert_bilden; // Keine Mittelwertberechnung
      SPB   ENDE; // 

      L     0.000000e+000; // Initialisiere Summe
      T     #Summe; // 
      L     #IN_Anzahl; // Schleife mit Anzahl Werte belegen
NEXT: T     #LOOP; // *** START LOOP
      L     #Summe; // Bilde Gesamtsumme aus Datenfeld
      L     DIW [#Zeiger]; // 
      ITD   ; // Wandeln in Realzahl
      DTR   ; // 
      +R    ; // 
      T     #Summe; // *** Neue Gesamtsumme

      L     #Zeiger; // Lade Zeiger
      +     16; // 1 Word weiter
      T     #Zeiger; // 

      L     #LOOP; // 
      LOOP  NEXT; // *** END LOOP

      L     0.000000e+000; // Vergleiche Gesamtsumme auf grösser '0'
      L     #Summe; // 
      <=R   ; // 
      SPB   DIV; // 
      NEGR  ; // Bei negativen Mittelwert wandel in positiv
DIV:  L     #IN_Anzahl; // 
      ITD   ; // Wandle Anzahl zu Realzahl
      DTR   ; // 
      /R    ; // Teile Summe aller Werte durch Anzahl
      T     #Akt_Mittelwert; // *** Mittelwert
      T     #OUT_Letzter_Mittelwert; // *** Letzter Mittelwert zur Anzeige
NETWORK
TITLE =Auslösen der Störmeldung

ENDE: L     #Akt_Mittelwert; // Bei Mittelwert grösser/gleich
      L     #IN_Grenzwert_Alarm; // Grenzwert Alarm
      >=R   ; // 
      U     #M_Flanke_Wert_eintragen; // 
      UN    #M_Kein_Mittelwert_bilden; // 
      S     #M_OUT_Alarm; // *** Alarm Grenzwert erreicht
      U     #M_IN_Quitt_Alarm; // 
      UN    #M_Flanke_Wert_eintragen; // 
      R     #M_OUT_Alarm; // *** Reset Alarm

      L     #IN_Anzahl; // Lade akt. Anzahl
      T     #Speicher_Anzahl_Werte; // in Speicher
END_FUNCTION_BLOCK
```


----------



## Markus (4 Januar 2006)

also hab mir den baustein mal angeschaut.

1. da fehlt die addition von ar2 -> er ist also kein multi-fb
gerettet werden die ar auch nicht

2. wenn du den baustein auf real umstellst hast du eben das problem das dir die variable für die summe überlaufen kann.
wie groß sind den die werte?

schau dir mal das real format in der s7 hilfe an.

wenn es sich um kleinere werte handelt wie zb. skalierte analogwerte, dann sollte es kein problem sein.


wo hast du probleme bei der umstellung auf real?
bin jetzt grad etwas zu faul den ganzen baustein zu übersetzen...


----------



## volker (4 Januar 2006)

suche mal nach schnitt ;-)

auf meiner hp findest du eine fc db_auswerten. der ist zwar auch nur für int aber sehr leicht anzupassen.

d.h. ich hab den eben mal angepasst. werde ich heute nachmittag auf meine hp stellen.

was das schreiben in den db betrifft schau dir mal den fifo auf meiner hp an.


----------



## Ingo dV (6 Januar 2006)

Das ist ja was, habe den Baustein mal von 3 Jahren oder so beim SPS-Net eingestellt um ein Bsp. für indirekte Adressierung zu zeigen (nicht als Multi-FB    )...

Die Mittelwertbildung  aus einen Datenfeld befindet sich hier:



> L     0.000000e+000; // Initialisiere Summe
> T     #Summe; //
> L     #IN_Anzahl; // Schleife mit Anzahl Werte belegen
> NEXT: T     #LOOP; // *** START LOOP
> ...



Dabei muss noch '#Zeiger' auf den Anfang des Feldes initialisiert werden.
Für die Mittelwertbildung aus Realwerten müsst du nur dein Datenfeld als Real anlegen, die ITD und DTR's löschen und die +16 in +32 ändern.
Und wie Marcus schon gesagt hat, musst du die Überlaufgrenze beachten dies ist bei Real:

-3.402822E+38 bis -1.175495E-38


----------



## SPS.bz (8 Januar 2006)

*FC zum Mittelwertberechnung für die S7*

Hallo und ein frohes neues Jahr!

Auch ich musste schon häufiger Mittelwertberechnungen programmieren. Ich würde dir dazu ein FC erstellen. Schreib mir einfach mal (per Posting, PN oder per Email [siehe mein Profil]), wie schnell du den Baustein brauchst, dann quetsche ich das zwischen meine Arbeit.


----------



## Waelder (9 Januar 2006)

*Meine Lösung*

Ich hab mich wohl ein bisschen im DB festgebraten.. mit genauem Überlegen wäre ich auch draufgekommen.
Mein FB (oder FC) macht folgendes :
Addiere eine vorgegebene Anzahl von Messwerten, dividiere die Summe der Messwerte durch die vorgenommene Anzahl der Messungen = Mittelwert.
Der Baustein nimmt nur soviel Werte auf wie vom Benutzer gewünscht, blockt anschliessend weitere Eingaben.
Hat er die gewünschte Anzahl erreicht, so gibt er ein Signal ab, dass vom Benutzer zurückgesetzt werden muss. Vohrher sind keine weiteren Eingaben möglich.
Der Eingang Abbruch wie gesagt bricht alles Ab Werte=0

Ich hoffe mich nicht zu sehr zu verprogrammiert zu haben.

Quellcode :

```
FUNCTION_BLOCK "MWERT"
TITLE =
VERSION : 0.1


VAR_INPUT
  Wert : REAL ;	//Eingelesener Wert
  Anzahl_Wert : INT ;	//Anzahl der einzulesenden Werte
  Puls : BOOL ;	//Puls zum Zählen
  Abbruch : BOOL ;	//Abbruch der Messung = Reset
END_VAR
VAR_OUTPUT
  Summe : REAL ;	//Summe
  M_Wert : REAL ;	//Mittelwert
END_VAR
VAR_IN_OUT
  Fertig : BOOL ;	//Rechnung fertig (muss zurückgesetzt werden)
END_VAR
VAR
  Messungen : INT ;	//Anzahl der Messungen
  Divisor : REAL ;	//Divisor
  Div_DI : DINT ;	//Divisor dint
  Flanke_Puls : BOOL ;	//Flanke Impulseingang
  Flanke_Wertio : BOOL ;	//Flanke Wert erreicht
  Flanke_Werte_0 : BOOL ;	//Werte auf null setzen
END_VAR
BEGIN
NETWORK
TITLE =

//Abbruch
      U     #Abbruch; 
      SPBN  STOP; //Wenn 0= Sprung
      L     0.000000e+000; //Werte auf 0
      T     #Messungen; 
      T     #Summe; 
      T     #M_Wert; 
      L     0; 
      T     #Messungen; 
      R     #Fertig; 
      SPB   ABBR; //Wenn 1= Sprung Rechnungen übergehen
STOP: NOP   0; 

//Wenn Messungen erreicht, dann springe nach Ende

      L     #Messungen; 
      L     #Anzahl_Wert; 
      >=I   ; 
      FP    #Flanke_Wertio; 
      S     #Fertig; 
      U     #Fertig; 
      SPB   M_IO; //Wenn 1= Sprung

//Rechnungen  für Mittelwert
      U     #Puls; 
      FP    #Flanke_Puls; 
      SPBN  NOAD; //Wenn 0= Sprung

//Zähle Messung hoch
      L     1; 
      L     #Messungen; 
      +I    ; 
      T     #Messungen; 

      L     #Wert; 
      L     #Summe; 
      +R    ; 
      T     #Summe; 

//Divisor (Anzahl der Messungen) im Gleitpunkt errechnen
      L     #Messungen; 
      ITD   ; 
      T     #Div_DI; 
      DTR   ; 
      T     #Divisor; 

//Errechne Mittelwert      
      L     #Summe; 
      L     #Divisor; 
      /R    ; 
      T     #M_Wert; 

M_IO: NOP   0; 
NOAD: NOP   0; 
ABBR: NOP   0; 

      U     #Fertig; 
      FN    #Flanke_Werte_0; 
      SPBN  A001; 
      L     0.000000e+000; //Werte auf 0
      T     #Messungen; 
      T     #Summe; 
      T     #M_Wert; 
      L     0; 
      T     #Messungen; 

A001: NOP   0; 



NETWORK
TITLE =



END_FUNCTION_BLOCK
```


----------

