# Estimating Simple Models from Real Laboratory Process Data

This example shows how to develop and analyze simple models from a real laboratory process data. We start with a small description of the process, learn how to import the data to the toolbox and preprocess/condition it and then proceed systematically to estimate parametric and nonparametric models. Once the models have been identified we compare the estimated models and also validate the model to the actual output data from the experiment.

### System Description

This case study concerns data collected from a laboratory scale "hairdryer". (Feedback's Process Trainer PT326; See also page 525 in Ljung, 1999). The process works as follows: Air is fanned through a tube and heated at the inlet. The air temperature is measured by a thermocouple at the outlet. The input is the voltage over the heating device, which is just a mesh of resistor wires. The output is the outlet air temperature represented by the measured thermocouple voltage.

### Setting up Data for Analysis

First we load the input-output data to the MATLAB® Workspace.

```
load dryer2;
```

Vector `y2`

, the output, contains 1000 measurements of the thermocouple voltage which is proportional to the temperature in the outlet airstream. Vector `u2`

contains 1000 input data points consisting of the voltage applied to the heater. The input was generated as a binary random sequence that switches from one level to the other with probability 0.2. The sample time is 0.08 seconds.

The next step is to set up the data as an iddata object.

dry = iddata(y2,u2,0.08);

To get information about the data, just type the name of the `iddata`

object at the MATLAB command window:

dry

dry = Time domain data set with 1000 samples. Sample time: 0.08 seconds Outputs Unit (if specified) y1 Inputs Unit (if specified) u1

To inspect the properties of the above iddata object, use the `get`

command:

get(dry)

ans = struct with fields: Domain: 'Time' Name: '' OutputData: [1000x1 double] y: 'Same as OutputData' OutputName: {'y1'} OutputUnit: {''} InputData: [1000x1 double] u: 'Same as InputData' InputName: {'u1'} InputUnit: {''} Period: Inf InterSample: 'zoh' Ts: 0.0800 Tstart: 0.0800 SamplingInstants: [1000x1 double] TimeUnit: 'seconds' ExperimentName: 'Exp1' Notes: {} UserData: []

For better book-keeping, it is good practice to give names to the input and output channels and Time units. These names would be propagated throughout the analysis of this iddata object:

dry.InputName = 'Heater Voltage'; dry.OutputName = 'Thermocouple Voltage'; dry.TimeUnit = 'seconds'; dry.InputUnit = 'V'; dry.OutputUnit = 'V';

Now that we have the data set ready, we choose the first 300 data points for model estimation.

ze = dry(1:300)

ze = Time domain data set with 300 samples. Sample time: 0.08 seconds Outputs Unit (if specified) Thermocouple Voltage V Inputs Unit (if specified) Heater Voltage V

### Preprocessing the Data

Plot the interval from sample 200 to 300:

plot(ze(200:300));

**Figure 1:** A snapshot of the measured hair-dryer data.

From the above plot, it can be observed that the data is not zero mean. So let us remove the constant levels and make the data zero mean.

ze = detrend(ze);

The same data set after it has been detrended:

```
plot(ze(200:300)) %show samples from 200 to 300 of detrended data
```

**Figure 2:** Detrended estimation data.

### Estimating Nonparametric and Parametric Models

Now that the dataset has been detrended and there are no obvious outliers, let us first estimate the impulse response of the system by correlation analysis to get some idea of time constants and the like:

clf mi = impulseest(ze); % non-parametric (FIR) model showConfidence(impulseplot(mi),3); %impulse response with 3 standard %deviations confidence region

**Figure 3:** Impulse response of the FIR model estimated using `ze`

.

The shaded region marks a 99.7% confidence interval. There is a time delay (dead-time) of 3 samples before the output responds to the input (significant output outside the confidence interval).

The simplest way to get started on a parametric estimation routine is to build a state-space model where the model-order is automatically determined, using a prediction error method. Let us estimate a model using the `ssest`

estimation technique:

m1 = ssest(ze);

`m1`

is a continuous-time identified state-space model, represented by an `idss`

object. The estimation algorithm chooses 3 as the optimal order of the model. To inspect the properties of the estimated model, just enter the model name at the command window:

m1

m1 = Continuous-time identified state-space model: dx/dt = A x(t) + B u(t) + K e(t) y(t) = C x(t) + D u(t) + e(t) A = x1 x2 x3 x1 -0.4839 -2.011 -2.092 x2 3.321 -1.913 -5.998 x3 -1.623 17.01 -15.61 B = Heater Volta x1 -0.05753 x2 0.02004 x3 -1.377 C = x1 x2 x3 Thermocouple -14.07 0.07729 -0.04252 D = Heater Volta Thermocouple 0 K = Thermocouple x1 -0.9457 x2 -0.02097 x3 -2.102 Parameterization: FREE form (all coefficients in A, B, C free). Feedthrough: none Disturbance component: estimate Number of free coefficients: 18 Use "idssdata", "getpvec", "getcov" for parameters and their uncertainties. Status: Estimated using SSEST on time domain data "ze". Fit to estimation data: 95.32% (prediction focus) FPE: 0.001621, MSE: 0.001526

The display suggests that the model is free-form (all entries of A, B and C matrices were treated as free parameters) and that the estimated model fits the data pretty well (over 90% fit). To retrieve the properties of this model, for example to obtain the `A`

matrix of the discrete state-space object generated above, we can use the dot operator:

A = m1.a;

See the "Data and Model Objects in System Identification Toolbox" example for more information regarding model objects. To find out which properties of the model object can be retrieved, use `get`

command:

get(m1)

A: [3x3 double] B: [3x1 double] C: [-14.0706 0.0773 -0.0425] D: 0 K: [3x1 double] StateName: {3x1 cell} StateUnit: {3x1 cell} Structure: [1x1 pmodel.ss] NoiseVariance: 1.2587e-04 InputDelay: 0 OutputDelay: 0 InputName: {'Heater Voltage'} InputUnit: {'V'} InputGroup: [1x1 struct] OutputName: {'Thermocouple Voltage'} OutputUnit: {'V'} OutputGroup: [1x1 struct] Notes: [0x1 string] UserData: [] Name: '' Ts: 0 TimeUnit: 'seconds' SamplingGrid: [1x1 struct] Report: [1x1 idresults.ssest]

To fetch the values of the state-space matrices and their 1 standard deviation uncertainties, use the `idssdata`

command:

[A,B,C,D,K,~,dA,dB,dC,dD,dK] = idssdata(m1)

A = -0.4839 -2.0112 -2.0917 3.3205 -1.9135 -5.9981 -1.6235 17.0096 -15.6070 B = -0.0575 0.0200 -1.3770 C = -14.0706 0.0773 -0.0425 D = 0 K = -0.9457 -0.0210 -2.1019 dA = 1.0e+14 * 1.1092 0.5679 0.8159 1.4001 2.4015 2.4846 9.9026 7.8089 2.6170 dB = 1.0e+13 * 0.4454 1.3110 5.0386 dC = 1.0e+14 * 2.0523 1.5535 0.4749 dD = 0 dK = 1.0e+13 * 1.3329 5.0605 7.4934

The uncertainties are quite large even though the model fit the estimation data well. This is because the model is over-parameterized, that is, it has more free parameters than what could be uniquely identified. The variance of parameters in such cases is not well defined. However this does not imply that the model is unreliable. We can plot the time- and frequency-response of this plot and view the variance as confidence regions as discussed next.

### Analyzing the Estimated Model

The Bode plot of the generated model can be obtained using the `bode`

function as shown below:

h = bodeplot(m1);

**Figure 4:** Bode plot of estimated model.

Right-click on the plot and pick Characteristics->Confidence Region. Or, use the `showConfidence`

command to view the variance of the response.

```
showConfidence(h,3) % 3 standard deviation (99.7%) confidence region
```

**Figure 5:** Bode plot with 3 standard deviation confidence region.

Similarly, we can generate the step plot and its associated 3 standard deviation confidence region. We can compare the responses and associated variances of the parametric model `m1`

with that of the nonparametric model `mi`

:

showConfidence(stepplot(m1,'b',mi,'r',3),3)

**Figure 6:** Step plot of models `m1`

and `mi`

with confidence regions.

We can also consider the Nyquist plot, and mark uncertainty regions at certain frequencies with ellipses, corresponding to 3 standard deviations:

```
Opt = nyquistoptions;
Opt.ShowFullContour = 'off';
showConfidence(nyquistplot(m1,Opt),3)
```

**Figure 7:** Nyquist plot of estimated model showing the uncertainty regions at certain frequencies.

The response plots show that the estimated model `m1`

is quite reliable.

### Estimating Models with a Prescribed Structure

System Identification Toolbox™ can also be used to obtain a model with a prescribed structure. For example, a difference equation model with 2 poles, 1 zero and 3 sample delays can be obtained using the `arx`

function as shown below:

m2 = arx(ze,[2 2 3]);

To look at the model, enter the model name at the command window.

m2

m2 = Discrete-time ARX model: A(z)y(t) = B(z)u(t) + e(t) A(z) = 1 - 1.274 z^-1 + 0.3942 z^-2 B(z) = 0.06679 z^-3 + 0.04429 z^-4 Sample time: 0.08 seconds Parameterization: Polynomial orders: na=2 nb=2 nk=3 Number of free coefficients: 4 Use "polydata", "getpvec", "getcov" for parameters and their uncertainties. Status: Estimated using ARX on time domain data "ze". Fit to estimation data: 95.08% (prediction focus) FPE: 0.001756, MSE: 0.001687

A continuous time transfer function with 2 poles, one zero and 0.2 second transport delay can be estimated using the `tfest`

command:

m3 = tfest(ze, 2, 1, 0.2)

m3 = From input "Heater Voltage" to output "Thermocouple Voltage": 1.183 s + 26.55 exp(-0.2*s) * --------------------- s^2 + 11.61 s + 28.63 Continuous-time identified transfer function. Parameterization: Number of poles: 2 Number of zeros: 1 Number of free coefficients: 4 Use "tfdata", "getpvec", "getcov" for parameters and their uncertainties. Status: Estimated using TFEST on time domain data "ze". Fit to estimation data: 88.79% FPE: 0.009126, MSE: 0.008768

### Validating the Estimated Model to Experimental Output

How good is an estimated model? One way to find out is to simulate it and compare the model output with measured output. Select a portion of the original data that was not used in building the model, say from samples 800 to 900. Once the validation data has been preprocessed, we use the `compare`

function as shown below to view the quality of prediction:

zv = dry(800:900); % select an independent data set for validation zv = detrend(zv); % preprocess the validation data set(gcf,'DefaultLegendLocation','best') compare(zv,m1); % perform comparison of simulated output

**Figure 8:** Model's simulated response vs. validation data output.

It can be observed here that the agreement is very good. The "Fit" value shown is calculated as:

`Fit = 100*(1 - norm(yh - y)/norm(y-mean(y)))`

where `y`

is the measured output (=|zv.y|), and `yh`

is the output of the model `m1`

.

### Comparing Estimated Models

To compare the performance of the models that we have estimated, for example `m1`

, `m2`

and `m3`

with the validation data `zv`

, we can again use the `compare`

command:

compare(zv,m1,'b',m2,'r',m3,'c');

**Figure 9:** Comparing the responses of models `m1`

, `m2`

, `m3`

on validation data set `ze`

.

The pole-zero plots for the models can be obtained using `iopzplot`

:

h = iopzplot(m1,'b',m2,'r',m3,'c');

**Figure 10:** Poles and zeros of the models `m1`

, `m2`

and `m3`

.

The uncertainties in the poles and zeroes can also be obtained. In the following statement, '3' refers to the number of standard deviations.

showConfidence(h,3);

**Figure 11:** Pole-zero map with uncertainty regions.

The frequency functions above that are obtained from the models can be compared with one that is obtained using a non-parametric spectral analysis method (`spa`

):

gs = spa(ze);

The `spa`

command produces an IDFRD model. The `bode`

function can again be used for a comparison with the transfer functions of the models obtained.

w = linspace(0.4,pi/m2.Ts,200); opt = bodeoptions; opt.PhaseMatching = 'on'; bodeplot(m1,'b',m2,'r',m3,'c',gs,'g',w,opt); legend('m1','m2','m3','gs')

ans = Legend (m1, m2, m3, gs) with properties: String: {'m1' 'm2' 'm3' 'gs'} Location: 'northeast' Orientation: 'vertical' FontSize: 8.1000 Position: [0.8397 0.7540 0.1388 0.1693] Units: 'normalized' Use GET to show all properties

**Figure 12:** Bode responses of `m1`

, `m2`

and `m3`

compared against the non-parametric spectral estimation model `gs`

.

The frequency responses from the three models/methods are very close. This indicates that this response is reliable.

Also, a Nyquist plot can be analyzed with the uncertainty regions marked at certain frequencies:

showConfidence(nyquistplot(m1,'b',m2,'r',m3,'c',gs,'g'),3)

**Figure 13:** Nyquist plots of models `m1`

, `m2`

, `m3`

and `gs`

.

The non-parametric model `gs`

exhibits the most uncertainty in response.