{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "*This notebook contains course material from [CBE30338](https://jckantor.github.io/CBE30338)\n", "by Jeffrey Kantor (jeff at nd.edu); the content is available [on Github](https://github.com/jckantor/CBE30338.git).\n", "The text is released under the [CC-BY-NC-ND-4.0 license](https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode),\n", "and code is released under the [MIT license](https://opensource.org/licenses/MIT).*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "< [PID Controller Tuning](http://nbviewer.jupyter.org/github/jckantor/CBE30338/blob/master/notebooks/04.06-PID-Controller-Tuning.ipynb) | [Contents](toc.ipynb) | [Implementing PID Control in Nonlinear Simulations](http://nbviewer.jupyter.org/github/jckantor/CBE30338/blob/master/notebooks/04.11-Implementing-PID-Control-in-Nonlinear-Simulations.ipynb) >

\"Open

\"Download\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# PID Control - Laboratory" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## PID Control\n", "\n", "The following Python code creates a PID generator that can be use for feedback control. This the same generator we developed in class on Thursday, with an additional filter used for a more robust implementation of derivative action. In today's laboratory you will use this code to test several aspects of feedback control of the TCLab heater." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def PID(Kp, Ki, Kd, MV_bar=0, beta=1, gamma=0):\n", " # initialize stored data\n", " t_prev = -100\n", " P = 0\n", " I = 0\n", " D = 0\n", " S = 0\n", " N = 10\n", " \n", " # initial control\n", " MV = MV_bar\n", " \n", " while True:\n", " # yield MV, wait for new t, SP, PV, TR\n", " data = yield MV\n", " \n", " # see if a tracking data is being supplied\n", " if len(data) < 4:\n", " t, SP, PV = data\n", " else:\n", " t, SP, PV, TR = data\n", " I = TR - MV_bar - P - D\n", " \n", " # PID calculations\n", " P = Kp*(beta*SP - PV)\n", " I = I + Ki*(SP - PV)*(t - t_prev)\n", " eD = gamma*SP - PV\n", " D = N*Kp*(Kd*eD - S)/(Kd + N*Kp*(t - t_prev))\n", " MV = MV_bar + P + I + D\n", " \n", " # Constrain MV to range 0 to 100 for anti-reset windup\n", " MV = 0 if MV < 0 else 100 if MV > 100 else MV\n", " I = MV - MV_bar - P - D\n", " \n", " # update stored data for next iteration\n", " S = D*(t - t_prev) + S\n", " t_prev = t" ] }, { "cell_type": "markdown", "metadata": { "tags": [ "exercise" ] }, "source": [ "## Exercise 1. Is the transfer from manual to automatic bumpless?\n", "\n", "The cell below is a starter code to implement feedback control. The code establishes a setpoint of 40°C, and for the first 30 seconds runs under manual control. The control then transfers to automatic. Do you observe bumpless transfer? Explain what you see." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": false }, "outputs": [], "source": [ "%matplotlib inline\n", "from tclab import clock, setup, Historian, Plotter\n", "\n", "TCLab = setup(connected=True)\n", "\n", "controller = PID(Kp=2, Ki=0.1, Kd=2, beta=0) # create pid control\n", "controller.send(None) # initialize\n", "\n", "tfinal = 600\n", "SP = 40\n", "\n", "with TCLab() as lab:\n", " sources = [('SP', lambda: SP), ('T1', lambda: lab.T1), ('MV', lambda: MV), ('Q1', lab.Q1)]\n", " h = Historian(sources)\n", " p = Plotter(h, tfinal)\n", " for t in clock(tfinal, 2):\n", " PV = lab.T1 # get measurement\n", " MV = controller.send([t, SP, PV, lab.U1]) # for bumpless transfer\n", " if t < 100: # choice between manual and auto control\n", " MV = 50\n", " lab.U1 = MV # apply \n", " p.update(t) # update information display" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Looking for Arduino on ...\n", "NHduino found on port /dev/cu.wchusbserial1410\n", "\n", "Testing TCLab object in debug mode\n", "----------------------------------\n", "TCLab version 0.4.6dev\n", "Sent: \"Q1 0\"\n", "Return: \"\"\n", "Sent: \"Q1 0\"\n", "Return: \"0\"\n", "Could not connect at high speed, but succeeded at low speed.\n", "This may be due to an old TCLab firmware.\n", "New Arduino TCLab firmware available at:\n", " https://github.com/jckantor/TCLab-sketch\n", "Sent: \"VER\"\n", "Return: \"TCLab Firmware Version 1.2.1\"\n", "NHduino connected on port /dev/cu.wchusbserial1410 at 9600 baud.\n", "TCLab Firmware Version 1.2.1.\n", "Sent: \"Q2 0\"\n", "Return: \"0\"\n", "Reading temperature\n", "Sent: \"T1\"\n", "Return: \"27.02\"\n", "27.02\n", "Sent: \"Q1 0\"\n", "Return: \"0\"\n", "Sent: \"Q2 0\"\n", "Return: \"0\"\n", "Sent: \"X\"\n", "Return: \"X\"\n", "TCLab disconnected successfully.\n", "\n", "Testing TCLab functions\n", "-----------------------\n", "TCLab version 0.4.6dev\n", "Could not connect at high speed, but succeeded at low speed.\n", "This may be due to an old TCLab firmware.\n", "New Arduino TCLab firmware available at:\n", " https://github.com/jckantor/TCLab-sketch\n", "NHduino connected on port /dev/cu.wchusbserial1410 at 9600 baud.\n", "TCLab Firmware Version 1.2.1.\n", "Testing LED. Should turn on for 10 seconds.\n", "Countdown: 0 \n", "\n", "Reading temperatures\n", "T1 = 26.05 °C, T2 = 26.05 °C\n", "\n", "We will now turn on the heaters, wait 30 seconds and see if the temperatures have gone up. \n", "Countdown: 0 \n", "\n", "T1 started a 26.05 °C and went to 31.86 °C\n", "T2 started a 26.05 °C and went to 28.63 °C\n", "\n", "TCLab disconnected successfully.\n", "\n", "Diagnostics complete\n" ] } ], "source": [ "from tclab import diagnose\n", "\n", "diagnose()" ] }, { "cell_type": "markdown", "metadata": { "tags": [ "exercise" ] }, "source": [ "## Exercise 2\n", "\n", "Create a new experiment in the cell below. The new experiment should do the following:\n", "\n", "* Add a second PID controller for the second heater. The control constants should be Kp=4, Ki=0.2, Kd=2, beta=0.\n", "* Should start up under automatic control with setpoints of 40 deg C for both heaters. \n", "* At t = 100, the setpoint for heater 1 should be raised to 50 deg C\n", "* You will need to modify the 'sources' line to include the additional data. Please see an instructor if you need help with this.\n", "\n", "Run the experiment for at least 800 seconds. How would describe the behavior at the point when the setpoint is changed?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Looking for Arduino on ...\n", "NHduino found on port /dev/cu.wchusbserial1410\n", "\n", "Testing TCLab object in debug mode\n", "----------------------------------\n", "TCLab version 0.4.6dev\n", "Sent: \"Q1 0\"\n", "Return: \"\"\n", "Sent: \"Q1 0\"\n", "Return: \"0\"\n", "Could not connect at high speed, but succeeded at low speed.\n", "This may be due to an old TCLab firmware.\n", "New Arduino TCLab firmware available at:\n", " https://github.com/jckantor/TCLab-sketch\n", "Sent: \"VER\"\n", "Return: \"TCLab Firmware Version 1.2.1\"\n", "NHduino connected on port /dev/cu.wchusbserial1410 at 9600 baud.\n", "TCLab Firmware Version 1.2.1.\n", "Sent: \"Q2 0\"\n", "Return: \"0\"\n", "Reading temperature\n", "Sent: \"T1\"\n", "Return: \"21.54\"\n", "21.54\n", "Sent: \"Q1 0\"\n", "Return: \"0\"\n", "Sent: \"Q2 0\"\n", "Return: \"0\"\n", "Sent: \"X\"\n", "Return: \"X\"\n", "TCLab disconnected successfully.\n", "\n", "Testing TCLab functions\n", "-----------------------\n", "TCLab version 0.4.6dev\n", "Could not connect at high speed, but succeeded at low speed.\n", "This may be due to an old TCLab firmware.\n", "New Arduino TCLab firmware available at:\n", " https://github.com/jckantor/TCLab-sketch\n", "NHduino connected on port /dev/cu.wchusbserial1410 at 9600 baud.\n", "TCLab Firmware Version 1.2.1.\n", "Testing LED. Should turn on for 10 seconds.\n", "Countdown: 0 \n", "\n", "Reading temperatures\n", "T1 = 22.19 °C, T2 = 22.51 °C\n", "\n", "We will now turn on the heaters, wait 30 seconds and see if the temperatures have gone up. \n", "Countdown: 5 " ] } ], "source": [ "from tclab import diagnose\n", "diagnose()\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "< [PID Controller Tuning](http://nbviewer.jupyter.org/github/jckantor/CBE30338/blob/master/notebooks/04.06-PID-Controller-Tuning.ipynb) | [Contents](toc.ipynb) | [Implementing PID Control in Nonlinear Simulations](http://nbviewer.jupyter.org/github/jckantor/CBE30338/blob/master/notebooks/04.11-Implementing-PID-Control-in-Nonlinear-Simulations.ipynb) >

\"Open

\"Download\"" ] } ], "metadata": { "celltoolbar": "Tags", "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 }