```python
!pip install tbcontrol
import control
import numpy
import matplotlib.pyplot as plt
import tbcontrol
tbcontrol.expectversion("0.1.10")
%matplotlib inline
# import pandas as pd
import scipy.optimize
from tbcontrol.responses import fopdt, sopdt
from ipywidgets import interact

def resultplot1(K, tau, theta, y0):
    plt.figure(figsize=(21, 7))
    plt.scatter(ts, ym)
    plt.plot(ts, fopdt(ts, K, tau, theta, y0), color='red')
    plt.grid(True)

def resultplot2(K, tau, zeta, theta, y0):
    plt.figure(figsize=(21, 7))
    plt.scatter(ts, ym)
    plt.plot(ts, sopdt(ts, K, tau, zeta,theta, y0), color='red')
    plt.grid(True)
```

    Requirement already satisfied: tbcontrol in c:\users\usuario\.conda\envs\sistdin\lib\site-packages (0.2.1)
    Requirement already satisfied: numpy in c:\users\usuario\.conda\envs\sistdin\lib\site-packages (from tbcontrol) (1.16.4)
    Requirement already satisfied: tqdm in c:\users\usuario\.conda\envs\sistdin\lib\site-packages (from tbcontrol) (4.62.3)
    Requirement already satisfied: scipy in c:\users\usuario\.conda\envs\sistdin\lib\site-packages (from tbcontrol) (1.3.0)
    Requirement already satisfied: packaging in c:\users\usuario\.conda\envs\sistdin\lib\site-packages (from tbcontrol) (21.0)
    Requirement already satisfied: pyparsing>=2.0.2 in c:\users\usuario\.conda\envs\sistdin\lib\site-packages (from packaging->tbcontrol) (2.4.7)
    Requirement already satisfied: colorama in c:\users\usuario\.conda\envs\sistdin\lib\site-packages (from tqdm->tbcontrol) (0.4.4)

# Identificación: Ajuste de la respuesta al escalón

## Respuesta al escalón

La respuesta de un sistema **LTI** $\mathcal{H}$ ante una señal escalón $\epsilon(t)$ en la entrada se conoce como [*respuesta al escalón*](https://en.wikipedia.org/wiki/Step_response) $h_\epsilon(t)$.

Se define como:

\begin{equation}
h_\epsilon(t) = \mathcal{H} \{ \epsilon(t) \}
\end{equation}

Puede entenderse la señal escalón en la entrada como el encendido/activación de un sistema en el instante $t=0$. Se relaciona con la respuesta al impulso $h(t)$ mediante:

\begin{equation}
h_\epsilon(t) = \epsilon(t) * h(t) = \int_{-\infty}^{t} h(\tau) \; d\tau
\end{equation}

Pues debe tenerse en cuenta que:

\begin{equation}
\epsilon(t) = \int_{-\infty}^{t} \delta(\tau) d\tau
\end{equation}

Esta expresión muestra que la respuesta al impulso puede encontrarse a partir de la respuesta al escalón mediante:

\begin{equation}
h(t) = \frac{d h_\epsilon(t)}{dt}
\end{equation}

Así, la respuesta de un sistema LTI $y(t) = \mathcal{H} \{ x(t) \}$  ante cualquier señal de entrada $x(t)$ puede encontrarse a partir de la respuesta al escalón como:

\begin{equation}
y(t) = x(t) * \frac{d h_\epsilon(t)}{dt} = \frac{d x(t)}{dt} * h_\epsilon(t)
\end{equation}

Observe que la señal de salida del sistema LTI está definida por :
- La covolución de la **derivada de la señal de entrada** con la **respuesta al escalón**.
- La covolución de la **señal de entrada** con la **derivada de la respuesta al escalón**.

Así, la respuesta al escalón es una alternativa apropiada a la caracterización de sistemas LTI pues el impulso de Dirac no puede obtenerse en la el mundo real.

## Función de transferencia

En el dominio del tiempo, la salida $y(t)$ de un [sistema LTI](https://es.wikipedia.org/wiki/Sistema_LTI) está determinada por la convolución entre la entrada $x(t)$ y su [respuesta al impulso](../systems_time_domain/impulse_response.ipynb) $h(t)$.

\begin{equation}
y(t) = x(t) * h(t)
\end{equation}

En el dominio transformado se tiene

\begin{equation}
Y(s) = X(s) \cdot H(s)
\end{equation}

\begin{equation}
H(s) = \frac{Y(s)}{X(s)}
\end{equation}

donde:
- $X(s) = \mathcal{L} \{ x(t) \}$ es la transformada de Laplace de la señal de entrada.
- $Y(s) = \mathcal{L} \{ y(t) \}$ es la transformada de Laplace de la señal salida.
- $H(s) = \mathcal{L} \{ h(t) \}$ es la transformada de Laplace de la respuesta impulsional, también denominada [Función de transferencia](https://es.wikipedia.org/wiki/Funci%C3%B3n_de_transferencia).

Así, un sistema LTI queda modelado mediante la respuesta impulsional bien sea en el dominio temporal o en el dominio frecuencial.

### Estrategia tentativa para obtener un modelo

Pasos para obtener experimentalmente un modelo $H(S)$ apropiado para un sistema:

- Se excita el sistema con una entrada conocida evitando comportamientos no lineales.
- Se encuentra una expresión matemática en el dominio de las frecuencias que se ajuste a la señal de salida obtenida.
- Se encuentra el cociente entre las representaciones frecuenciales de la salida y la entrada.

En sistemas de orden elevado es recomendable encontrar modelos de orden más bajo.

## Ejemplo

Suponga un sistema de orden elevado cuyo modelo conoce

\begin{equation}
G_{real}(s) = \frac{s+2}{2s^3 + 3s^2 + 4s + 1}
\end{equation}

La respuesta ante un escalón unitario es:
\begin{equation}
\frac{1}{s} \cdot \frac{s+2}{2s^3 + 3s^2 + 4s + 1}
\end{equation}

```python
# Se define la función de transferencia
Greal = control.tf([1, 2], [2, 3, 4, 1])
Greal
```

$$\frac{s + 2}{2 s^3 + 3 s^2 + 4 s + 1}$$

```python
# Simulación del sistema ante un escalón unitario
ts, ys = control.step_response(Greal)
```

En datos de sistemas reales se tiene el efecto de ruido y sesgo, lo que modifica el valor inicial de la señal de salida e introduce un comportamieto aleatorio alrededor de la señal de salida "ideal".

```python
yinitial = 10
measurement_noise = numpy.random.randn(len(ys))*0.1
```

```python
ym = ys + yinitial + measurement_noise
```

```python
plt.figure(figsize=(21, 7))
plt.scatter(ts, ym, label='Datos')
plt.plot(ts, ys + yinitial, color='red', label='Modelo ideal')
plt.legend(loc='best')
plt.grid(True)
```

## Ajuste de un modelo de primer orden

Se encuentra un modelo de primer orden de la forma:

\begin{equation}
G(s) = \frac{K}{\tau s + 1} \cdot e^{-s\theta}
\end{equation}

Introduciendo además el sesgo.

Librería `tbcontrol.responses`

```python
fopdt
```

    <function tbcontrol.responses.fopdt(t, K, tau, theta=0, y0=0)>

```python
interact(resultplot1,
         K=(0., 10.),
         tau=(0., 10.),
         theta=(0., 10.),
         y0=(0., 20.));

```

    interactive(children=(FloatSlider(value=5.0, description='K', max=10.0), FloatSlider(value=5.0, description='t…

We can use the `scipy.optimize.curve_fit` tool to do this fit just like when we did regression without time.

```python
[K, tau, theta, y0], _ = scipy.optimize.curve_fit(fopdt, ts, ym, [3.0, 1.3, 1.3, 9.2])
[K, tau, theta, y0]
```

    [1.937945921272576, 2.719460115263922, 0.7889129626405381, 10.047724072352908]

Estos parámetros definen la función de transferencia de primer orden que mejor se ajusta al comportamiento medido.

```python
Y_1orden = fopdt(ts, K, tau, theta, y0)
```

\begin{equation}
G(s) = \frac{K}{\tau s + 1} \cdot e^{-s\theta}
\end{equation}
más sesgo.

## Ajuste de un modelo de segundo orden

También puede ajustarse un modelo de segundo orden.

\begin{align}
G(s) &= \frac{K}{(\tau s)^2 + 2\tau \zeta s + 1} \cdot e^{-s\theta} \\
&= \frac{K \omega_0^2}{s^2 + 2\zeta \omega_0 s + \omega_0^2} \cdot e^{-s\theta} \\
\end{align}

Introduciendo además el sesgo.

```python
interact(resultplot2,
         K=(0., 10.),
         tau=(0., 10.),
         theta=(0., 10.),
         zeta = (-2.0, 2.0),
         y0=(0., 20.));

```

    interactive(children=(FloatSlider(value=5.0, description='K', max=10.0), FloatSlider(value=5.0, description='t…

```python
p2_inicial = [2, 0.6, 2, 0.5, 10]
[K_2, tau_2, zeta_2, theta_2, y0_2], pcov = scipy.optimize.curve_fit(sopdt, ts, ym, p2_inicial)
[K_2, tau_2, zeta_2, theta_2, y0_2]
```

    [1.947796987536108,
     0.5841621734710918,
     2.43675496329597,
     0.6454333226644818,
     10.037957935183517]

```python
perr = numpy.sqrt(numpy.diag(pcov))
perr
```

    array([0.05240559, 0.95385485, 3.68498641, 0.39741511, 0.05065218])

```python
Y_2orden = sopdt(ts, K_2, tau_2, zeta_2, theta_2, y0_2)
```

```python
plt.figure(figsize=(21, 7))
plt.scatter(ts, ym, label='Data')
plt.plot(ts, Y_1orden, color='red', label='ajuste orden 1')
plt.plot(ts, Y_2orden, color='blue', label='ajuste orden 2')
plt.plot(ts, ys + 10, color='gray', label='Original')
plt.legend(loc='best')
plt.grid(True)
```

```python

```