3.8 Gráfica animada para interpretar el Integral de convolución con matplotlib-Python

Para interpretar mejor la operación se presenta una animación gráfica de h(t-τ) y y(t), para diferentes valores de t en el intervalo de observación [t_a,t_b] se incorpora la función graf_animada_xh_y().

x(t) \circledast h(t) = \int_{-\infty}^{+\infty} x(\tau)h(t-\tau) \delta \tau

Integral de Convolucion 01 animado

Otros ejemplos de gif animado de gráficas con matplotlib, se pueden revisar en: Una partícula en movimiento circular del curso CCPG1001 Fundamentos de Programación.

# GRAFICA CON ANIMACION ------------
def graf_animada_xh_y(xt,ht,yt,t_a,t_b,
                      muestras=101,y_nombre='y',
                      reprod_x = 4,retardo  = 200,
                      archivo_nombre = ''):
    '''grafica animada convolucionx(t) y h(t)
       en dos subgráficas con Parametros de animación trama/foto
        y_nombre = 'ZSR' # o y nombre de resultado convolución
        reprod_x = 4     # velocidad de reproducción
        retardo  = 200   # milisegundos entre tramas
        archivo_nombre = '' # crea gif animado si hay nombre
    '''
    # grafica evaluación numerica
    x_t = sym.lambdify(t,xt,modules=equivalentes)
    h_t = sym.lambdify(t,ht,modules=equivalentes)
    y_t = sym.lambdify(t,yt,modules=equivalentes)

    ti = np.linspace(t_a,t_b,muestras)
    xi = x_t(ti)
    hi = h_t(ti)
    yi = y_t(ti)

    import matplotlib.animation as animation
    # h(t-tau) para cada t
    ht_tau   = []
    for tau in range(0,muestras,reprod_x):
        ht_tau.append(h_t(ti[tau]-ti))
    tramas = len(ht_tau) # tramas creadas

    # figura con dos sub-graficas
    fig_anim = plt.figure()
    graf_a1  = fig_anim.add_subplot(211)
    graf_a2  = fig_anim.add_subplot(212)

    # grafico superior
    x_linea, = graf_a1.plot(ti,xi, color='blue',
                            label=r'$x(\tau)$')
    h_linea, = graf_a1.plot(ti,hi,color='magenta',
                             linestyle='dashed',
                             label=r'$h(\tau)$')
    htau_linea, = graf_a1.plot(ti,ht_tau[0],
                               color='magenta',
                               label=r'$h(t-\tau)$')
    punto1, = graf_a1.plot(0,0, color='magenta',marker=6)

    # grafico inferior
    color_y = 'green'
    if y_nombre=='ZSR':
        color_y ='dodgerblue'
    y_linea, = graf_a2.plot(ti,yi, color=color_y,
                            label=y_nombre+'(t)')
    punto2,  = graf_a2.plot(0,0, color=color_y,marker=6)
    y_sombra, = graf_a2.plot(ti,yi, color=color_y)
    y_sombra.set_visible(False) # Para fijar leyend()


    # Configura gráfica
    titulo = r''+y_nombre+'(t)= x(t)$\circledast$h(t)'
    graf_a1.set_title(titulo)
    ymax1 = np.max([np.max(xi),np.max(hi)])*1.11
    ymin1 = np.min([np.min(xi),np.min(hi)])-0.1*ymax1
    graf_a1.set_xlim([t_a,t_b])
    graf_a1.set_ylim([ymin1,ymax1])
    graf_a1.set_xlabel(r'$\tau$')
    graf_a1.legend()
    graf_a1.grid()

    ymax2 = np.max(yi)*1.1
    ymin2 = np.min(yi)-0.1*ymax2
    graf_a2.set_xlim([t_a,t_b])
    graf_a2.set_ylim([ymin2,ymax2])
    graf_a2.set_xlabel('t')
    graf_a2.legend()
    graf_a2.grid()

    # cuadros de texto en gráfico
    txt_x = (t_b+t_a)/2
    txt_y = ymax1*(1-0.09)
    txt_tau = graf_a1.text(txt_x,txt_y,'t='+str(t_a),
                   horizontalalignment='center')

    def trama_actualizar(i,ti,ht_tau):
        # actualiza cada linea
        htau_linea.set_xdata(ti)
        htau_linea.set_ydata(ht_tau[i])

        hasta   = i*reprod_x
        porusar = (muestras-reprod_x*(i+1))
        if porusar>=reprod_x: # en intervalo
            y_linea.set_xdata(ti[0:hasta])
            y_linea.set_ydata(yi[0:hasta])
            punto1.set_xdata(ti[hasta])
            punto1.set_ydata(0)
            punto2.set_xdata(ti[hasta])
            punto2.set_ydata(0)
        else: # insuficientes en intervalo
            y_linea.set_xdata(ti)
            y_linea.set_ydata(yi)
            punto1.set_xdata(ti[-1])
            punto1.set_ydata(0)
            punto2.set_xdata(ti[-1])
            punto2.set_ydata(0)

        # actualiza texto
        t_trama = np.around(ti[i*reprod_x],4)
        txt_tau.set_text('t= '+str(t_trama))
        
        return(htau_linea,y_linea,punto1,punto2,txt_tau)

    def trama_limpiar(): # Limpia Trama anterior
        htau_linea.set_ydata(np.ma.array(ti, mask=True))
        y_linea.set_ydata(np.ma.array(ti, mask=True))
        punto1.set_ydata(np.ma.array(ti, mask=True))
        punto2.set_ydata(np.ma.array(ti, mask=True))
        txt_tau.set_text('')
        return(htau_linea,y_linea,punto1,punto2,txt_tau)

    i   = np.arange(0,tramas,1) # Trama contador
    ani = animation.FuncAnimation(fig_anim,trama_actualizar,i ,
                                  fargs = (ti,ht_tau),
                                  init_func = trama_limpiar,
                                  interval = retardo,
                                  blit=True)
    # Guarda archivo GIF animado o video
    if len(archivo_nombre)>0:
        ani.save(archivo_nombre+'_animado.gif',
                 writer='imagemagick')
        #ani.save(archivo_nombre+'_video.mp4')
    plt.draw()
    #plt.show()
    return(ani)

# grafica animada de convolución
n_archivo = '' # sin crear archivo gif animado 
n_archivo = 'convolucion01' # requiere 'imagemagick'
figura_animada = graf_animada_xh_y(x,h,y,t_a,t_b,
                      muestras, reprod_x = 4,
                      archivo_nombre = n_archivo)
plt.show()

Para obtener un gif animado se debe asignar un 'archivo_nombre' para identificar el ejercicio; si se mantiene el nombre vacio '', solamente se crea la gráfica. El resultado se almacena en el archivo_nombre_animado.gif del directorio de trabajo,

La función también se incorpora  a telg1001.py para su uso posterior en los ejercicios