# Frequenzauswertung/Prognose



## McNugget (7 September 2009)

Hallo allerseits.

Wie kann ich mit dem Signal einer Lichtschranke, die auf eine Lochscheibe auf einer Motorachse schaut, die Aktualgeschwindigkeit Pulse pro Minute berechnen?

Es ist mit ca. 800 bis 1000 Pulsen pro Minute zu rechnen.


Gruss

McNugget


----------



## Ralle (7 September 2009)

Kommt ein wenig darauf an, wie genau es sein soll und wie schnell eine Änderung der Geschwindigkeit angezeigt werden soll.

Als einfachste Variante kannst du 1 Minute lang die Impulse zählen, dann hast du die Impulse/Minute.
Oder zu zählst die Impulse genau 1 Sekunde lang und multiplizierst sie mit 60, dann hast du auch wieder Impulse/Minute. Das ist dann zwar ungenauer, aber dafür wird jede Sekunde eine neue aktuelle Geschwindigkeit angezeigt.

Ist das alles zu ungenau, gibts auch noch andere Methoden, aber die sind dann abhängig vom Anwendungsfal und sicher auch aufwendiger.


----------



## McNugget (7 September 2009)

Hallo Ralle.

Es muss nicht exorbitant genau sein, aber ich hätte gerne schnell die Werte.

Da die Lochscheibe ja immer die gleichen hell/dunkel-Abstände hat, habe ich mir gedacht, 2-5 hell und dunkel Zeiten zu nehmen, den Mittelwert und die Abweichung zu rechnen und dann auf eine Minute hochzurechnen.

Aber wie mache ich es, dass immer die aktuellsten Werte wieder in die Berechnung einfliessen? Man müsste ja die Werte sozusagen immer wieder in einer Tabelle durchschieben. (Bin leider alles andere als der König im Umgang mit Arrays.)

Wenn ich eine Minute lang betrachte und sich dann die Geschwindigkeit ändert, dauert es mindestens eine Minute in der neuen Geschwindigkeit um den neuen wert zu ermitteln. Das wäre irgendwie doch nicht elegant..

Gruss

McNugget


----------



## Ralle (7 September 2009)

Ja genau, daher muß man sehen, was man braucht. Ich weiß nicht so genau, wie schnell deine Beckhoff die Impulse erfassen kann.

1. Du kannst entweder Impulse zählen über einen festen Zeitraum, wobei dieser Zeitraum dann deinen kürzeste "Aktualisierungszeit" darstellt.

2. Du nimmst die Zeit zwischen 2 Impulsen und rechnest das um. Ist die SPS nicht schnell genug, kannst du auch die Zeit zwischen 2, 3, 4 ... Impulsen messen. Dann variiert die Aktualisierungszeit, je langsamer das Rad, desto länger die Aktualisierungszeit. Je schneller das Rad, desto kürzer die Aktualisierungszeit, aber desto ungenauer wohl auch das Ergebnis.

Das mit dem Array und die Werte die Werte da durchschieben, so in einer Art gleitendem Mittelwert bedeutet ja im Endeffekt auch, daß eine Änderung der Geschwindigkeit am Impulsrad nur langsam in die Ausgabe der Geschwindigkeit eingeht. 

Also ich würde mal mit Variante 2 anfangen. Einen Zähler, der z.Bsp. 4 Impulse zählt und  einen Timer der die Zeit dazwischen mißt. das kann man dann in Impulse/Minute umrechnen. Wichtig ist, daß die LS und die Inputs auch schnell genug sind, aber bei 1000 Imp./Minute sollte das noch gehen. Je schneller die sind, und je kürzer die Zykluszeit der SPS, je genauer wird das ganze natürlich auch bei wenig ausgemessenen Impulsen.


----------



## McNugget (7 September 2009)

Habe Wago.

Die Klemmen können bis 3ms runter, meine ich.

Wie würde ich Variante 2 (gefällt mir auch besser) in ST machen?

Gruss

McNugget


----------



## Ralle (8 September 2009)

Das kann man natürlich auf viele Arten machen.

ich habs mal so probiert, zur Veranschaulichung und zum selbst üben :
Folgende Lib noch einbinden: TCSystem

einen FB anlegen --> FB_Zeitmessung_zwischen_Impulsen und
im Deklarationsteil:


```
FUNCTION_BLOCK FB_Zeitmessung_zwischen_Impulsen
VAR_INPUT
	bImpuls: BOOL;
	iImpulsanzahl: INT;
END_VAR
VAR_OUTPUT
	iImpulseproMinute: DINT;
END_VAR
VAR
	fbR_Trig: R_TRIG;
	iImpulszaehler: INT;
	fbGetSystemTime: GETSYSTEMTIME;
	iLowTimeNew, ILowTimeOld: UDINT;
	iImpulsTime: DINT;
END_VAR
```
und im Codeteil:


```
fbR_TRIG(CLK:=bImpuls , Q=>);

IF fbR_TRIG.Q THEN
	iImpulszaehler := iImpulszaehler + 1;
END_IF;

IF iImpulszaehler >= iImpulsanzahl THEN
	fbGETSYSTEMTIME(timeLoDW=>iLowTimeNew , timeHiDW=> );
	iImpulsTime := UDINT_TO_DINT(iLowTimeNew - iLowTimeOld) / (10000 * iImpulsanzahl); (* iImpTime in ms*)
	iImpulseproMinute := 60000 / ( iImpulsTime); (*Umrechnungin Impulse/Minute*)
	iLowTimeOld := ILowTimeNew;
	iImpulszaehler := 0;
END_IF;
```

Aufruf:


```
fbZeitmessungzwischenImpulsen(bImpuls:=bMyImpuls , iImpulsanzahl:=2 , iImpulseproMinute=>iMyImpulseproMinute );
```

wobei ich folgende globale Variablen definiert habe:


```
VAR_GLOBAL
	bMyImpuls: BOOL;
        iMyImpulseproMinute: DINT;
END_VAR
```

Der allererste Aufruf bringt hier natürlich immer ein falsches Ergebnis, da hier iLowTimeOld noch 0 ist oder einen alten Wert hat.
Dafür kann man sich im Notfall auch noch etwas einfallen lassen.


----------



## McNugget (8 September 2009)

Hallo Ralle, danke für Deine Mühe.

Ich habe allerdings Wago und die TCSystem.lib scheint eine Beckhoff Lib zu sein.
Was gibt die aufgerufene Zeitfunktion genau aus?
Ich muss das ja irgendwie mit OSCAT oder Wago-Libs bewältigen.



Gruss

McNugget


----------



## MasterOhh (8 September 2009)

GetSystemTime gibt in TwinCat den Systemzeitstempel als 64bit Wert mit einer 100ns Genauigkeit aus. 
Bei  Wago müsste es doch mindestens was gleichwertiges geben.


----------



## McNugget (8 September 2009)

Danke für die Info.

Leider wüsste ich eben nicht, was bei Wago adäquat wäre.

Wie sieht das Datenformat den ganz genau aus?

Oder weiss jemand eine Wago Lib. die passen würde?

Gruss

McNugget


----------



## repök (8 September 2009)

Mit der Funktion TIME() bekommst du die laufzeit des systems in ms als dint.


----------



## McNugget (8 September 2009)

Danke Repök.

Ich habe es jetzt so angepasst:


bR_TRIG(CLK:=bImpuls , Q=>);

IF fbR_TRIG.Q THEN
    iImpulszaehler := iImpulszaehler + 1;
END_IF;

IF iImpulszaehler >= iImpulsanzahl THEN
    iLowTimeNew:=DINT_TO_UDINT (TIME());
    iImpulsTime := UDINT_TO_DINT(iLowTimeNew - iLowTimeOld) / (10000 * iImpulsanzahl); (* iImpTime in ms*)
    iImpulseproMinute := 60000 / ( iImpulsTime); (*Umrechnungin Impulse/Minute*)
    iLowTimeOld := ILowTimeNew;
    iImpulszaehler := 0;
END_IF;


Dabei bekomme ich die Fehlermeldung:

Kann Time nicht in DINT konvertieren.

Was mache ich noch falsch?



Gruss

McNugget


----------



## repök (8 September 2009)

McNugget schrieb:


> Danke Repök.
> 
> Ich habe es jetzt so angepasst:
> 
> ...



Ich hatte eben geschrieben, dass die laufzeit als dint ausgegeben wird. Ist im prinzip richtig, aller dings im time-format. die konvertierung muss noch ngepasst werden. alle 49,7... tage bekommst du einen überlauf, da dann die 32bit voll sind.


----------



## MasterOhh (8 September 2009)

Ist dein Motor langsamg genug / bzw die Zykluszeit der SPS kurz genug damit dein Programm mit dem Zählen der Impulse hinterher kommt?

Außerdem glaube ich, dass Time() nur 1ms Auflösung hat. Dann kannst du dir die 10000 bei der Impulszeit-berechnung sparen.


----------



## McNugget (8 September 2009)

ich gebe ja zu, dass ich Einsteiger bin, daher würden mir etwas detailliertere Ausführungen sehr viel weiterhelfen.

Ich habe noch diverse Probleme mit den diversen Typen und deren Konvertierung.
Wann benutzt man welchen Datentyp wofür? Gibt es darüber ein Tut oder einen Leitfaden für Dummies?

Ich habe jetzt einen Lösungsnasatz mit einer Bibliothek, an die ich nicht rankomme und einen, in dem ich die Typen nicht verstehe/konvertieren kann.


Wo ist Time() beschrieben?


@Repök: Wie müsste ich denn die Konvertierung anpassen?

@MasterOhh: Ich weiss es aktuell noch nicht, icha habe einen Wago-Controller schon mit unter 18 ms Zykluszeit gehabt, es könnte also klappen. Da ich aber noch keinerlei brauchbaren Ansatz habe, weiss ich nicht, wie ich rausfinden kann, ob meine Annahmen stimmen.



Gruss

McNugget


----------



## Ralle (8 September 2009)

Ich kenne mich mit Wago gar nicht aus, aber geh mal in das Hilfesystem der Programmierumgebung und suche nach "Konvertierung" oder "Umwandlung" oder "Typkonvertierung" oder "Typumwandlung".

Wann benutzt man welchen Datentyp? Na ja, das hat sicher was mit Erfahrung zu tun, aber i.d.R. nutzt man den Datentyp, in den die Zahlen, mit denen man gerade operiert auch problemlos hineinpassen. Hat man nur positive Werte, nimmt man einen vorzeichenlosen, hat man kleine Zahlen, reicht ein INT, große Zahlen benötigen eher einen DINT. Ist man unsicher, am Anfang, nimmt man eher die größeren Datentypen, also DINT für ganze Zahlen mit oder ohne Vorzeichen, dafür verbraucht man etwas mehr Speicherplatz. Nun ist es so, daß man verschiedene Standarfunktionen nutzt, die das System mitbringt oder z.Bsp eine Library wie die Oscat-Library. Deren Funktionen verlangen nach bestimmten Eingabe- und Ausgabetypen, die wurden halt beim erstellen der Funktion vom Programmierer so festgelegt. Will also eine Funktion einen DINT als Input, du hast aber bisher mit INT gerechnet, dann mußt du diese INT nach DINT konvertieren, damit die Funktion diese Variable als Eingabewert akzeptiert.


----------



## McNugget (10 September 2009)

So. 

Ein kleiner Nachtrag.

@MasterOhh: Die GEschwindigkeit reicht vollends um die Pulse auszuwerten! ;-)

@Ralle: Vielen Dank, dass Du mir das noch mal etwas nöher gebracht hast. Gibt es die verscheidenen Typen irgendwo als Tabelle?

Ich habe den Code-Teil jetzt folgendermassen am Laufen:



> fbR_TRIG(CLK:=bImpuls , Q=>);
> 
> IF fbR_TRIG.Q THEN
> iImpulszaehler := iImpulszaehler + 1;
> ...



Vielen Dank an alle, die mich hier unterstützt/mit Infos gefüttert haben.

Ich habe gaaanz sicher bald wieder die ein oder andere dumme Frage.

Gruss

McNugget


----------



## Ralle (10 September 2009)

McNugget schrieb:


> @Ralle: Vielen Dank, dass Du mir das noch mal etwas nöher gebracht hast. Gibt es die verscheidenen Typen irgendwo als Tabelle?



Z.Bsp. hier: http://infosys.beckhoff.com/index.p...ol/html/tcplcctrl_plc_data_types_overview.htm

Das Beckhoff-Informationsystem ist ganz gut für so etwas. Da Codesys genutzt wird, hilft es bei manchen Sachen auch bei einer Anderen SPS weiter.


----------



## McNugget (11 September 2009)

@Ralle: Super Hinweis. Das hat mir schon wieder weitergeholfen.

@All:

Ich habe noch mal die OSCAT durchgesucht und etwas gefunden, was ich dann gleich für den Baustein verwenden konnte.

Vielleicht mag sich ja mal jemand den Code anschauen und mir sagen, ob das so elegant gelöst ist, oder ob es noch etwas performanenter laufen könnte.

Ich rufe diesen Baustein als eigenen Task im 5ms-Takt  auf, damit er wirklich schnell sein kann.
Aktuell braucht er maximal (nur Start) 526us
und minimal 30us
Aktualwerte schwanken bis 50us.

Deklaration:


> PROGRAM P_Pulse_Pro_Minute
> 
> VAR_INPUT
> bImpuls: BOOL;
> ...





Anweisungsteil:


> fbR_TRIG(CLK:=bImpuls , Q=>);
> 
> steht := NOT bImpuls;
> 
> ...




Bitte um Kritik und Verbesserungen. (Ich will ja was lernen.)

Gruss

McNugget


----------



## Ralle (11 September 2009)

Sieht doch ganz gut aus. Nun hast du ja eine Funktion in der Wago gefunden, die die Zeit hinreichend genau bringt. Auch den Stillstand hast du eingebaut, damit die Anfangswerte nicht völlig falsch sind.


----------



## McNugget (11 September 2009)

Die Funktion habe ich in der OSCAT.LIB gefunden. 

Danke auch an dieser Stelle mal an alle Entwickler der OSCAT.Lib.

Den Stillstand habe ich eingebaut, da die Geschwindigkeitsanzeige (also bei Ausbleiben der Takte) auf dem letzten gemessenen Wert blieb.

Vielen Dank noch mal für die Hilfe.



Gruss

McNugget


----------



## McNugget (14 September 2009)

Guten Morgen allerseits.

Zu dem bereits gelösten Problemchen habe ich nun noch eine Erweiterung.

Man muss sich das System, das ich hier umzusetzen versuche, als Endlosförderband vorstellen.

In regelmässigen Abständen sind Mitnehmer, die detektiert werden.

An einer Stelle ist ein zusätzlicher Mitnehmer auf halbem Abstand der anderen Mitnehmer.

Sobald dieser Mitnehmer kommt, soll der Mitnehmerzähler auf Null gesetzt werden, und die Zählung soll erneut beginnen.

Zudem soll der Rundenzähler um 1 erhöht werden.

Ich habe es jetzt so gemacht, dass ich eine prozentuale Zeitabweichung zwischen den Mitnehmersignalen definiere und dies Abfrage, aber das funktionier nicht, weil das System immer die aktuelle Passe heranzieht.

Wahrscheinlich wäre ein Array besser, um Schwankungen besser "schlucken" zu können. (Also Array-Mittelwert)

Stehe leider noch mit Arrays auf Kriegsfuss. 

Wichtig ist vielleicht auch noch zu wissen, dass die Anlage per Frequenzumrichter stufenlos in der Geschwindigkeit verstellt werden kann. Daher sollte es eben bei diesen timings eine gewisse Abweichungstoleranz geben. 

Ich habe den vorhandenen Code mal folgendermassen angepasst:

Deklaration:


> FUNCTION_BLOCK FB_Impulsabstand
> 
> VAR_INPUT
> bImpuls: BOOL;
> ...


Anweisungsteil:


> fbR_TRIG(CLK:=bImpuls , Q=>);
> Index_rst :=FALSE;
> 
> IF fbR_TRIG.Q THEN
> ...


Wie mache ich es besser/optimal die Runden korrekt zu zählen?



Weitere Problemstellungen wären eine Warnung, wenn mal ein Mitnehmer fehlt bzw. nicht gesehen wird und eine Warnung bei schwankender Geschwindigkeit.


Ach ja: Nein, dies ist keine Aufgabe für die Schule. ;-)
Ich möchte nur lernen/umsetzen.




Gruss

McNugget


----------



## MasterOhh (14 September 2009)

Du könntest auch einfach die Anzahl der Mitnehmer zählen und wenn alle durch sind, die den Wert wieder auf Null setzten und den Rundenzähler um eins erhöhen. 
Damit wäre zwar dein Problem mit den fehlerhaft detektierten Mitnehmern nicht gelöst, aber diese Variante wäre auf jeden Fall robuster als die zeitlichen Abstände zwischen den einzelnen Mitnehmern zu messen. Wenn nämlich gerade der eine Mitnehmer mit halben Abstand nicht erfasst wird, fehlt dir eine ganze Runde.

Ist das die selbe Anlage für die du auch die Drehzahlmessung für den Motor realisiert hast? Ich kenne mich bei Förderanlagen nicht wirklich aus (wg. Schlupf etc.) aber vielleicht wäre es ja möglich deine Rundenzahl über die Motorumdrehungen zu bestimmen......


----------



## McNugget (14 September 2009)

Hallo Master. 

Ja, es ist die selbe Anlage.

Ich versuche eine alte Steuerung abzulösen/ zu optimieren.

Daher muss ich mich leider auch an einige alte konstruktive Gegebenheiten halte, da ich sanft migrieren muss. Ich bin da leider sehr gebunden, da einige Einzelheiten für meinen Sonderfall auch schon recht gut gelöst sind.

Das Thema mit den fehlenden Mitnehmern ist so wichtig, dass ich es wie angegeben lösen muss.

Es wird nämlich für jeden Mitnehmer auch ein Gewicht übergeben.

Das mit der fehlenden Runde bei nicht gesehene "Index"-Mitnehmer könnte ich evtl kompensieren, da die Mitnehmererfassung mehrmals stattfindet. Das heisst, wenn es gut programmiert ist, würde ich nur einen Teil der Runde verlieren.

Aktuell ist die alte Steuerung so daneben, dass ich mindestens 3 (!) Runden verliere, wenn etwas nicht optimal läuft.

Zudem kann die Anlagenkonfiguration auch mal geändert werden. Da macht es Sinn, dass man den "Index"-Mitnehmer sucht und dann eine Initialisierungsrunde fährt, um mal alle durchzuzählen und als aktuellen Konfigurationswert festzulegen. Es geht hier leider nicht nur um 10 oder 20 Mitnehmer.


Gruss

McNugget


----------



## McNugget (14 September 2009)

Hat jemand eine Idee/Anregung?

Oder sollte ich evtl ein eigenes Thema für das neue "Problemchen" erstellen?


----------

