https://www.halvorsen.blog Python for Control Engineering Hans-Petter Halvorsen Free Textbook with lots of Practical Examples https://www.halvorsen.blog/documents/programming/python/ Additional Python Resources https://www.halvorsen.blog/documents/programming/python/ Contents • Introduction to Control Engineering • Python Libraries useful in Control Engineering Applications – NumPy, Matplotlib – SciPy (especially scipy.signal) – Python Control Systems Library (control) • Python Examples • Additional Tutorials/Videos/Topics Additional Tutorials/Videos/Topics This Tutorial is only the beginning. Some Examples of Tutorials that goes more in depth: • Transfer Functions with Python • State-space Models with Python Videos available • Frequency Response with Python on YouTube • PID Control with Python • Stability Analysis with Python • Frequency Response Stability Analysis with Python • Logging Measurement Data to File with Python • Control System with Python – Exemplified using Small-scale Industrial Processes and Simulators • • DAQ Systems etc. https://www.halvorsen.blog/documents/programming/python/ https://www.halvorsen.blog Python Libraries Hans-Petter Halvorsen NumPy, Matplotlib • In addition to Python itself, the Python libraries NumPy, Matplotlib is typically needed in all kind of application • If you have installed Python using the Anaconda distribution, these are already installed SciPy.signal • An alternative to The Python Control Systems Library is SciPy.signal, i.e. the Signal Module in the SciPy Library • https://docs.scipy.org/doc/scipy/reference/signal.html With SciPy.signal you can create Transfer Functions, State-space Models, you can simulate dynamic systems, do Frequency Response Analysis, including Bode plot, etc. Python Control Systems Library • The Python Control Systems Library (control) is a Python package that implements basic operations for analysis and design of feedback control systems. • Existing MATLAB user? The functions and the features are very similar to the MATLAB Control Systems Toolbox. • Python Control Systems Library Homepage: https://pypi.org/project/control • Python Control Systems Library Documentation: https://python-control.readthedocs.io https://www.halvorsen.blog Control Engineering Hans-Petter Halvorsen Control System Noise/Disturbance π π − π£ The Controller is typically a PID Controller Reference Value Controller π’ Actuators Control Signal π¦ Filtering Process π₯ Process Output Sensors Control System • π – Reference Value, SP (Set-point), SV (Set Value) • π¦ – Measurement Value (MV), Process Value (PV) • π – Error between the reference value and the measurement value (π = π – π¦) • π£ – Disturbance, makes it more complicated to control the process • π’ - Control Signal from the Controller The PID Algorithm & πΎ# ( πππ + πΎ# π' πΜ π’ π‘ = πΎ# π + π$ % Where π’ is the controller output and π is the control error: π π‘ = π π‘ − π¦(π‘) π is the Reference Signal or Set-point π¦ is the Process value, i.e., the Measured value Tuning Parameters: πΎ! π" π# Proportional Gain Integral Time [sec. ] Derivative Time [sec. ] The PID Algorithm & πΎ# π’ π‘ = πΎ# π + ( πππ + πΎ# π' πΜ π$ % P Proportional Gain Tuning Parameters: πΎ% I Integral Time π& D Derivative Time π' https://www.halvorsen.blog Python Examples Hans-Petter Halvorsen https://www.halvorsen.blog Dynamic Systems and Differential Equations Hans-Petter Halvorsen Dynamic Systems and Models • The purpose with a Control System is to Control a Dynamic System, e.g., an industrial process, an airplane, a self-driven car, etc. (a Control System is “everywhere“). • Typically we start with a Mathematical model of such as Dynamic System • The mathematical model of such a system can be – A Differential Equation – A Transfer Function – A State-space Model • We use the Mathematical model to create a Simulator of the system 1.order Dynamic System Assume the following general Differential Equation: Input Signal π¦Μ = ππ¦ + ππ’ π’(π‘) or: 1 π¦Μ = (−π¦ + πΎπ’) π Where π = − ! " and π = Dynamic System Output Signal π¦(π‘) # " Where πΎ is the Gain and π is the Time constant This differential equation represents a 1. order dynamic system Assume π’(π‘) is a step (π), then we can find that the solution to the differential equation is: π¦ π‘ = πΎπ(1 − π $ % ") (we use Laplace) Python We start by plotting the following: π¦ π‘ = 0 /1 πΎπ(1 − π ) In the Python code we can set: π=1 πΎ=3 π=4 import numpy as np import matplotlib.pyplot as plt K = 3 T = 4 start = 0 stop = 30 increment = 0.1 t = np.arange(start,stop,increment) y = K * (1-np.exp(-t/T)) plt.plot(t, y) plt.title('1.order Dynamic System') plt.xlabel('t [s]') plt.ylabel('y(t) ') plt.grid() Comments We have many different options when it comes to simulation a Dynamic System: • We can solve the differential Equation(s) and then implement the the algebraic solution and plot it. – This solution may work for simple systems. For more complicated systems it may be difficult to solve the differential equation(s) by hand • We can use one of the “built-in” ODE solvers in Python • We can make a Discrete version of the system • We can convert the differential equation(s) to Transfer Function(s) • etc. We will demonstrate and show examples of all these approaches Python Differential Equation: Using ODE Solver 1 π¦Μ = (−π¦ + πΎπ’) π In the Python code we can set: πΎ=3 π=4 import numpy as np from scipy.integrate import odeint import matplotlib.pyplot as plt # Initialization K = 3 T = 4 u = 1 tstart = 0 tstop = 25 increment = 1 y0 = 0 t = np.arange(tstart,tstop+1,increment) # Function that returns dx/dt def system1order(y, t, K, T, u): dydt = (1/T) * (-y + K*u) return dydt # Solve ODE x = odeint(system1order, y0, t, args=(K, T, u)) print(x) # Plot the Results plt.plot(t,x) plt.title('1.order System dydt=(1/T)*(-y+K*u)') plt.xlabel('t') plt.ylabel('y(t)') plt.grid() plt.show() Discretization We start with the differential equation: π¦Μ = ππ¦ + ππ’ We can use the Euler forward method: π¦456 − π¦4 π¦Μ ≈ π7 This gives: 8!"# /8! 1$ = ππ¦4 + ππ’4 Further we get: π¦456 = π¦4 + π7 ππ¦4 + ππ’4 π¦456 = π¦4 + π7 ππ¦4 + π7 ππ’4 This gives the following discrete differential equation: π¦$%& = (1 + π' π)π¦$ + π' ππ’$ Python Let's simulate the discrete system: π¦()* = (1 + π+ π)π¦( + π+ ππ’( Where π = − ! " and π = # " In the Python code we can set: πΎ=3 π=4 import numpy as np import matplotlib.pyplot as plt # Model Parameters K = 3 T = 4 a = -1/T b = K/T # Simulation Parameters Ts = 0.1 Tstop = 30 uk = 1 # Step Response yk = 0 # Initial Value N = int(Tstop/Ts) # Simulation length data = [] data.append(yk) # Simulation for k in range(N): yk1 = (1 + a*Ts) * yk yk = yk1 data.append(yk1) + Ts * b * uk # Plot the Simulation Results t = np.arange(0,Tstop+Ts,Ts) plt.plot(t,data) plt.title('1.order Dynamic System') plt.xlabel('t [s]') plt.ylabel('y(t)') plt.grid() https://www.halvorsen.blog Transfer Functions Hans-Petter Halvorsen Transfer Functions • Transfer functions are a model form based on the Laplace transform. • Transfer functions are very useful in analysis and design of linear dynamic systems. • You can create Transfer Functions both with SciPy.signal and the Python Control Systems Library 1.order Transfer Functions A 1.order transfer function is given by: π¦(π ) πΎ π» π = = π’(π ) ππ + 1 Where πΎ is the Gain and π is the Time constant In the time domain we get the following equation (using Inverse Laplace): π¦ π‘ = πΎπ(1 − ) ( π *) (After a Step π for the unput signal π’(π )) Output Signal Input Signal π’(π ) Differential Equation π» π π¦(π ) 1 π¦Μ = (−π¦ + πΎπ’) π We ca find the Transfer function from the Differential Equation using Laplace 1.order – Step Response 100% π¦(π‘) πΎπ π¦ π‘ = 63% 0 /1 πΎπ(1 − π ) π¦(π ) πΎ π» π = = π’(π ) ππ + 1 π‘ π Python Transfer Function: 3 π»(π ) = 4π + 1 import control import numpy as np import matplotlib.pyplot as plt K = T = num den 3 4 = np.array([K]) = np.array([T , 1]) H = control.tf(num , den) print ('H(s) =', H) t, y = control.step_response(H) plt.plot(t,y) plt.title("Step Response") https://www.halvorsen.blog State-space Models Hans-Petter Halvorsen State-space Models System A general State-space Model is given by: π₯Μ = π΄π₯ + π΅π’ π¦ = πΆπ₯ + π·π’ Note that π₯Μ is the same as !" !# π’ Input Internal States π₯ Output π¦ π΄, π΅, πΆ and π· are matrices π₯, π₯,Μ π’, π¦ are vectors • A state-space model is a structured form or representation of a set of differential equations. State-space models are very useful in Control theory and design. The differential equations are converted in matrices and vectors. • You can create State.space Models both with SciPy.signal and the Python Control Systems Library Basic Example Given the following System: We want to put the equations on the following form: π₯Μ & = π₯+ π₯Μ + = −π₯+ + π’ π¦ = π₯& This gives the following State-space Model: π₯Μ * 0 1 π₯* 0 = + π’ π₯ π₯Μ , 0 −1 , 1 π₯* π¦= 1 0 π₯ , π₯Μ = π΄π₯ + π΅π’ π¦ = πΆπ₯ + π·π’ Where: 0 π΄= 0 1 −1 πΆ= 1 0 π₯Μ = π₯Μ ! π₯Μ & π΅= 0 1 π·= 0 π₯! π₯= π₯ & Python We have the differential equations: 1 π₯Μ ! = −π₯! + πΎπ’ π π₯Μ & = 0 π¦ = π₯! The State-space Model becomes: 1 πΎ π₯ π₯Μ ! ! = −π 0 π₯ + π π’ π₯Μ & & 0 0 0 π¦= 1 π₯! 0 π₯ & Here we use the following function: t, y = sig.step(sys, x0, t) import scipy.signal as sig import matplotlib.pyplot as plt import numpy as np #Simulation Parameters x0 = [0,0] start = 0 stop = 30 step = 1 t = np.arange(start,stop,step) K = 3 T = 4 # State-space Model A = [[-1/T, 0], [0, 0]] B = [[K/T], [0]] C = [[1, 0]] D = 0 sys = sig.StateSpace(A, B, C, D) # Step Response t, y = sig.step(sys, x0, t) # Plotting plt.plot(t, y) plt.title("Step Response") plt.xlabel("t") plt.ylabel("y") plt.grid() plt.show() Python State-space Model: 1 π₯Μ ! − = π π₯Μ " 0 π¦= 1 πΎ 0 π₯! + π π’ π₯" 0 0 π₯! 0 π₯ " We want to find the Transfer Function: π¦(π ) π» π = π’(π ) TransferFunctionContinuous( array([0.75, 0. ]), array([1. , 0.25, 0. ]), dt: None) Python give us the following: 0.75 π»(π ) = π + 0.25 3 π»(π ) = 4π + 1 Which is the same as import scipy.signal as sig import matplotlib.pyplot as plt import numpy as np #Simulation Parameters x0 = [0,0] start = 0; stop = 30; step = 1 t = np.arange(start,stop,step) K = 3; T = 4 # State-space Model A = [[-1/T, 0], [0, 0]] B = [[K/T], [0]] C = [[1, 0]] D = 0 sys = sig.StateSpace(A, B, C, D) H = sys.to_tf() print(H) # Step Response t, y = sig.step(H, x0, t) # Plotting plt.plot(t, y) plt.title("Step Response") plt.xlabel("t"); plt.ylabel("y") plt.grid() plt.show() https://www.halvorsen.blog Frequency Response Hans-Petter Halvorsen Frequency Response • The Frequency Response is an important tool for Analysis and Design of signal filters and for analysis and design of Control Systems • The frequency response can be found from a transfer function model • The Bode diagram gives a simple Graphical overview of the Frequency Response for a given system • The Bode Diagram is tool for Analyzing the Stability properties of the Control System. Python Transfer Function Example: import numpy as np import scipy.signal as signal import matplotlib.pyplot as plt SciPy.signal 3(2π + 1) π» π = (3π + 1)(5π + 1) # Define Transfer Function num1 = np.array([3]) num2 = np.array([2, 1]) num = np.convolve(num1, num2) den1 = np.array([3, 1]) den2 = np.array([5, 1]) den = np.convolve(den1, den2) H = signal.TransferFunction(num, den) print ('H(s) =', H) # Frequencies w_start = 0.01 w_stop = 10 step = 0.01 N = int ((w_stop-w_start )/step) + 1 w = np.linspace (w_start , w_stop , N) # Bode Plot w, mag, phase = signal.bode(H, w) plt.figure() plt.subplot (2, 1, 1) plt.semilogx(w, mag) # Bode Magnitude Plot plt.title("Bode Plot") plt.grid(b=None, which='major', axis='both') plt.grid(b=None, which='minor', axis='both') plt.ylabel("Magnitude (dB)") plt.subplot (2, 1, 2) plt.semilogx(w, phase) # Bode Phase plot plt.grid(b=None, which='major', axis='both') plt.grid(b=None, which='minor', axis='both') plt.ylabel("Phase (deg)") plt.xlabel("Frequency (rad/sec)") plt.show() Python Transfer Function Example: 3(2π + 1) π» π = (3π + 1)(5π + 1) Python Control Systems Library import numpy as np import control # Define Transfer Function num1 = np.array([3]) num2 = np.array([2, 1]) num = np.convolve(num1, num2) den1 = np.array([3, 1]) den2 = np.array([5, 1]) den = np.convolve(den1, den2) H = control.tf(num, den) print ('H(s) =', H) # Bode Plot control.bode(H, dB=True) https://www.halvorsen.blog PID Control Hans-Petter Halvorsen Control System The purpose with a Control System is to Control a Dynamic System, e.g., an industrial process, an airplane, a self-driven car, etc. (a Control System is “everywhere“). PID Controller Reference Value π π − π¦ Controller π’ Control Signal Process π¦ PID • The PID Controller is the most used controller today • It is easy to understand and implement • There are few Tuning Parameters The PID Algorithm & πΎ# ( πππ + πΎ# π' πΜ π’ π‘ = πΎ# π + π$ % Where π’ is the controller output and π is the control error: π π‘ = π π‘ − π¦(π‘) π is the Reference Signal or Set-point π¦ is the Process value, i.e., the Measured value Tuning Parameters: πΎ! π" π# Proportional Gain Integral Time [sec. ] Derivative Time [sec. ] Discrete PI Controller We start with the continuous PI Controller: πΎ$ # π’ π‘ = πΎ$ π + > πππ π% & We derive both sides in order to remove the Integral: π’Μ = πΎ( πΜ + πΎ( π π) We can use the Euler Backward Discretization method: π₯Μ ≈ π₯ π −π₯ π−1 π' Where π' is the Sampling Time Then we get: π’( − π’()* π( − π()* πΎ$ = πΎ$ + π π' π' π% ( Finally, we get: π’4 = π’4/6 + πΎC π4 − π4/6 Where π* = π* − π¦* πΎC + π7 π4 πD Control System Simulations PI Controller: Discrete Version (Ready to implement in Python): πΎ, / ( πππ π’ π‘ = πΎ, π + π- . π( = π( − π¦( π’( = π’(F* + πΎ% π( − π(F* πΎ% + π( π& Process (1.order system): π¦Μ = ππ¦ + ππ’ Where π = − ! " and π = # Discrete Version (Ready to implement in Python): π¦()* = (1 + π+ π)π¦( + π+ ππ’( " In the Python code we can set πΎ = 3 and π = 4 import numpy as np import matplotlib.pyplot as plt # K T a b Model Parameters = 3 = 4 = -(1/T) = K/T # Simulation Parameters Ts = 0.1 # Sampling Time Tstop = 20 # End of Simulation Time N = int(Tstop/Ts) # Simulation length y = np.zeros(N+2) # Initialization the Tout vector y[0] = 0 # Initial Vaue # PI Controller Settings Kp = 0.5 Ti = 5 r = 5 # Reference value e = np.zeros(N+2) # Initialization u = np.zeros(N+2) # Initialization # Simulation for k in range(N+1): e[k] = r - y[k] u[k] = u[k-1] + Kp*(e[k] - e[k-1]) + (Kp/Ti)*Ts*e[k] y[k+1] = (1+Ts*a)*y[k] + Ts*b*u[k] # Plot the Simulation Results t = np.arange(0,Tstop+2*Ts,Ts) #Create the Time Series Python # Plot Process Value plt.figure(1) plt.plot(t,y) # Formatting the appearance of the Plot plt.title('Control of Dynamic System') plt.xlabel('t [s]') plt.ylabel('y') plt.grid() xmin = 0 xmax = Tstop ymin = 0 ymax = 8 plt.axis([xmin, xmax, ymin, ymax]) plt.show() # Plot Control Signal plt.figure(2) plt.plot(t,u) # Formatting the appearance of the Plot plt.title('Control Signal') plt.xlabel('t [s]') plt.ylabel('u [V]') plt.grid() Python https://www.halvorsen.blog Stability Analysis Hans-Petter Halvorsen Stability Analysis Poles: Unstable System Im Marginally Stable System Im Asymptotically Stable System Im Re Re Re Step Response: π‘ lim π¦ π‘ = π #→, Frequency Response: π- < π*.& π‘ 0 < lim π¦ π‘ < ∞ #→, π- = π*.& π‘ lim π¦ π‘ = ∞ #→, π- > π*.& Stability Analysis Example π πΎ$ (π% π + 1) π»- π = π% π π − π¦F Controller Filter π»0 π = π’ 3 π»$ π = 4π + 1 1 π0 π + 1 Process π₯ Sensor π»/ 1 π = π/ π + 1 In Stability Analysis we use the following Transfer Functions: Loop Transfer Function: πΏ π = π»- (π )π»$ (π )π»/ (π )π»0 (π ) Tracking Transfer Function: π(π ) = 1(') 4(') = 5(') *65(') import numpy as np import matplotlib.pyplot as plt import control # Tracking transfer function T = control.feedback(L,1) print ('T(s) =', T) # Transfer Function Process K = 3; T = 4 num_p = np.array ([K]) den_p = np.array ([T , 1]) Hp = control.tf(num_p , den_p) print ('Hp(s) =', Hp) # Step Response Feedback System (Tracking System) t, y = control.step_response(T) plt.figure(1) plt.plot(t,y) plt.title("Step Response Feedback System T(s)") plt.grid() # Transfer Function PI Controller Kp = 0.4 Ti = 2 num_c = np.array ([Kp*Ti, Kp]) den_c = np.array ([Ti , 0]) Hc = control.tf(num_c, den_c) print ('Hc(s) =', Hc) # Bode Diagram with Stability Margins plt.figure(2) control.bode(L, dB=True, deg=True, margins=True) # Transfer Function Measurement Tm = 1 num_m = np.array ([1]) den_m = np.array ([Tm , 1]) Hm = control.tf(num_m , den_m) print ('Hm(s) =', Hm) # Transfer Function Lowpass Filter Tf = 1 num_f = np.array ([1]) den_f = np.array ([Tf , 1]) Hf = control.tf(num_f , den_f) print ('Hf(s) =', Hf) # The Loop Transfer function L = control.series(Hc, Hp, Hf, Hm) print ('L(s) =', L) # Poles and Zeros control.pzmap(T) p = control.pole(T) z = control.zero(T) print("poles = ", p) # Calculating stability margins and crossover frequencies gm , pm , w180 , wc = control.margin(L) # Convert gm to Decibel gmdb = 20 * np.log10(gm) print("wc =", f'{wc:.2f}', "rad/s") print("w180 =", f'{w180:.2f}', "rad/s") print("GM =", f'{gm:.2f}') print("GM =", f'{gmdb:.2f}', "dB") print("PM =", f'{pm:.2f}', "deg") # Find when Sysem is Marginally Stable (Kritical Gain Kc = Kp*gm print("Kc =", f'{Kc:.2f}') Kc) πΎ( = 0.4 π) = 2π Results Frequency Response Gain Margin (GM): ΔπΎ ≈ 11. ππ΅ Phase Margin (PM): φ ≈ 30° This means that we can increase πΎ# a bit without problem Poles Step Response As you see we have an Asymptotically Stable System The Critical Gain is πΎ+ = πΎ( × ΔπΎ = 1.43 Conclusions We have an Asymptotically Stable System when πΎ$ < πΎ• We have Poles in the left half plane • lim π¦ π‘ = 1 (Good Tracking) #→, • π- < π*.& We have a Marginally Stable System when πΎ$ = πΎ• We have Poles on the Imaginary Axis • 0 < lim π¦ π‘ < ∞ #→, • π- = π*.& We have an Unstable System when πΎ$ > πΎ• We have Poles in the right half plane • lim π¦ π‘ = ∞ #→, • π- > π*.& Additional Tutorials/Videos/Topics Want to learn more? Some Examples: • Transfer Functions with Python • State-space Models with Python Videos available • Frequency Response with Python on YouTube • PID Control with Python • Stability Analysis with Python • Frequency Response Stability Analysis with Python • Logging Measurement Data to File with Python • Control System with Python – Exemplified using Small-scale Industrial Processes and Simulators • DAQ Systems • etc. https://www.halvorsen.blog/documents/programming/python/ Additional Python Resources https://www.halvorsen.blog/documents/programming/python/ Hans-Petter Halvorsen University of South-Eastern Norway www.usn.no E-mail: hans.p.halvorsen@usn.no Web: https://www.halvorsen.blog