{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Padé approximation\n", "\n", "*Todo: derivation and visuals of different approx orders and their accuracy, elaborate on explainations, intuition on the mathematical form*\n", "\n", "Time delays are very common in process control. For example, they arise from delays in (1) measuring a process variable, because the sensor is physically located some distance away from the point of interest or (2) controlling a process variable, because instantaneous movement of valves and other physical devices is practically impossible.\n", "\n", "The time delay of a transfer function is:\n", "\n", "$$ H(s) = e^{-\\theta s} $$\n", "\n", "Most transfer functions we encounter are **rational functions**, given as a ratio of 2 polynomials, $P$ and $Q$:\n", "\n", "$$ H(s) = \\frac{P(s)}{Q(s)} = \\frac{a_ps^p + a_{p-1}s^{p-1} + \\dots + a_0}{b_qs^q + b_{q-1}s^{q-1} + \\dots + b_0} $$\n", "\n", "Rational transfer functions have some nice properties. It is easy to find their poles and zeros and inverse Laplace transform. However, with a time delay, $e^{-\\theta s}$ term in the transfer function, it is no longer rational and we lose those convenient properties.\n", "\n", "## Approximating the time delay\n", "In these situations, we use a Padé approximation to find a rational approximation to $e^{-\\theta s}$.\n", "\n", "TODO: [content here]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Example" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Given a transfer function with a 2-second delay:\n", "\n", "$$ H(s) = \\frac{1}{5s+1} e^{-2s} $$\n", "\n", "We can define this system in `python.control` by doing the following" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import control" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `control.pade` function takes in 3 arguments:\n", "\n", "- The delay, $\\theta$\n", "- The denominator degree, `n`\n", "- The numerator degree, `numdeg`\n", "\n", "and returns the numerator and denominator coefficients in a tuple." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "([-1.0, 0.2], [1.0, 0.2])" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "theta = 10\n", "n=1\n", "numdeg=1\n", "\n", "coeffs = control.pade(theta, n, numdeg)\n", "coeffs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can create our approximation of the delay by doing:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\n", "-s + 0.2\n", "--------\n", "s + 0.2" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sys_delay = control.tf(coeffs[0],coeffs[1])\n", "sys_delay" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The system transfer function is" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "\n", " 1\n", "-------\n", "5 s + 1" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sys = control.tf([1],[5,1])\n", "sys" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The combined transfer function is" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\n", " -s + 0.2\n", "-----------------\n", "5 s^2 + 2 s + 0.2" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g = sys * sys_delay\n", "g" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Python tips\n", "Notice that the `control.pade` returns a tuple of 2 items, the numerator and denominator coefficients. Instead of storing the tuple in `g_delay`, we can *unpack* the tuple and improve code readibility by doing:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": true }, "outputs": [], "source": [ "(num, deg) = control.pade(theta, n, numdeg)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\n", "-s + 0.2\n", "--------\n", "s + 0.2" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "control.tf(num, deg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, we can also use the `asterisk(*)` method to unpack the tuple in the function argument:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": true }, "outputs": [], "source": [ "coeffs = control.pade(theta, n, numdeg)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\n", "-s + 0.2\n", "--------\n", "s + 0.2" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "control.tf(*coeffs) # Equivalent to control.tf(g_delay[0],g_delay[1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Further reading:\n", "\n", "- Find out more about the unpacking operator here: https://codeyarns.com/2012/04/26/unpack-operator-in-python/\n", "- http://inside.mines.edu/~jjechura/ProcessDynamics/09_HigherOrderSystems.pdf\n", "- https://www.embeddedrelated.com/showarticle/927.php" ] } ], "metadata": { "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.6.1" } }, "nbformat": 4, "nbformat_minor": 2 }