Grundlagen: Hysterese und PID-Tuning - ein Überblick

von cooper.bin
veröffentlicht am 18.10.2025 aktualisiert am 18.10.2025

Wenn es darum geht, Systeme zu steuern oder zu regeln braucht man effektive Methoden. Sei es die Temperatur des Heizbetts oder der Nozzle im 3D-Drucker, die Drehzahl eines Motors oder auch die Leistung eines Lüfters, je nach Anwendung müssen die Steuerungsprozesse unterschiedlich präzise sein.

Zwei bewährte Prinzipien die in solchen Szenarien zum Einsatz kommen, sind die Hysterese und das PID-Tuning. Beide bieten jeweils ihre eigenen Vor- und Nachteile, je nachdem, wie genau und stabil die Steuerung am Ende sein muss.

Zum Kapitel springen Hysterese

Eine einfache Möglichkeit, Systeme wie Lüfter oder Heizungen zu steuern, ist die Hysterese. Hierbei wird ein Gerät beispielsweise aktiviert, wenn ein bestimmter Schwellenwert überschritten wird, und wieder deaktiviert, wenn der Wert unter eine festgelegte Grenze fällt. Das Prinzip der Hysterese lässt sich gut an einem Wasserbehälter verdeutlichen:

  • Ein Sensor erfasst den Füllstand im Behälter
  • Ist der maximale Füllstand erreicht, schaltet sich eine Pumpe ein und der Pegel nimmt ab
  • Bei minimalem Füllstand wird die Pumpe wieder abgeschaltet und das Wasser kann wieder steigen

Der Wasserpegel pendelt also stets zwischen diesen beiden Werten, ohne kontinuierlich auf einem fest definierten Pegel zu bleiben.

Das Prinzip einer Hysterese-Steuerung ><

Zum Kapitel springen Beispiel

Nehmen wir mal noch ein anderes Beispiel aus der Praxis: Eine geschlossene und beheizte Kammer wird nur durch einen Ablufventilator gekühlt. Dies passiert, da er die heiße Luft aus der Kammer nach draußen befördert und somit kalte Luft von außen in die Kammer strömt.

Ziel ist es, eine relativ konstante Temperatur zu haben. Hierzu werden bei der Hysterese Schwellwerte festgelegt. Wird ein Schwellenwert überschritten, wird der Lüfter ein- oder ausgeschaltet, was wiederum die Temperatur in der Kammer senkt oder wieder erhöht.

Um die Hysterese mit konkreten Zahlen zu veranschaulichen, definieren wir für die Lüftersteuerung folgende Parameter:

  • Schwellenwert: 30°C
  • Hysterese: ±10°C

Verhalten der Hysterese-Steuerung:

  1. Der Lüfter wird eingeschaltet, wenn die Temperatur über 40°C steigt.
  2. Der Lüfter schaltet sich aus, wenn die Temperatur unter 20°C fällt.

Die Auswirkungen dieser Steuerung werden deutlich, wenn wir uns das folgende Diagramm anschauen, das die Temperaturverläufe und die entsprechenden Einschalt- sowie Ausschaltpunkte des Lüfters zeigt:

Darstellung einer Hysterese-Steuerung mit einem Lüfter ><

Die Hysterese ist also ein Steuerungsverfahren, da unser Steuerungssystem nur auf einen Eingangswert reagiert, ohne direkte Rückmeldung vom Ausgang.

// --- Parameter für Hysterese ---
const int SCHWELLENWERT   = 30; // Basiswert
const int HYSTERESE       = 10; // ±10°C

// Daraus ergeben sich:
const int EINSCHALT_TEMP  = SCHWELLENWERT + HYSTERESE; // 40°C
const int AUSSCHALT_TEMP  = SCHWELLENWERT - HYSTERESE; // 20°C

// Variablen
int temperatur            = 0; // Aktuelle Temperatur (z. B. vom Sensor)


// --- Hysterese-Logik ---
if ( temperatur > EINSCHALT_TEMP ) {
  // Lüfter anschalten
}

if ( temperatur < AUSSCHALT_TEMP ) {
  // Lüfter ausschalten
}

Zum Kapitel springen Vor- und Nachteile

Zum Kapitel springen PID-Tuning

Im Gegensatz zur vergleichsweise einfachen Hysterese arbeitet ein PID-Regler - Proportional-Integral-Differential-Regler - nicht mit festen Schwellwerten, sondern regelt dynamisch anhand eines gesetzten Sollwerts. Dabei wird nicht nur ein fester Schwellwert berücksichtigt, sondern kontinuierlich die Abweichung vom gewünschten Sollwert berechnet.

Dieses Prinzip lässt sich ebenfalls am Wasserbehälter verdeutlichen: Ein Sensor erfasst kontinuierlich den Füllstand. Je nachdem, wie weit der Wasserstand vom Sollwert entfernt ist, wird die Pumpe stärker oder schwächer betrieben - Ziel ist es nämlich, den Pegel möglichst konstant zu halten.

Anders als bei der Hysterese, bei der die Pumpe nur zwischen zwei Werten pendelt, arbeitet der PID-Regler deutlich feinfühliger und gezielter. Dadurch entstehen weniger große Schwankungen, und der Wasserstand bleibt stabiler im gewünschten Bereich. Annahme: Wir möchten einen Wasserpegel von exakt 50% haben.

Das Prinzip einer PID-Regelung ><

Nach dem Einschalten oder einer plötzlichen und starken Störung pendelt sich der Wasserstand nicht sofort wieder auf den Sollwert ein, sondern schwankt zunächst um ihn herum - wie in der Abbildung zu sehen. Dieses Verhalten wird als Einschwingen bezeichnet. Je nach Abstimmung des PID-Regler kann dieses Einschwingen unterschiedlich stark ausgeprägt sein. Eine zu aggressive Regelung führt zu starken Überschwingern, während eine zu träge Regelung das Erreichen des Sollwerts verzögert.

Sobald sich das System aber eingependelt hat, läuft es ziemlich stabil und kann den Sollwert präzise halten. Ein gutes Beispiel dafür ist ein 3D-Drucker: Beim Aufheizen der Nozzle kann die Temperatur anfangs stark überschwingen, bevor sie sich dann einpendelt. Danach hält der PID-Regler die Temperatur mit minimalen Schwankungen konstant.

Zum Kapitel springen Beispiel

Nehmen wir auch wieder ein Beispiel aus der Praxis: Eine geschlossene und beheizte Kammer wird nur durch einen Ablufventilator Zirkuliert. Das Ziel ist es, eine relativ konstante Temperatur zu haben. Hierzu wird eine Zieltemperatur festgelegt. Diese wird permanent überwacht und durch anschließendes Gegensteuern wird versucht, diese zu erreichen und anschließend stabil zu halten.

  • Soll-Temperatur: 30°C

Der Lüfter passt seine Geschwindigkeit dynamisch an, sobald die Temperatur vom Sollwert abweicht. Je größer die Abweichung, desto stärker wird der Lüfter aktiviert. Durch die kontinuierliche Anpassung wird die Temperatur nahe am Sollwert gehalten, ohne starke Über- oder Unterschwinger nach dem Einschwingen.
Die Auswirkungen dieser Regelung werden im folgenden Diagramm sichtbar:

Darstellung einer PID-Regelung mit einem Lüfter ><

Das PID-Tuning ist also im Gegensetz zur Hysterese ein Regelungsverfahren, da unser Steuerungssystem den Istwert mit dem gewünschten Sollwert vergleicht und dynamisch reagiert.

#include <PID_v1.h>

// --- PID-Parameter ---
double temperatur   = 20.0;     // Starttemperatur (Simulation)
double zielTemp     = 30.0;     // Sollwert
double leistung     = 0.0;      // PID-Ausgang (0-255)

// --- PID-Faktoren ---
double Kp           = 2.0;
double Ki           = 0.1;
double Kd           = 0.5;

// --- PID-Objekt ---
PID myPID(&temperatur, &leistung, &zielTemp, Kp, Ki, Kd, DIRECT);

void setup() {
  myPID.SetOutputLimits(0, 255);  // Lüfterleistung 0–255
  myPID.SetMode(AUTOMATIC);
}

void loop() {

  // PID berechnen und Regeln
  myPID.Compute();

  // --- Temperatur-Physik ---
  // In diesem Fall einfach stumpf simuliert
  temperatur += (0.05 * (25 - temperatur)); // Umgebungstendenz
  temperatur -= leistung * 0.01;            // Lüftereffekt


}

Zum Kapitel springen Vor- und Nachteile

Zum Kapitel springen HTML-Code für die Diagramme

Als kleines Zückerli habe ich hier noch die Charts aus den beiden Screenshots bereitgestellt. Es handelt sich hier um HTML-Dateien, die in jedem Webbrowser aufgerufen werden können.

<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hysterese vs. PID</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation"></script>
</head>
<body>
    <div style="display: flex; justify-content: space-around;">
        <canvas id="hystereseChart"></canvas>
        <canvas id="pidChart"></canvas>
    </div>
    
    <script>

        function generateHystereseData() {

            
            let time        = [...Array(100).keys()];
            let fanPower    = [ ];
            let temperature = [ ];
            
            let temp            = 25;  // Starttemperatur
            let hystereseMin    = 20;  // Untere Grenze für Lüfter an
            let hystereseMax    = 40;  // Obere Grenze für Lüfter aus
        
            for (let i = 0; i < time.length; i++) {
                // Hysterese-Logik: Lüfter schaltet genau dann um, wenn die Grenze erreicht ist
                if (temp >= hystereseMax) {
                    fanPower.push(100); // Lüfter an
                } else if (temp <= hystereseMin) {
                    fanPower.push(0); // Lüfter aus
                } else {
                    fanPower.push(fanPower[i - 1] || 0); // Beibehalten des vorherigen Status
                }
        
                // Temperaturberechnung mit sanftem Übergang
                temp += fanPower[i] === 100 ? -2 : 2;
                temperature.push(temp);
            }

            // Fan-Power shiften damit die Täler und Kämme übereinstimmen
            fanPower.shift();
            // Letzten Wert wieder anhängen
            fanPower.push(fanPower[fanPower.length - 1]);
            
            
            return {
                labels: time,
                datasets: [
                    {
                        label: "Temperatur (°C)",
                        data: temperature,
                        borderColor: "red",
                        borderWidth: 2,
                        fill: true,
                        backgroundColor:  'rgba(255, 0, 0, 0.2)',
                        pointStyle: false,
                        cubicInterpolationMode: "monotone",
                        tension: 0.8,
                    },
                    {
                      label:            "Lüfterleistung (%)",
                      data:             fanPower,
                      borderColor:      "blue",
                      borderWidth:      2,
                      fill:             true,
                      backgroundColor:  'rgba(0, 0, 255, 0.05)',
                      stepped:          true,
                      pointStyle:       false                
                    },
                ],
            };
        }

        function generatePIDData() {
            let time = [...Array(50).keys()];
            let target = Array(50).fill(50);
            let P = time.map(t => 50 + 10 * Math.sin(t / 10));
            let I = time.map(t => 50 + 5 * Math.log(t + 1));
            let D = time.map(t => 50 + (t % 5 === 0 ? 10 : -10));

            return {
                labels: time,
                datasets: [
                    { label: "Zielwert", data: target, borderColor: "black", borderWidth: 2, borderDash: [5, 5], fill: false },
                    { label: "P-Anteil", data: P, borderColor: "blue", borderWidth: 2, fill: false },
                    { label: "I-Anteil", data: I, borderColor: "green", borderWidth: 2, fill: false },
                    { label: "D-Anteil", data: D, borderColor: "orange", borderWidth: 2, fill: false },
                ],
            };
        }

        window.onload = function() {
            let ctx1 = document.getElementById("hystereseChart").getContext("2d");
            
            new Chart(ctx1, { 
              type:     "line",
              options: {
                plugins: {
                    annotation: {
                        annotations: {
                            line1: {
                                type: "line",
                                yMin: 20,
                                yMax: 20,
                                borderColor: "gray",
                                borderWidth: 2,
                                borderDash: [5, 5], // Gepunktete Linie
                                label: {
                                    display: false,
                                    content: "Min",
                                    position: "start",
                                    backgroundColor: "rgba(0, 0, 0, 0.2)",
                                },
                            },
                            line2: {
                                type: "line",
                                yMin: 40,
                                yMax: 40,
                                borderColor: "gray",
                                borderWidth: 2,
                                borderDash: [5, 5], // Gepunktete Linie
                                label: {
                                    display: false,
                                    content: "Max",
                                    position: "start",
                                    backgroundColor: "rgba(0, 0, 0, 0.2)",
                                },
                            },
                        },
                    },
                },
            },
              data:     generateHystereseData()
            });
            

            let ctx2 = document.getElementById("pidChart").getContext("2d");

            new Chart(ctx2, {
              type: "line",
              data: generatePIDData()
            });

        };
    </script>
</body>
</html>

<!DOCTYPE html>
<html lang="de">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Hysterese vs. PID</title>
        <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation"></script>
    </head>
    <body>

        <div style="display: flex; justify-content: space-around;">
            <canvas id="pidChart"></canvas>
        </div>
        
        <script>

        
            function generatePIDData() {

                let time          = [...Array(150).keys()];
                let targetTemp    = 30;
                let temperature   = [];
                let fanPower      = [];
            
                for (let i = 0; i < time.length; i++) {

                    // Schematische Annäherung an Zieltemperatur mit langsamerem Einschwingen
                    let temp = targetTemp + 10 * Math.exp(-i / 40) * Math.cos(i / 3);
                    temperature.push(temp);
                
                    // Lüfterleistung nimmt am Anfang stärker zu und stabilisiert sich langsamer
                    let fan = 100 * Math.exp(-i / 20) * Math.sin(i / 3) + 40;
                    fanPower.push(Math.max(0, Math.min(100, fan)));
                    
                }
            
                return {
                    labels: time,
                    datasets: [

                        {
                            label:                    "Temperatur (°C)",
                            data:                     temperature,
                            borderColor:              "red",
                            borderWidth:              2,
                            fill:                     false,
                            pointStyle:               false,
                            cubicInterpolationMode:   "monotone",
                            tension:                  0.4,
                        },

                        {
                            label:                    "Lüfterleistung (%)",
                            data:                     fanPower,
                            borderColor:              "blue",
                            borderWidth:              2,
                            fill:                     false,
                            pointStyle:               false,
                            stepped:                  false,
                            cubicInterpolationMode:   "monotone",
                            tension:                  0.4,
                        },

                    ],

                };
                
            }




            window.onload = function() {

                let ctx2 = document.getElementById("pidChart").getContext("2d");

                new Chart(ctx2, {
                    type: "line",
                    data: generatePIDData(),
                    options: {
                        plugins: {
                            annotation: {
                                annotations: {
                                    line1: {
                                        type: "line",
                                        yMin: 30,
                                        yMax: 30,
                                        borderColor: "gray",
                                        borderWidth: 2,
                                        borderDash: [5, 5], // Gepunktete Linie
                                        label: {
                                            display: false,
                                            content: "Zieltemperatur",
                                            position: "start",
                                            backgroundColor: "rgba(0, 0, 0, 0.2)",
                                        }
                                    }
                                }
                            }
                        }
                    }
                });

            };


        </script>

    </body>
</html>

cooper.bin Avatar

cooper.bin

Unterstütze mich und meine Arbeit, so kann ich weiter meiner Leidenschaft nachgehen. Ich lege viel Wert auf Qualität und stecke daher sehr viel Zeit in meine Beiträge. Wenn sie dir gefallen kannst du dir gerne auch meine anderen Artikel anschauen.

Mit PayPal unterstützen

Ich bin auf dem makesmart Discord-Server aktiv. Dort bin ich auch relativ gut erreichbar.

Jetzt neu: makesmart Threads

Entdecke spannende Projekte, hilfreiche Tutorials und interessante Diskussionen in unseren Threads. Stelle Fragen, teile eigene Ideen oder lass dich von den Erfahrungen der Community inspirieren.
Erfahre mehr im Thread: Willkommen in den makesmart Threads

Threads sind deine Anlaufstelle für Themen rund um hysterese, pid und vieles mehr. Werde Teil unserer neuen und wachsenden Community!


Discord

Das Thema elektrotechnik gibt es auch auf dem makesmart Discord-Server!

Teile diesen Beitrag



Dieser Artikel könnte dich auch interessieren

Einführung in das Serial Peripheral Interface

Erfahre in diesem informativen Blogbeitrag alles über das Serial Peripheral Interface (SPI), das in der Elektronik weit verbreitet ist.

renax am 31.03.2024