und zwar arbeite ich schon länger an einem Projekt.
Ich gebe an den Port A0 eine Spannung von 0-5V und möchte über eine Regelstrecke am Ausgang dann auch wieder 0-5V abgreifen nur über eine PT1-Strecke zeitverzögert.
Mein Problem ist allerdings, dass ich nicht genau weiß, wie ich diese Regelstrecke programmieren soll.
Strobelix:
und zwar arbeite ich schon länger an einem Projekt.
Ich gebe an den Port A0 eine Spannung von 0-5V und möchte über eine Regelstrecke am Ausgang dann auch wieder 0-5V abgreifen nur über eine PT1-Strecke zeitverzögert.
An welchen Ausgang möchtest du denn die Spannung ausgeben ?
Ist dir bekannt, dass der Arduino nur "High oder Low" also 5 Volt oder 0 Volt ausgeben kann.
Auf Wunsch geht das auch per PWM.
Das Ausgangssignal gebe ich über ein PWM-Signal aus. Die externe Beschaltung habe ich alles.
Mir fehlt nur die Programmierung der Regelstrecke, dass mein Eingangssignal zeitverzögert hinten am Ausgang abgreifen kann.
Okay..bei diesem Programm wird quasi mein Eingangssignal nur zeitverzögert am Ausgang als PWM-Signal ausgegeben?!
Für meine Anwendung hast du aber mit dem ersten Programm den richtigen Vorschlag gebracht.
Ich muss über ein PTx Glied mein Eingangssignal an den Ausgang geben. Da habe ich mich für eine PT1-Strecke entschieden, also passt glaub dein erster Vorschlag oder sehe ich das falsch?
die zuerst vorgeschlagene Funktion stellt ein PT1-Glied dar.
digitale Filter leben davon, dass sie regelmäßig aufgerufen werden.
am besten du packst die AD-Scan mit in die Funktion rein, oder du nimmst den Teil “if (millis()-lastRun < Periode) return;” aus der Funktion raus und stellst Funktion und analogRead in eine Zeitscheibe.
Selbstredend, dass keinerlei delays verwendet werden dürfen.
Mit “Zeitscheibe” meint er, dass das Filtern um so “richtiger” ist, je regelmäßiger deas Ganze gerechnet wird.
Das macht bisher die Zeile if (millis()-lastRun < Periode) return false; Wenn zu früh, erstmal nichts machen.
Hab mir mal erlaubt, das Messen in das Filtern zu integrieren:
/**********************************************************
** Funktion MessenUndFiltern() (c) GuntherB 2014 - 2016
***********************************************************
** Bildet einen Tiefpassfilter (RC-Glied) nach.
**
** Kanal: der Messkanal ( A0 ... )
** Tau :[ms] FilterFaktor
** Periode:[ms] neuer Wert alle Periode
**
** FiltVal (In/Out) der gefilterte Wert
*
** return: true wenn nach Ablauf der Periode FiltVal einen neuen Wert hat
************************************************************/
bool PT1Messung(float &FiltVal, byte Kanal, unsigned long Periode, unsigned long Tau){
static unsigned long lastRun = 0;
unsigned long FF = Tau / Periode;
unsigned int neuWert = analogRead(Kanal);
if (millis()-lastRun < Periode) return false;
if (lastRun == 0) FiltVal = neuWert;
FiltVal= ((FiltVal * FF) + neuWert) / (FF+1);
lastRun = millis();
return true;
}
void setup() { }
const byte PWMOut = 9;
const byte AnalogIn = A0;
void loop() {
static float FiltVal;
if (PT1Messung(FiltVal, AnalogIn, 10, 1000) )
analogWrite(PWMOut, (int)FiltVal / 4) ; // PWM auf 0 .. 255 skalieren
}
nur compiliert, nicht getestet.
Eigentlich braucht es das if in loop nicht, denn den gleichen Wert nochmal überschreiben sollte bei
analogWrite nicht viel ausmachen…
hilfreich wäre es wenn ich wüsste was hier jede Programmzeile genau macht, weil hier Sachen dabei sind, die mir gerade noch ein wenig fremd sind! Würde das gehen, dass du mir das dahinter schreibst?!
Sorry…
Wie in c generell programmiert wird und was Variable und Datentypen sind, ist dir ungefähr klar?
Falls ja, kannst du z.B. anhand von Zahlenwerten nachvollziehen, was hier gerechnet wird?
Falls nein: = ist eine Zuweisung des Ergebnisses des Ausdrucks rechts an die Variable links / ist das Divisionszeichen ...
(sorry, kann schlecht abschätzen, wie tief deine Frage zu verstehen ist... )
Sonst such erstmal in der Referenz Informationen zu den verwendeten Funktionen
Es wird eine Funktion PT1Messung() definiert.
in der wird alle Periode millisekunden ein Wert aus dem AD-Pin Kanal gelesen und mit einem Tiefpass (PT1-Glied) mit der Grenzfrequenz 1/Tau gefiltert und in der Variablen FiltVal gespeichert.
if (millis()-lastRun < Periode) return false; Hier wird geprüft, ob die Zeit bis zur nächsten Berechnung schon gekommen ist. Wenn nicht, wird die Funktion sofort verlassen und false zurückgegeben.
FiltVal= ((FiltVal * FF) + neuWert) / (FF+1);
Das ist das PT1-Glied.
z.B.: Tau = 990ms; Periode = 10ms; => Filterfaktor FF = 99
Formel:
Neuer Filterwert = (alter Filterwert * 99+ neuer Messwert) / 100
Bei jeder Messung nehme ich 99% des alten Wertes und addiere den neuen dazu.
Das sorgt für eine starke Filterung, weil sich “ausreisser” nicht so stark bemerkbar machen.
/**********************************************************
** Funktion PT1Messung()
***********************************************************
** Bildet einen Tiefpassfilter (RC-Glied) nach.
**
** Kanal: der Messkanal ( A0 ... )
** Tau :[ms] Tau des Tiefpasses
** Periode:[ms] neuer Wert alle Periode ms (Tau >> Periode!!)
**
** FiltVal (In/Out) der gefilterte Wert
*
** return: true wenn nach Ablauf der Periode FiltVal einen neuen Wert hat
************************************************************/
bool PT1Messung(float &FiltVal, byte Kanal, unsigned long Periode, unsigned long Tau){
static unsigned long lastRun = 0;
unsigned long FF = Tau / Periode; //
if (millis()-lastRun < Periode) return false;
unsigned int neuWert = analogRead(Kanal);
FiltVal= ((FiltVal * FF) + neuWert) / (FF+1);
lastRun = millis();
return true;
} // ende Funktion PT1Messung
//******* Pindefinitionen **************
const byte PWMOut = 9;
const byte AnalogIn = A0;
void setup() {
pinMode(PWMOut, OUTPUT);
}
void loop() {
static float FiltVal;
if (PT1Messung(FiltVal, AnalogIn, 10, 1000) )
analogWrite(PWMOut, (int)FiltVal / 4) ; // PWM auf 0 .. 255 skalieren
}
Wenn diese Erklärung zu speziell sein sollte, dann solltest du dich erst mal in die Grundlagen einarbeiten.
@Michael_X: Die Zeile " if (lastRun == 0) FiltVal = neuWert; " funktioniert nicht, weil lastrun nur nach dem Reset Null ist.
Also mir sind Variablen und Datentypen schon bekannt keine Sorge
Bin nur nicht ganz dahinter gekommen, wie genau das Programm arbeitet aber durch die Erklärung ist das klar!
Vielen Dank euch beiden..ich versuche mal das ganze umzusetzen und gebe dann natürlich Rückmeldung!
Die Zeile " if (lastRun == 0) FiltVal = neuWert; " funktioniert nicht, weil lastrun nur nach dem Reset Null ist.
Nur dafür ist diese Zeile gedacht. Bei Start soll der Anfangswert sofort übernommen werden. Und nicht langsam ab 0 hochgefiltert werden.
Da filtVal auch ausserhalb der Funktion existiert, kann man natürlich auch andere "Reset"-Möglichkeiten realisieren. Hier gibt es z.Zt. nur den Reset-Button.
Das Programm läuft, alles gut nur möchte ich versuchen es über die konkreten Formeln einer PT1 Regelung zu programmieren. Es gibt ja verschiedene Formeln für die zeitdiskrete Programmierung, ich habe diese hier mal gefunden, die für mich auch erklärbar ist
X(n) = X(n-1) * (1 - Ta/T) + Y(n) * Ta/(T*V)
X(n) = neuer Ausgangswert
X(n-1) = vorhergehender Wert
Y(n) = Eingangswert
Ta = Abtastzeit
T = Zeitkonstante
V = Verstärkung
oder gibt es wie für den PID-Regler auch für den PT1 Regler eine Bibliothek?