# Grundglieder
## Gliederung der Regelstrecken
::: {#tbl-gliederung-A}
| mit Ausgleich | | ohne Ausgleich | |
|:-----------|:----------|:-----------|:----------|
| ohne Verzögerung | mit Verzögerung | ohne Verzögerung | mit Verzögerung |
| **P** | **P-T~1~** | **I** | **I-T~1~** |
| | **P-T~2~** bzw. **P-T~2S~** | | **I-T~2~** bzw. **I-T~2S~** |
| | **P-T~n~** | | **I-T~n~** |
| | **P-T~t~** | | **I-T~t~** |
Gliederung
:::
## Grundglieder
<div style="text-align: right;">
<a href="../assets/SYS_RE1_Grundglieder.pdf">
<img src="/images/icons/file-pdf.svg"
alt="PDF"
width="24"
height="24"
style="vertical-align: middle; margin-right: 0.3em;">
PDF
</a>
</div>
### Glieder mit Ausgleich (P-Glieder)
::: {.callout-note title="Reminder"}
P-Glieder weisen einen Beharrungszustand (stationären Zustand) auf, d. h. der Ausgang strebt für einen konstanten Eingang einen stationären Endwert an. Die Beziehung zwischen Ausgang und Eingang im Beharrungszustand wird durch die (statische) Kennlinie bzw. den Proportionalbeiwert $K_P$ beschrieben.
{width=80% fig-align="center"}
:::
:::: {.columns}
::: {.column width="18%"}
**P**
<br>
<br>
<br>

:::
::: {.column width="52%"}

:::
::: {.column width="30%" .small-eq}
$$
x_a(t) = K_P \cdot x_e(t)
$$
:::
::::
---
:::: {.columns}
::: {.column width="18%"}
**P-T~1~**
<br>
<br>
<br>

:::
::: {.column width="52%"}

:::
::: {.column width="30%" .small-eq}
$$
\begin{aligned}
T_1 \cdot \dot{x}_a(t) + x_a(t) \\
= K_P \cdot x_e(t)
\end{aligned}
$$
:::
::::
---
:::: {.columns}
::: {.column width="18%"}
**P-T~2~**
<br>
<br>
<br>

:::
::: {.column width="52%"}

:::
::: {.column width="30%" .small-eq}
$$
\begin{aligned}
T_1 \cdot T_2 \cdot \ddot{x}_a(t) \\
+ (T_1+T_2) \cdot \dot{x}_a(t) \\
+ x_a(t) \\
= K_P \cdot x_e(t)
\end{aligned}
$$
:::
::::
---
:::: {.columns}
::: {.column width="18%"}
**P-T~2S~**
<br>
<br>
<br>

:::
::: {.column width="52%"}

:::
::: {.column width="30%" .small-eq}
$$
\begin{aligned}
\frac{1}{\omega_0^2} \cdot \ddot{x}_a(t) \\
+ 2 \cdot \frac{D}{\omega_0} \cdot \dot{x}_a(t) \\
+ x_a(t) \\
= K_P \cdot x_e(t)
\end{aligned}
$$
:::
::::
---
:::: {.columns}
::: {.column width="18%"}
**P-T~n~**
<br>
<br>
<br>

:::
::: {.column width="52%"}

:::
::: {.column width="30%" .small-eq}
$$
\ldots
$$
:::
::::
---
:::: {.columns}
::: {.column width="18%"}
**P-T~t~**
<br>
<br>
<br>

:::
::: {.column width="52%"}

:::
::: {.column width="30%" .small-eq}
$$
x_a(t) = K_P \cdot x_e(t - T_t)
$$
:::
::::
#### Interaktive Sprungantwort
Ändere die Parameter im untenstehenden Sprungantwort-Simulator und betrachte den Effekt auf die Sprungantwort. Die Sprungantwort wird jeweils für einen Einheitssprung am Eingang und $K_P = 1$ angezeigt. Für $K_P$ gibt es keinen Slider, dieser würde lediglich die Höhe der Sprungantwort verändern.
:::: {.columns}
::: {.column width="30%"}
```{ojs}
//| echo: false
isChromium = /\bChrome\b|\bEdg\b/.test(navigator.userAgent);
typeSelect = html`<select style="width:100%;padding:6px;font-size:15px;border:1px solid #ccc;border-radius:4px;">
<option value="ns">nicht schwingfähig</option>
<option value="os">schwingfähig</option>
</select>`
systemType = Generators.input(typeSelect)
_label1 = html`<span></span>`
_label2 = html`<span></span>`
_label3 = html`<span></span>`
_s1 = html`<input type="range" min="0" max="200" step="1" value="100" style="width:100%;">`
_s2 = html`<input type="range" min="0" max="200" step="1" value="100" style="width:100%;">`
_s3 = html`<input type="range" min="0" max="200" step="1" value="0" style="width:100%;">`
function updateLabels() {
var s1 = _s1.valueAsNumber, s2 = _s2.valueAsNumber, s3 = _s3.valueAsNumber;
var t1 = s1 / 100 * 2, t2 = s2 / 100 * 2, w = 1 + s1 / 200 * 4, d = s2 / 200, tt = s3 / 100 * 2;
_label1.innerHTML = typeSelect.value === "os" ? `ω₀: ${w.toFixed(2)} rad/s` : `T₁: ${t1.toFixed(2)} s`;
_label2.innerHTML = typeSelect.value === "os" ? `D: ${d.toFixed(2)}` : `T₂: ${t2.toFixed(2)} s`;
_label3.innerHTML = `T<sub>t</sub>: ${tt.toFixed(2)} s`;
}
function makeSlider(slider) {
return Generators.observe(function(change) {
var last = slider.valueAsNumber;
function inputted() { last = slider.valueAsNumber; updateLabels(); if (!isChromium) change(last); }
function released() { change(last); }
slider.addEventListener("input", inputted);
slider.addEventListener("change", released);
change(last);
return function() { slider.removeEventListener("input", inputted); slider.removeEventListener("change", released); };
});
}
s1Val = makeSlider(_s1);
s2Val = makeSlider(_s2);
s3Val = makeSlider(_s3);
_ = (updateLabels(), typeSelect.addEventListener("change", updateLabels));
T1 = s1Val / 100 * 2
T2 = s2Val / 100 * 2
omega = 1 + s1Val / 200 * 4
Damp = s2Val / 200
Tt = s3Val / 100 * 2
html`
<div style="padding:0.25em;font-family:sans-serif;">
<div style="margin-bottom:0.75em;">
${typeSelect}
</div>
<div style="margin-bottom:0.75em;">
<label style="display:block;font-size:14px;margin-bottom:2px;">${_label1}</label>
${_s1}
</div>
<div style="margin-bottom:0.75em;">
<label style="display:block;font-size:14px;margin-bottom:2px;">${_label2}</label>
${_s2}
</div>
<div style="margin-bottom:0.75em;">
<label style="display:block;font-size:14px;margin-bottom:2px;">${_label3}</label>
${_s3}
</div>
</div>`
```
:::
::: {.column width="70%"}
```{ojs}
//| echo: false
stepData = {
const K = 1;
const dt = 0.01;
const tMax = 10;
const data = [];
for (let t = 0; t <= tMax + Tt; t += dt) {
let y = 0;
if (t >= Tt) {
const tau = t - Tt;
if (systemType === "ns") {
if (Math.abs(T1 - T2) < 1e-6 && T1 > 1e-6) {
y = K * (1 - (1 + tau / T1) * Math.exp(-tau / T1));
} else if (T1 > 1e-6 && T2 > 1e-6) {
const a = T1 / (T1 - T2);
const b = T2 / (T1 - T2);
y = K * (1 - a * Math.exp(-tau / T1) + b * Math.exp(-tau / T2));
} else if (T1 > 1e-6) {
y = K * (1 - Math.exp(-tau / T1));
} else if (T2 > 1e-6) {
y = K * (1 - Math.exp(-tau / T2));
} else {
y = K;
}
} else if (systemType === "os") {
const w = omega;
const d = Damp;
if (d < 1) {
const wd = w * Math.sqrt(1 - d * d);
const phi = Math.acos(d);
y = K * (1 - Math.exp(-d * w * tau) / Math.sqrt(1 - d * d) * Math.sin(wd * tau + phi));
} else {
y = K * (1 - (1 + w * tau) * Math.exp(-w * tau));
}
}
}
if (t <= tMax) {
data.push({t, y});
}
}
return data;
}
```
```{ojs}
//| echo: false
Plot.plot({
height: 400,
marginLeft: 60,
marginBottom: 50,
grid: true,
style: {background: "transparent", color: "#333", fontSize: "14px"},
x: {label: "Zeit [s]", domain: [0, 10]},
y: {domain: [0, 2], label: null},
marks: [
Plot.lineY(stepData || [], {x: "t", y: "y", stroke: "#e74c3c", strokeWidth: 2.5}),
Plot.ruleY([1], {stroke: "#888", strokeDasharray: "4 4"}),
]
})
```
:::
::::
### Glieder ohne Ausgleich (I-Glieder)
::: {.callout-note title="Reminder"}
I-Glieder weisen keinen Beharrungszustand (keinen stationären Zustand) auf, d. h. es gibt keine (statische) Kennlinie und damit keinen Proportionalbeiwert $K_P$.
I-Glieder beschreiben wir mit dem Integrierbeiwert $K_I$ bzw. alternativ mit der Integrierzeit $T_I$, wobei die eine Grösse die Inverse der andern ist ($K_I=1/T_I$).
:::
:::: {.columns}
::: {.column width="18%"}
**I**
<br>
<br>
<br>

:::
::: {.column width="52%"}

:::
::: {.column width="30%" .small-eq}
$$
\dot{x}_a(t) = K_I \cdot x_e(t)
$$
:::
::::
---
:::: {.columns}
::: {.column width="18%"}
**I-T~1~**
<br>
<br>
<br>

:::
::: {.column width="52%"}

:::
::: {.column width="30%" .small-eq}
$$
\begin{aligned}
T_1 \cdot \ddot{x}_a(t) \\
+ \dot{x}_a(t) \\
= K_I \cdot x_e(t)
\end{aligned}
$$
:::
::::
---
:::: {.columns}
::: {.column width="18%"}
$$
\ldots
$$
:::
::: {.column width="52%"}
$$
\ldots
$$
:::
::: {.column width="30%" .small-eq}
$$
\ldots
$$
:::
::::
---
:::: {.columns}
::: {.column width="18%"}
**I-T~t~**
<br>
<br>
<br>

:::
::: {.column width="52%"}

:::
::: {.column width="30%" .small-eq}
$$
\begin{aligned}
\dot{x}_a(t) \\
= K_I \cdot x_e(t - T_t)
\end{aligned}
$$
:::
::::
#### Interaktive Sprungantwort
Ändere die Parameter im untenstehenden Sprungantwort-Simulator und betrachte den Effekt auf die Sprungantwort. Die Sprungantwort wird jeweils für einen Einheitssprung am Eingang angezeigt.
:::: {.columns}
::: {.column width="30%"}
```{ojs}
//| echo: false
_label1b = html`<span></span>`
_label2b = html`<span></span>`
_label3b = html`<span></span>`
_s1b = html`<input type="range" min="0" max="200" step="1" value="50" style="width:100%;">`
_s2b = html`<input type="range" min="0" max="200" step="1" value="0" style="width:100%;">`
_s3b = html`<input type="range" min="0" max="200" step="1" value="0" style="width:100%;">`
function updateLabelsB() {
var s1 = _s1b.valueAsNumber, s2 = _s2b.valueAsNumber, s3 = _s3b.valueAsNumber;
var ki = s1 / 200 * 2, t1 = s2 / 200 * 4, tt = s3 / 200 * 4;
_label1b.innerHTML = `K<sub>I</sub>: ${ki.toFixed(3)}`;
_label2b.innerHTML = `T₁: ${t1.toFixed(2)} s`;
_label3b.innerHTML = `T<sub>t</sub>: ${tt.toFixed(2)} s`;
}
function makeSliderB(slider) {
return Generators.observe(function(change) {
var last = slider.valueAsNumber;
function inputted() { last = slider.valueAsNumber; updateLabelsB(); if (!isChromium) change(last); }
function released() { change(last); }
slider.addEventListener("input", inputted);
slider.addEventListener("change", released);
change(last);
return function() { slider.removeEventListener("input", inputted); slider.removeEventListener("change", released); };
});
}
s1ValB = makeSliderB(_s1b);
s2ValB = makeSliderB(_s2b);
s3ValB = makeSliderB(_s3b);
KIval = s1ValB / 200 * 2
T1val = s2ValB / 200 * 4
Ttval = s3ValB / 200 * 4
__initB = updateLabelsB();
html`
<div style="padding:0.25em;font-family:sans-serif;">
<div style="margin-bottom:0.75em;">
<label style="display:block;font-size:14px;margin-bottom:2px;">${_label1b}</label>
${_s1b}
</div>
<div style="margin-bottom:0.75em;">
<label style="display:block;font-size:14px;margin-bottom:2px;">${_label2b}</label>
${_s2b}
</div>
<div style="margin-bottom:0.75em;">
<label style="display:block;font-size:14px;margin-bottom:2px;">${_label3b}</label>
${_s3b}
</div>
</div>`
```
:::
::: {.column width="70%"}
```{ojs}
//| echo: false
stepData2 = {
const dt = 0.01;
const tMax = 10;
const data = [];
for (let t = 0; t <= tMax + Ttval; t += dt) {
let y = 0;
if (t >= Ttval) {
const tau = t - Ttval;
if (T1val > 1e-6) {
y = KIval * (tau - T1val + T1val * Math.exp(-tau / T1val));
} else {
y = KIval * tau;
}
}
if (t <= tMax) {
data.push({t, y});
}
}
return data;
}
```
```{ojs}
//| echo: false
Plot.plot({
height: 400,
marginLeft: 60,
marginBottom: 50,
grid: true,
style: {background: "transparent", color: "#333", fontSize: "14px"},
x: {label: "Zeit [s]", domain: [0, 10]},
y: {domain: [0, 10], label: null},
marks: [
Plot.lineY(stepData2 || [], {x: "t", y: "y", stroke: "#e74c3c", strokeWidth: 2.5}),
Plot.ruleX([0], {stroke: "#888"}),
]
})
```
:::
::::
### D-Glieder
Neben dem P-Glied (*proportional*), dem I-Glied (*integrierend*) kommt in Reglern auch das D-Glied (*differenzierend*) zur Anwendung.
::: {.callout-note title="D-Glieder"}
D-Glieder wirken *differenzierend* (daher der Name), d. h. sie wirken auf die Ableitung des Eingangssignals (d. h. für eine konstante Eingangsgrösse ist die Ausgangsgrösse null).
D-Glieder werden mit dem Differenzierbeiwert $K_D$ bzw. alternativ mit der Differenzierzeit $T_D$ beschrieben, wobei $K_D = T_D$ gilt.
Das *ideale* D-Glied existiert in der Praxis nicht, d. h. ein *reales* D-Glied wird immer mit einer zusätzlichen Verzögerung, also als D-T~1~ implementiert.
:::
:::: {.columns}
::: {.column width="18%"}
**D**
<br>
<br>
<br>

:::
::: {.column width="52%"}

:::
::: {.column width="30%" .small-eq}
$$
x_a(t) = K_D \cdot \dot{x}_e(t)
$$
:::
::::
---
:::: {.columns}
::: {.column width="18%"}
**D-T~1~**
<br>
<br>
<br>

:::
::: {.column width="52%"}

:::
::: {.column width="30%" .small-eq}
$$
\begin{aligned}
T_1 \cdot \dot{x}_a(t) \\
+ x_a(t) \\
= K_D \cdot \dot{x}_e(t)
\end{aligned}
$$
:::
::::
### Regelglieder
Werden P-, I- und D-Glied parallelgeschaltet, entstehen das PI-, das PD- und das PID-Glied.
::: {.callout-note title="PD/PID-T~1~"}
Wenn der Regler einen D-Anteil enthält (PD bzw. PID), wird der D-Anteil immer mit einer zusätzlichen (kleinen) Verzögerung $T_{\mathrm{Vz}}$ implementiert (ein reines D-Glied ist nicht realisierbar). Der Regler wird dann PD-T~1~ bzw. PID-T~1~ genannt oder einfach *realer* PD/PID-Regler. Der Effekt der Verzögerungszeit ist in der jeweiligen Sprungantwort ersichtlich, beim Icon und der Reglergleichung verwenden wir der Einfachheit halber jeweils die *ideale* Darstellung.
:::
:::: {.columns}
::: {.column width="18%"}
**P-Regler**
:::
::: {.column width="52%"}
siehe P-Glied oben
:::
::::
---
:::: {.columns}
::: {.column width="18%"}
**I-Regler**
:::
::: {.column width="52%"}
siehe I-Glied oben
:::
::::
---
:::: {.columns}
::: {.column width="18%"}
**PI-Regler**
<br>
<br>

:::
::: {.column width="52%"}

:::
::: {.column width="30%" .small-eq}
$$
\begin{aligned}
y(t) = K_P \cdot \Bigl(
&e(t) \\
&+ \frac{1}{T_i} \cdot \int{e(t) \, \mathrm{d}t}
\Bigr)
\end{aligned}
$$
:::
::::
---
:::: {.columns}
::: {.column width="18%"}
**PD-Regler**
<br>
<br>

:::
::: {.column width="52%"}

:::
::: {.column width="30%" .small-eq}
$$
\begin{aligned}
y(t) = K_P \cdot \Bigl(
&e(t) \\
&+ T_d \cdot \dot{e}(t)
\Bigr)
\end{aligned}
$$
:::
::::
---
:::: {.columns}
::: {.column width="18%"}
**PID-Regler**
<br>
<br>

:::
::: {.column width="52%"}

:::
::: {.column width="30%" .small-eq}
$$
\begin{aligned}
y(t) = K_P \cdot \Bigl(
&e(t) \\
&+ \frac{1}{T_i} \cdot \int{e(t) \, \mathrm{d}t} \\
&+ T_d \cdot \dot{e}(t)
\Bigr)
\end{aligned}
$$
:::
::::
#### Interaktive Sprungantwort
Ändere die Parameter im untenstehenden Sprungantwort-Simulator und betrachte den Effekt auf die Sprungantwort. Die Sprungantwort wird für einen Einheitssprung am Eingang (Regelfehler) des realen PID-T~1~-Reglers angezeigt. Der D-Anteil ist dabei mit der Verzugszeit $T_\mathrm{Vz}$ realisiert.
:::: {.columns}
::: {.column width="30%"}
```{ojs}
//| echo: false
_label1c = html`<span></span>`
_label2c = html`<span></span>`
_label3c = html`<span></span>`
_label4c = html`<span></span>`
_s1c = html`<input type="range" min="0" max="200" step="1" value="100" style="width:100%;">`
_s2c = html`<input type="range" min="0" max="200" step="1" value="100" style="width:100%;">`
_s3c = html`<input type="range" min="0" max="200" step="1" value="100" style="width:100%;">`
_s4c = html`<input type="range" min="0" max="200" step="1" value="100" style="width:100%;">`
_checkI = html`<input type="checkbox" style="transform:scale(1.25);margin-right:6px;" checked>`
_checkD = html`<input type="checkbox" style="transform:scale(1.25);margin-right:6px;" checked>`
function updateLabelsC() {
var s1 = _s1c.valueAsNumber, s2 = _s2c.valueAsNumber, s3 = _s3c.valueAsNumber, s4 = _s4c.valueAsNumber;
var kp = 0.5 + s1 / 200 * 4, ti = 1 + s2 / 200 * 9, td = s3 / 200 * 1, tvz = 0.05 + s4 / 200 * 0.45;
_label1c.innerHTML = `K<sub>P</sub>: ${kp.toFixed(2)}`;
_label2c.innerHTML = `T<sub>i</sub>: ${ti.toFixed(2)} s`;
_label3c.innerHTML = `T<sub>d</sub>: ${td.toFixed(2)} s`;
_label4c.innerHTML = `T<sub>Vz</sub>: ${tvz.toFixed(3)} s`;
}
function makeSliderC(slider) {
return Generators.observe(function(change) {
var last = slider.valueAsNumber;
function inputted() { last = slider.valueAsNumber; updateLabelsC(); if (!isChromium) change(last); }
function released() { change(last); }
slider.addEventListener("input", inputted);
slider.addEventListener("change", released);
change(last);
return function() { slider.removeEventListener("input", inputted); slider.removeEventListener("change", released); };
});
}
s1ValC = makeSliderC(_s1c);
s2ValC = makeSliderC(_s2c);
s3ValC = makeSliderC(_s3c);
s4ValC = makeSliderC(_s4c);
function makeCheck(cb) {
return Generators.observe(function(change) {
function changed() { change(cb.checked); }
cb.addEventListener("change", changed);
change(cb.checked);
return function() { cb.removeEventListener("change", changed); };
});
}
enableI = makeCheck(_checkI);
enableD = makeCheck(_checkD);
__disableC = {
_s2c.disabled = !enableI;
_s3c.disabled = !enableD;
_s4c.disabled = !enableD;
}
Kp = 0.5 + s1ValC / 200 * 4
Ti = 1 + s2ValC / 200 * 9
Td = s3ValC / 200 * 1
Tvz = 0.05 + s4ValC / 200 * 0.45
__initC = updateLabelsC();
html`
<div style="padding:0.25em;font-family:sans-serif;">
<div style="margin-bottom:0.75em;display:flex;gap:14px;">
<label style="display:flex;align-items:center;font-size:14px;">${_checkI} I-Anteil</label>
<label style="display:flex;align-items:center;font-size:14px;">${_checkD} D-Anteil</label>
</div>
<div style="margin-bottom:0.75em;">
<label style="display:block;font-size:14px;margin-bottom:2px;">${_label1c}</label>
${_s1c}
</div>
<div style="margin-bottom:0.75em;">
<label style="display:block;font-size:14px;margin-bottom:2px;">${_label2c}</label>
${_s2c}
</div>
<div style="margin-bottom:0.75em;">
<label style="display:block;font-size:14px;margin-bottom:2px;">${_label3c}</label>
${_s3c}
</div>
<div style="margin-bottom:0.75em;">
<label style="display:block;font-size:14px;margin-bottom:2px;">${_label4c}</label>
${_s4c}
</div>
</div>`
```
:::
::: {.column width="70%"}
```{ojs}
//| echo: false
stepData3 = {
const K = Kp;
const dt = 0.01;
const tMax = 10;
const data = [];
for (let t = 0; t <= tMax; t += dt) {
const y = K * (1
+ (enableI ? t / Ti : 0)
+ (enableD ? (Td / Tvz) * Math.exp(-t / Tvz) : 0));
data.push({t, y});
}
return data;
}
```
```{ojs}
//| echo: false
Plot.plot({
height: 400,
marginLeft: 60,
marginBottom: 50,
grid: true,
style: {background: "transparent", color: "#333", fontSize: "14px"},
x: {label: "Zeit [s]", domain: [0, 10]},
y: {domain: [0, 10], label: null},
marks: [
Plot.line([[0, 0], [0, Kp * (1 + (enableD ? Td / Tvz : 0))]], {stroke: "#e74c3c", strokeWidth: 2.5}),
Plot.lineY(stepData3 || [], {x: "t", y: "y", stroke: "#e74c3c", strokeWidth: 2.5}),
]
})
```
:::
::::