{ "cells": [ { "cell_type": "markdown", "metadata": { "nbpages": { "level": 0, "link": "[](https://jckantor.github.io/CBE30338/05.04-Controller-Tuning-Rules-in-Frequency-Domain.html)", "section": "" } }, "source": [ "\n", "*This notebook contains material from [CBE30338](https://jckantor.github.io/CBE30338);\n", "content is available [on Github](https://github.com/jckantor/CBE30338.git).*\n" ] }, { "cell_type": "markdown", "metadata": { "nbpages": { "level": 0, "link": "[](https://jckantor.github.io/CBE30338/05.04-Controller-Tuning-Rules-in-Frequency-Domain.html)", "section": "" } }, "source": [ "\n", "< [5.3 Creating Bode Plots](https://jckantor.github.io/CBE30338/05.03-Creating-Bode-Plots.html) | [Contents](toc.html) | [Tag Index](tag_index.html) | [5.5 Baroreflex as a Linear Control System](https://jckantor.github.io/CBE30338/05.05-Baroreflex-as-a-Linear-Control-System.html) >
"
]
},
{
"cell_type": "markdown",
"metadata": {
"nbpages": {
"level": 1,
"link": "[5.4 Controller Tuning Rules in Frequency Domain](https://jckantor.github.io/CBE30338/05.04-Controller-Tuning-Rules-in-Frequency-Domain.html#5.4-Controller-Tuning-Rules-in-Frequency-Domain)",
"section": "5.4 Controller Tuning Rules in Frequency Domain"
}
},
"source": [
"# 5.4 Controller Tuning Rules in Frequency Domain\n",
"\n",
"Demonstrate the use of a Bode plot for controller tuning. The notebook uses the [Python Control Systems Library](https://github.com/python-control/python-control). "
]
},
{
"cell_type": "markdown",
"metadata": {
"nbpages": {
"level": 2,
"link": "[5.4.1 Closed-Loop Transfer Functions](https://jckantor.github.io/CBE30338/05.04-Controller-Tuning-Rules-in-Frequency-Domain.html#5.4.1-Closed-Loop-Transfer-Functions)",
"section": "5.4.1 Closed-Loop Transfer Functions"
}
},
"source": [
"## 5.4.1 Closed-Loop Transfer Functions"
]
},
{
"cell_type": "markdown",
"metadata": {
"nbpages": {
"level": 3,
"link": "[5.4.1.1 Process Transfer Functions](https://jckantor.github.io/CBE30338/05.04-Controller-Tuning-Rules-in-Frequency-Domain.html#5.4.1.1-Process-Transfer-Functions)",
"section": "5.4.1.1 Process Transfer Functions"
}
},
"source": [
"### 5.4.1.1 Process Transfer Functions\n",
"\n",
"The process output $y$ is modeled as the output of a linear system described by the transfer function \n",
"\n",
"$$y = G_p(s)G_v(s) u + G_d(s) d$$\n",
"\n",
"where $G_p(s)$ is the process response to a manipulated variable, $G_v(s)$ is the transfer function of the actuator dynamics, and $G_d(s)$ is process response to a disturbance. The measured process output is given by\n",
"\n",
"$$y_m = G_m(s) y$$\n",
"\n",
"where $G_m(s)$ is the sensor transfer function. We assume the sensor has unit gain, i.e., $G_m(0) = 1$"
]
},
{
"cell_type": "markdown",
"metadata": {
"nbpages": {
"level": 3,
"link": "[5.4.1.2 Control Transfer Functions](https://jckantor.github.io/CBE30338/05.04-Controller-Tuning-Rules-in-Frequency-Domain.html#5.4.1.2-Control-Transfer-Functions)",
"section": "5.4.1.2 Control Transfer Functions"
}
},
"source": [
"### 5.4.1.2 Control Transfer Functions\n",
"\n",
"The controller generates a command $u$ in response to the reference input $r$ and measured process output $y_m$. This is modeled as\n",
"\n",
"$$u = G_r(s)r - G_y(s)y_m$$\n",
"\n",
"where $G_r(s)$ and $G_y(s)$ are the controller transfer functions. Textbook simulations often set $G_r(s) = G_y(s)$ for PID control. In practice, however, it is generally desireable to avoid excessive action in response to setpoint changes, so generally $G_r \\neq G_y$.\n",
"\n",
"Below we employ a parallel implementation of PID control for output feedback where\n",
"\n",
"$$G_y(s) = K_p\\left[ 1 + \\frac{1}{\\tau_Is} + \\frac{\\tau_D s}{\\alpha\\tau_Ds + 1}\\right]$$\n",
"\n",
"where parameter $\\alpha$ limits excessive derivative action. Typical values of $\\alpha$ are in the range 0.2 to 0.1.\n",
"\n",
"For the reference signal, $G_r(s)$ has the same general form but with additional parameters $\\beta$ and $\\gamma$\n",
"\n",
"$$G_r(s) = K_p\\left[ \\beta + \\frac{1}{\\tau_Is} + \\gamma\\frac{\\tau_D s}{\\alpha\\tau_Ds + 1}\\right]$$\n",
"\n",
"Commonly $\\gamma = 0$ to avoid derivative action on the reference signal. Parameter $\\beta$ is typically set to a value $0\\leq\\beta\\leq 1$ that balances prompt response to a setpoint against undue 'kicks' caused by sudden changes in setpoint."
]
},
{
"cell_type": "markdown",
"metadata": {
"nbpages": {
"level": 3,
"link": "[5.4.1.3 Closed Loop Transfer Functions](https://jckantor.github.io/CBE30338/05.04-Controller-Tuning-Rules-in-Frequency-Domain.html#5.4.1.3-Closed-Loop-Transfer-Functions)",
"section": "5.4.1.3 Closed Loop Transfer Functions"
}
},
"source": [
"### 5.4.1.3 Closed Loop Transfer Functions\n",
"\n",
"The overall response of the closed-loop system is characterized by a set of four transfer functions relating the reference and disturbance inputs to the process output $y$ and the control command $u$.\n",
"\n",
"$$\\begin{align*}\n",
"y & = H_{yr}(s)r + H_{yd}(s)d \\\\\n",
"u & = H_{ur}(s)r + H_{ud}(s)d\n",
"\\end{align*}\n",
"$$\n",
"\n",
"The closed-loop transfer functions are given explicitly as \n",
"\n",
"$$H_{yr} = \\frac{G_pG_vG_r}{1 + G_pG_vG_yG_m}$$\n",
"\n",
"$$H_{yd} = \\frac{G_d}{1 + G_pG_vG_yG_m}$$\n",
"\n",
"$$H_{ur} = \\frac{G_r}{1 + G_yG_mG_pG_v}$$\n",
"\n",
"$$H_{ud} = -\\frac{G_yG_mG_d}{1 + G_yG_mG_pG_v}$$\n",
"\n",
"where the argument $s$ has been suppressed for brevity."
]
},
{
"cell_type": "markdown",
"metadata": {
"nbpages": {
"level": 2,
"link": "[5.4.2 Example](https://jckantor.github.io/CBE30338/05.04-Controller-Tuning-Rules-in-Frequency-Domain.html#5.4.2-Example)",
"section": "5.4.2 Example"
}
},
"source": [
"## 5.4.2 Example\n",
"\n",
"We'll assume a process transfer function with time delay\n",
"\n",
"$$G_p(s) = \\frac{0.2}{s^2 + 1.5 s + 1}$$\n",
"\n",
"and a disturbance response\n",
"\n",
"$$G_d(s) = \\frac{1}{s + 1}$$\n",
"\n",
"and a measurement transfer function\n",
"\n",
"$$G_m(s) = e^{-s}$$\n",
"\n",
"We'll assume the dynamics of the actuator are negligible such that $G_v(s) = 1$. \n",
"\n",
"The Python Control Systems Library does not provide a specific representation for time delay. It does, however, provide a function `pade` for creating Pade approximations to time delay systems."
]
},
{
"cell_type": "markdown",
"metadata": {
"nbpages": {
"level": 3,
"link": "[5.4.2.1 Entering Process Transfer Functions](https://jckantor.github.io/CBE30338/05.04-Controller-Tuning-Rules-in-Frequency-Domain.html#5.4.2.1-Entering-Process-Transfer-Functions)",
"section": "5.4.2.1 Entering Process Transfer Functions"
}
},
"source": [
"### 5.4.2.1 Entering Process Transfer Functions"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"nbpages": {
"level": 3,
"link": "[5.4.2.1 Entering Process Transfer Functions](https://jckantor.github.io/CBE30338/05.04-Controller-Tuning-Rules-in-Frequency-Domain.html#5.4.2.1-Entering-Process-Transfer-Functions)",
"section": "5.4.2.1 Entering Process Transfer Functions"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gp = \n",
" 0.2\n",
"---------------\n",
"s^2 + 1.5 s + 1\n",
"\n",
"Gd = \n",
" 1\n",
"-----\n",
"s + 1\n",
"\n",
"Gv = \n",
"1\n",
"-\n",
"1\n",
"\n",
"Gm = \n",
"-s^3 + 12 s^2 - 60 s + 120\n",
"--------------------------\n",
"s^3 + 12 s^2 + 60 s + 120\n",
"\n"
]
}
],
"source": [
"%matplotlib inline\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"from control.matlab import *\n",
"\n",
"Gp = tf([0.2],[1, 1.5, 1])\n",
"print(\"Gp = \", Gp)\n",
"\n",
"Gd = tf([1],[1,1])\n",
"print(\"Gd = \", Gd)\n",
"\n",
"Gv = tf([1],[1])\n",
"print(\"Gv = \", Gv)\n",
"\n",
"(num,den) = pade(1.0,3)\n",
"Gm = tf(num,den)\n",
"print(\"Gm = \", Gm)"
]
},
{
"cell_type": "markdown",
"metadata": {
"nbpages": {
"level": 3,
"link": "[5.4.2.2 Finding Cross-Over Frequency and Gain at Cross-Over](https://jckantor.github.io/CBE30338/05.04-Controller-Tuning-Rules-in-Frequency-Domain.html#5.4.2.2-Finding-Cross-Over-Frequency-and-Gain-at-Cross-Over)",
"section": "5.4.2.2 Finding Cross-Over Frequency and Gain at Cross-Over"
}
},
"source": [
"### 5.4.2.2 Finding Cross-Over Frequency and Gain at Cross-Over"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"nbpages": {
"level": 3,
"link": "[5.4.2.2 Finding Cross-Over Frequency and Gain at Cross-Over](https://jckantor.github.io/CBE30338/05.04-Controller-Tuning-Rules-in-Frequency-Domain.html#5.4.2.2-Finding-Cross-Over-Frequency-and-Gain-at-Cross-Over)",
"section": "5.4.2.2 Finding Cross-Over Frequency and Gain at Cross-Over"
}
},
"outputs": [
{
"data": {
"text/plain": [
"
"
]
}
],
"metadata": {
"anaconda-cloud": {},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}