Initiale Bereitstellung
Initiale Bereitstellung der aktuellen Version auf Gitea
This commit is contained in:
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2011, Brett Beauregard
|
||||
Modifiziert von Thomas Müller, 2025
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#if ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#include <PID_AutoTune_v0.h>
|
||||
|
||||
// #define ATUNE_DEBUG // Debug-Ausgaben aktivieren
|
||||
|
||||
PID_ATune::PID_ATune(double* Input, double* Output)
|
||||
{
|
||||
input = Input;
|
||||
output = Output;
|
||||
controlType = 0; //default to PI
|
||||
noiseBand = 0.5;
|
||||
running = false;
|
||||
oStep = 30; // Default-Wert, wird normalerweise mit SetOutputStep überschrieben
|
||||
SetLookbackSec(10); // Default-Wert, wird normalerweise mit SetLookbackSec überschrieben
|
||||
lastTime = millis();
|
||||
}
|
||||
|
||||
void PID_ATune::Cancel()
|
||||
{
|
||||
running = false;
|
||||
}
|
||||
|
||||
int PID_ATune::Runtime()
|
||||
{
|
||||
justevaled = false;
|
||||
|
||||
// Failsafe-Check (wie vorher)
|
||||
if (peakCount > 9 && running)
|
||||
{
|
||||
#ifdef ATUNE_DEBUG
|
||||
Serial.println("ATune: Failsafe peakCount > 9 triggered!");
|
||||
#endif
|
||||
running = false;
|
||||
FinishUp();
|
||||
return 1; // Fertig (Grund: Max Peaks)
|
||||
}
|
||||
|
||||
unsigned long now = millis();
|
||||
|
||||
// Prüfen, ob SampleTime abgelaufen ist
|
||||
if ((now - lastTime) < sampleTime) return 0; // Nicht return false, sondern 0 (wie im Original)
|
||||
lastTime = now;
|
||||
double refVal = *input;
|
||||
justevaled = true;
|
||||
|
||||
// Initialisierung beim ersten Durchlauf
|
||||
if (!running)
|
||||
{
|
||||
peakType = 0;
|
||||
peakCount = 0;
|
||||
justchanged = false;
|
||||
absMax = refVal;
|
||||
absMin = refVal;
|
||||
setpoint = refVal;
|
||||
running = true;
|
||||
outputStart = *output;
|
||||
*output = outputStart + oStep;
|
||||
// Initialize lastInputs buffer
|
||||
for (int i = 0; i <= nLookBack; i++) { // Initialize entire buffer size
|
||||
lastInputs[i] = refVal;
|
||||
}
|
||||
#ifdef ATUNE_DEBUG
|
||||
Serial.println("ATune: Initializing and starting run.");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update absMax und absMin (wie vorher)
|
||||
if (refVal > absMax) absMax = refVal;
|
||||
if (refVal < absMin) absMin = refVal;
|
||||
}
|
||||
|
||||
// Output oszillieren lassen (wie vorher)
|
||||
if (refVal > setpoint + noiseBand) *output = outputStart - oStep;
|
||||
else if (refVal < setpoint - noiseBand) *output = outputStart + oStep;
|
||||
|
||||
|
||||
// Peak-Erkennung ============================================
|
||||
isMax = true; isMin = true;
|
||||
|
||||
// --- CHANGE 1: Kurzeres Fenster für Peak-Erkennung definieren ---
|
||||
// Vergleiche nur mit den letzten N Samples, nicht mit allen 100
|
||||
// Ein Wert von 3 bis 7 ist hier oft sinnvoll. 5 ist ein Kompromiss.
|
||||
const int peakDetectLookback = 5;
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
// Prüfen, ob genug Samples für die *generelle* Pufferfüllung vorhanden sind
|
||||
// (Originalbedingung, lassen wir zur Sicherheit drin)
|
||||
if (nLookBack < 9) {
|
||||
// Schiebe den gesamten Puffer, aber führe noch keine Peak-Logik aus
|
||||
for (int i = nLookBack - 1; i >= 0; i--) {
|
||||
lastInputs[i + 1] = lastInputs[i];
|
||||
}
|
||||
lastInputs[0] = refVal;
|
||||
// Serial.println("ATune: Filling buffer..."); // DEBUG (kann viel Output erzeugen)
|
||||
return 0; // Noch nicht genug Historie für die Hauptlogik
|
||||
}
|
||||
|
||||
|
||||
// --- CHANGE 2: Peak-Erkennungsschleife angepasst ---
|
||||
// Führe die eigentliche Peak-Prüfung nur über das kurze Fenster durch
|
||||
int checkWindow = (nLookBack < peakDetectLookback) ? nLookBack : peakDetectLookback;
|
||||
for (int i = checkWindow - 1; i >= 0; i--) { // Schleife nur über 'checkWindow' Samples
|
||||
double val = lastInputs[i];
|
||||
if (isMax) isMax = refVal > val; // Ist aktueller Wert größer als die letzten 'checkWindow' Werte?
|
||||
if (isMin) isMin = refVal < val; // Ist aktueller Wert kleiner als die letzten 'checkWindow' Werte?
|
||||
}
|
||||
// ----------------------------------------------------
|
||||
|
||||
// Schiebe den *gesamten* Puffer für die nächste Iteration weiter
|
||||
// (Behält die volle Historie für andere Zwecke oder zukünftige Logik bei)
|
||||
for (int i = nLookBack - 1; i >= 0; i--) {
|
||||
lastInputs[i + 1] = lastInputs[i];
|
||||
}
|
||||
lastInputs[0] = refVal;
|
||||
|
||||
// Ende Peak-Erkennung =========================================
|
||||
|
||||
|
||||
// Peak-Logik & Zählung (weitgehend wie vorher) =================
|
||||
if (isMax)
|
||||
{
|
||||
if (peakType == 0) peakType = 1; // Erster Peak ist ein Max
|
||||
if (peakType == -1) // Übergang von Min zu Max
|
||||
{
|
||||
peakType = 1;
|
||||
justchanged = true;
|
||||
peak2 = peak1; // Zeit des vorigen Max merken für Pu-Berechnung
|
||||
#ifdef ATUNE_DEBUG
|
||||
Serial.printf("ATune: Max Peak detected (Value: %.2f, Time: %lu, PrevMaxTime: %lu)\n", refVal, now, peak2);
|
||||
#endif
|
||||
}
|
||||
peak1 = now; // Zeit des aktuellen Max speichern
|
||||
|
||||
// --- CHANGE 3: peaks[] Zugriff abgesichert ---
|
||||
if (peakCount < 10) {
|
||||
peaks[peakCount] = refVal; // Max-Wert speichern (am Index des letzten Min!)
|
||||
} else {
|
||||
// Optional: Fehler loggen, falls dieser Fall eintritt
|
||||
#ifdef ATUNE_DEBUG
|
||||
Serial.println("ATune Warning: peakCount >= 10 in isMax!");
|
||||
#endif
|
||||
}
|
||||
// --------------------------------------------
|
||||
}
|
||||
else if (isMin)
|
||||
{
|
||||
if (peakType == 0) peakType = -1; // Erster Peak ist ein Min
|
||||
if (peakType == 1) // Übergang von Max zu Min
|
||||
{
|
||||
peakType = -1;
|
||||
peakCount++; // <<<< peakCount wird hier erhöht
|
||||
justchanged = true;
|
||||
#ifdef ATUNE_DEBUG
|
||||
Serial.printf("ATune: Min Peak detected (Value: %.2f). peakCount incremented to: %d\n", refVal, peakCount);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (peakCount < 10) {
|
||||
peaks[peakCount] = refVal; // Min-Wert speichern
|
||||
}
|
||||
// Keine Absicherung für peakCount >= 10 nötig, da peakCount > 9 oben abfängt
|
||||
}
|
||||
|
||||
// --- CHANGE 4: Mindestzyklen für Stabilitätsprüfung erzwingen ---
|
||||
// Original war peakCount > 2 (Prüfung ab Zyklus 3)
|
||||
// Neu: peakCount >= 5 (Prüfung beginnt erst ab Zyklus 5)
|
||||
if(justchanged && peakCount >= 5)
|
||||
// -------------------------------------------------------------
|
||||
{
|
||||
// ... (Rest der Stabilitätsprüfung bleibt gleich) ...
|
||||
double amp1 = abs(peaks[peakCount - 1] - peaks[peakCount - 2]);
|
||||
double amp2 = abs(peaks[peakCount - 2] - peaks[peakCount - 3]);
|
||||
double avgSeparation = (amp1 + amp2) / 2.0;
|
||||
|
||||
double threshold = 0.05 * (absMax - absMin);
|
||||
|
||||
#ifdef ATUNE_DEBUG
|
||||
Serial.printf("ATune Stability Check - peakCount: %d, avgSeparation: %.4f, Threshold: %.4f (absMax: %.2f, absMin: %.2f)\n",
|
||||
peakCount, avgSeparation, threshold, absMax, absMin);
|
||||
#endif
|
||||
|
||||
if (avgSeparation < threshold && threshold > 0)
|
||||
{
|
||||
#ifdef ATUNE_DEBUG
|
||||
Serial.println("ATune: Amplitude stable (and min cycles >= 5 met), finishing!");
|
||||
#endif
|
||||
FinishUp();
|
||||
running = false;
|
||||
return 1; // Fertig (Grund: Stabilität nach min 5 Zyklen)
|
||||
}
|
||||
}
|
||||
justchanged = false; // Zurücksetzen für nächste Iteration
|
||||
|
||||
// Wenn keine Bedingung erfüllt, weiterlaufen
|
||||
// Serial.println("ATune: Continuing..."); // DEBUG (Sehr viel Output!)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Restliche Funktionen (FinishUp, GetKp, GetKi, GetKd, Set/Get-Methoden) bleiben wie im Original,
|
||||
// es sei denn, du möchtest auch die Tuning-Regeln ändern (wie in Prio 2 zuvor besprochen).
|
||||
|
||||
void PID_ATune::FinishUp()
|
||||
{
|
||||
*output = outputStart; // Setze Output zurück auf den Wert vor dem Tuning
|
||||
// Berechne Ku und Pu
|
||||
// Ku = Ultimate Gain. Formel basiert auf Relay-Methode.
|
||||
// Pu = Ultimate Period (Zeit zwischen zwei Maxima in Sekunden).
|
||||
if ((absMax - absMin) == 0) { // Division durch Null verhindern
|
||||
Ku = 0; // Oder einen anderen Fehlerwert setzen?
|
||||
Pu = 0;
|
||||
#ifdef ATUNE_DEBUG
|
||||
Serial.println("ATune ERROR: absMax - absMin is zero in FinishUp!");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
Ku = 4.0 * (2.0 * oStep) / ((absMax - absMin) * 3.14159);
|
||||
|
||||
if (peak1 == peak2) { // Division durch Null bzw. ungültige Zeit verhindern
|
||||
Pu = 0;
|
||||
#ifdef ATUNE_DEBUG
|
||||
Serial.println("ATune ERROR: peak1 == peak2 in FinishUp!");
|
||||
#endif
|
||||
} else {
|
||||
Pu = (double)(peak1 - peak2) / 1000.0; // Zeitdifferenz in Sekunden
|
||||
}
|
||||
#ifdef ATUNE_DEBUG
|
||||
Serial.printf("ATune FinishUp - Ku: %.2f, Pu: %.2f\n", Ku, Pu);
|
||||
#endif
|
||||
}
|
||||
|
||||
// --- GetKp, GetKi, GetKd verwenden die berechneten Ku, Pu ---
|
||||
// Diese kannst du ändern, wenn du andere Tuning-Regeln möchtest
|
||||
|
||||
double PID_ATune::GetKp()
|
||||
{
|
||||
// Klassisch ZN für PID Regler (controlType=1)
|
||||
if (controlType == 1) return 0.6 * Ku;
|
||||
// Klassisch ZN für PI Regler (controlType=0)
|
||||
else return 0.4 * Ku;
|
||||
}
|
||||
|
||||
double PID_ATune::GetKi()
|
||||
{
|
||||
// Klassisch ZN für PID Regler (controlType=1) -> Ki = Kp / Ti = (0.6*Ku) / (Pu/2.0) = 1.2*Ku / Pu
|
||||
if (controlType == 1) {
|
||||
if (Pu == 0) return 0; // Division durch Null verhindern
|
||||
return 1.2 * Ku / Pu;
|
||||
}
|
||||
// Klassisch ZN für PI Regler (controlType=0) -> Ki = Kp / Ti = (0.4*Ku) / (Pu/1.2) = 0.48*Ku/Pu
|
||||
else {
|
||||
if (Pu == 0) return 0; // Division durch Null verhindern
|
||||
return 0.48 * Ku / Pu;
|
||||
}
|
||||
}
|
||||
|
||||
double PID_ATune::GetKd()
|
||||
{
|
||||
// Klassisch ZN für PID Regler (controlType=1) -> Kd = Kp * Td = (0.6*Ku) * (Pu/8.0) = 0.075 * Ku * Pu
|
||||
if (controlType == 1) return 0.075 * Ku * Pu;
|
||||
// Kein Kd für PI Regler (controlType=0)
|
||||
else return 0;
|
||||
}
|
||||
|
||||
|
||||
// --- Setter und Getter bleiben unverändert ---
|
||||
|
||||
void PID_ATune::SetOutputStep(double Step)
|
||||
{
|
||||
oStep = Step;
|
||||
}
|
||||
|
||||
double PID_ATune::GetOutputStep()
|
||||
{
|
||||
return oStep;
|
||||
}
|
||||
|
||||
void PID_ATune::SetControlType(int Type) //0=PI, 1=PID
|
||||
{
|
||||
controlType = Type;
|
||||
}
|
||||
int PID_ATune::GetControlType()
|
||||
{
|
||||
return controlType;
|
||||
}
|
||||
|
||||
void PID_ATune::SetNoiseBand(double Band)
|
||||
{
|
||||
noiseBand = Band;
|
||||
}
|
||||
|
||||
double PID_ATune::GetNoiseBand()
|
||||
{
|
||||
return noiseBand;
|
||||
}
|
||||
|
||||
void PID_ATune::SetLookbackSec(int value)
|
||||
{
|
||||
if (value < 1) value = 1;
|
||||
|
||||
if (value < 25)
|
||||
{
|
||||
nLookBack = value * 4;
|
||||
sampleTime = 250;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hier wird nLookBack immer auf 100 gesetzt für längere Zeiten!
|
||||
// Das Array lastInputs muss entsprechend groß sein (im .h File prüfen!)
|
||||
nLookBack = 100;
|
||||
sampleTime = value * 10; // sampleTime wird größer
|
||||
}
|
||||
// Ensure nLookBack isn't larger than array capacity (assuming array size is 101 based on original loop logic)
|
||||
if (nLookBack > 100) nLookBack = 100;
|
||||
}
|
||||
|
||||
int PID_ATune::GetLookbackSec()
|
||||
{
|
||||
// Berechne den tatsächlichen Lookback basierend auf nLookBack und sampleTime
|
||||
// Beachte: sampleTime kann variieren!
|
||||
return nLookBack * sampleTime / 1000;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2011, Brett Beauregard
|
||||
Modifiziert von Thomas Müller, 2025
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef PID_AutoTune_v0
|
||||
#define PID_AutoTune_v0
|
||||
#define LIBRARY_VERSION 1.0 // Original von Brett Beauregard, modifiziert von Thomas Müller, 2025
|
||||
|
||||
class PID_ATune
|
||||
{
|
||||
|
||||
|
||||
public:
|
||||
//commonly used functions **************************************************************************
|
||||
PID_ATune(double*, double*); // * Constructor. links the Autotune to a given PID
|
||||
int Runtime(); // * Similar to the PID Compue function, returns non 0 when done
|
||||
void Cancel(); // * Stops the AutoTune
|
||||
|
||||
void SetOutputStep(double); // * how far above and below the starting value will the output step?
|
||||
double GetOutputStep(); //
|
||||
|
||||
void SetControlType(int); // * Determies if the tuning parameters returned will be PI (D=0)
|
||||
int GetControlType(); // or PID. (0=PI, 1=PID)
|
||||
|
||||
void SetLookbackSec(int); // * how far back are we looking to identify peaks
|
||||
int GetLookbackSec(); //
|
||||
|
||||
void SetNoiseBand(double); // * the autotune will ignore signal chatter smaller than this value
|
||||
double GetNoiseBand(); // this should be acurately set
|
||||
|
||||
double GetKp(); // * once autotune is complete, these functions contain the
|
||||
double GetKi(); // computed tuning parameters.
|
||||
double GetKd(); //
|
||||
|
||||
// ----- NEUE GETTER FUNKTIONEN -----
|
||||
int getPeakCount() { return peakCount; }
|
||||
int getPeakType() { return peakType; }
|
||||
// ----------------------------------
|
||||
|
||||
private:
|
||||
void FinishUp();
|
||||
bool isMax, isMin;
|
||||
double *input, *output;
|
||||
double setpoint;
|
||||
double noiseBand;
|
||||
int controlType;
|
||||
bool running;
|
||||
unsigned long peak1, peak2, lastTime;
|
||||
int sampleTime;
|
||||
int nLookBack;
|
||||
int peakType;
|
||||
double lastInputs[101];
|
||||
double peaks[10];
|
||||
int peakCount;
|
||||
bool justchanged;
|
||||
bool justevaled;
|
||||
double absMax, absMin;
|
||||
double oStep;
|
||||
double outputStart;
|
||||
double Ku, Pu;
|
||||
|
||||
};
|
||||
#endif
|
||||
Reference in New Issue
Block a user