# 67. Minimal integral measures¶

One approach to finding controller parameters is to minimise some error measure with respect to the parameters. We will simulate a first order plus dead time system under PI control. The block diagram here is for simple feedback:

[1]:

import numpy
import scipy.signal
import scipy.optimize
import matplotlib.pyplot as plt
from tbcontrol import blocksim
%matplotlib inline

[2]:

# This is the 1,1 element of a Wood and Berry column (see eq 16-12)
K = 12.8
tau = 16.7
theta = 1

[3]:

ts = numpy.linspace(0, 2*tau, 500)

[4]:

ysp = 1

[5]:

Kc = 1
tau_i = 1

[6]:

def response(Kc, tau_i):
Gp = blocksim.LTI('G', 'u', 'y', [K], [tau, 1], theta)
Gc = blocksim.PI('Gc', 'e', 'u', Kc, tau_i)

blocks = [Gp, Gc]
inputs = {'ysp': lambda t: ysp}
sums = {'e': ('+ysp', '-y')}

diagram = blocksim.Diagram(blocks, sums, inputs)

return numpy.array(diagram.simulate(ts)['y'])


What does the setpoint response look like?

[7]:

for Kc in [0.5, 1, 2]:
plt.plot(ts, response(Kc, 10), label='$K_c={}$'.format(Kc))
plt.axhline(ysp, label='$y_{sp}$')
plt.legend()

[7]:

<matplotlib.legend.Legend at 0x1c17a02e80>


These are the error measures in the book (eq 11-35 to 11-37). Note that the syntax f(*parameters) with parameters=(1, 2) is equivalent to f(1, 2).

[8]:

def iae(parameters):
return scipy.integrate.trapz(numpy.abs(response(*parameters) - ysp), ts)

[9]:

def ise(parameters):
return scipy.integrate.trapz((response(*parameters) - ysp)**2, ts)

[10]:

def itae(parameters):
return scipy.integrate.trapz(numpy.abs(response(*parameters) - ysp)*ts, ts)

[11]:

errfuns = [iae, ise, itae]


Now we can find the optimal parameters for the various error measures.

[12]:

%%time
optimal_parameters = {}
for error in errfuns:
name = error.__name__.upper()
optimal_parameters[name] = scipy.optimize.minimize(error, [1, 10]).x
print(name, *optimal_parameters[name])
plt.plot(ts, response(*optimal_parameters[name]), label=name)
plt.axhline(1, label='setpoint')
plt.legend(loc='best')

IAE 0.6639499654277579 16.700104675285132
ISE 0.8519796593454986 25.498163370798167
ITAE 0.5856442797588636 16.69999502290863
CPU times: user 1min 26s, sys: 3 s, total: 1min 29s
Wall time: 1min 35s


We could also have used table 11.3, which is automated by a function in tbcontrol (see the ITAE parameters for FOPDT system notebook for more information on this function)

[14]:

from tbcontrol.fopdtitae import parameters

[15]:

Kc, tau_i = parameters(K, tau, theta, "Set point", "PI")

print(Kc, tau_i)

0.6035259299979403 16.370626907724816


These values correspond approximately with those found through direct minimisation.

[16]:

plt.plot(ts, response(Kc, tau_i), label='Table 11.3')
plt.plot(ts, response(*optimal_parameters['ITAE']), label='Minimisation')
plt.legend()

[16]:

<matplotlib.legend.Legend at 0x1c1952be10>

[17]:

itae([Kc, tau_i])

[17]:

4.025429557262326

[18]:

itae(optimal_parameters['ITAE'])

[18]:

3.644873223762508


Note that the error we obtained via direct minimisation was lower than the one obtained via the table, as they have fitted a curve through the results.