[Python] Problema com gráfico animado em tempo real

Discussão em 'Python' iniciado por Stack, Outubro 3, 2024.

  1. Stack

    Stack Membro Participativo

    Olá, comunidade!

    Estou desenvolvendo uma classe em Python responsável por capturar dados em tempo real do Arduino e gerar dois gráficos. Para melhorar o desempenho, estou utilizando a técnica de blitting. No entanto, estou enfrentando alguns problemas:

    Atualização do Eixo X: O eixo X, que representa o tempo, não está se atualizando corretamente conforme novos dados são recebidos. Ele permanece estático, o que impede a visualização dinâmica dos dados em tempo real.

    Independência dos Gráficos: Preciso que os gráficos funcionem de maneira independente um do outro, cada um refletindo os dados em tempo real sem interferência.

    Animação em Tempo Real: O gráfico não parece ser animado em tempo real, o que é essencial para a minha aplicação.


    Alguém tem sugestões sobre como resolver esses problemas? Agradeço antecipadamente pela ajuda!

    class Graficos:
    def __init__(self, arduino, master):
    self.arduino = arduino # Recebe o objeto arduíno
    self.master = master # Recebe o objeto master

    self.dados_lvdt = {'lvdt1': [], 'lvdt2': [], 'lvdt3': [], 'media': []} # Cria um dicionário para armazenar os dados do lvdt 1, 2, 3 e da média
    self.dados_lvdt_axial = {'lvdt_axial': []} # Cria um dicionário para armazenar os dados do lvdt axial
    self.tempos = [] # Adicionado para armazenar os tempos

    self.fig, self.axs = plt.subplots(1, 2, layout="constrained", figsize=(25.3, 5))# figura e um conjunto de subplots,largura de 15 polegadas e uma altura de 3 polegadas, e há 45 subplots na figura.
    self.fig.suptitle('Gráficos LVDT', fontsize=16)#adiciona um título à figura.
    self.canvas = FigureCanvasTkAgg(self.fig, master=self.master)#exibir a figura na interface gráfica do usuário.
    self.canvas_widget = self.canvas.get_tk_widget() # Cria o widget Tkinter
    self.canvas_widget.grid(row=0, column=0) # Posicionamento
    self.limite_pontos = 50 # Limite de pontos

    self.ax1, self.ax2 = self.axs[0], self.axs[1] # Cria os 2 gráficos
    self.line_lvdt1, = self.axs[0].plot([], [], lw=2) # Cria a linha do lvdt 1
    self.line_lvdt2, = self.axs[0].plot([], [], lw=2) # Cria a linha do lvdt 2
    self.line_lvdt3, = self.axs[0].plot([], [], lw=2) # Cria a linha do lvdt 3
    self.line_media, = self.axs[0].plot([], [], lw=2) # Cria a linha da média
    self.line_lvdt_axial, = self.axs[1].plot([], [], lw=2) # Cria a linha do lvdt axial

    self.configurar_graficos() # Configura os gráficos

    # Mudança em 03/10/2024

    # Exibe a figura e prepara o fundo para o blit
    plt.show(block=False)
    self.fig.canvas.draw() # Necessário para capturar o fundo corretamente
    self.bg = self.fig.canvas.copy_from_bbox(self.fig.bbox) # Salva o fundo da figura



    """
    02/10/2024
    # Exibe a figura e prepara o fundo para o blit
    plt.show(block=False)
    plt.pause(0.1) # Garante que a figura seja exibida
    self.bg = self.fig.canvas.copy_from_bbox(self.fig.bbox) # Salva o fundo da figura
    """
    self.iniciar_animacao() # Inicia a animação

    # Inicializando as linhas nos subplots
    self.lines = [ax.plot([], [])[0] for ax in self.axs]#inicializa uma lista de linhas vazias para cada subplot.


    # Metódo para configurar os gráficos.
    def configurar_graficos(self):
    self.ax1.set_xlabel('Tempo (ms)') # Título do eixo x
    self.ax1.set_ylabel('Deslocamento (mm)') # Título do eixo y
    #self.ax1.set_xlim(0, 1000) # Intervalo do eixo X (ms)
    self.ax1.set_ylim(-1.4, 1.6) # Intervalo do eixo Y (mm)
    #self.ax1.set_yticks(np.arange(-1.4, 1.6, 0.2)) # Escala do eixo y, passo de 0,25
    self.ax1.set_yticks(ticker.MultipleLocator(0.2).tick_values(-1.4,1.4))
    self.ax1.set_title('LVDT 1, 2, 3 e Média') # Titulo do subplot
    self.ax1.legend(['LVDT 1', 'LVDT 2', 'LVDT 3', 'Média'], loc='lower left') # Legenda
    #self.ax1.set_facecolor('gray')
    self.ax1.grid(True) # Mostra a grade

    self.ax2.set_xlabel('Tempo (ms)') # Nomemclatura do eixo x
    self.ax2.set_ylabel('Deslocamento (mm)') # Título do eixo y
    #self.ax2.set_xlim(0, 500) # Intervalo do eixo X (ms)
    self.ax2.set_ylim(-1.4, 1.6) # Intervalo do eixo Y (mm)
    #self.ax2.set_yticks(np.arange(-1.4, 1.6, 0.2)) # Escala do eixo y, passo de 0,2
    #self.ax2.set_yticks(list(self.float_range(-1.4, 1.6, 0.2)))
    self.ax2.set_yticks(ticker.MultipleLocator(0.2).tick_values(-1.4,1.4))
    self.ax2.set_title('LVDT Axial') # Titulo do subplot
    #self.ax2.set_facecolor('gray')
    self.ax2.grid(True) # Mostra a grade

    def gerar_dados(self, frame): # Método para gerar os dados. Recebe o frame e o objeto arduíno
    novo_dado_lvdt = self.arduino.dados_lvdt() # Recebe novos dados do objeto arduíno

    if novo_dado_lvdt: # Se houver novos dados
    # Atualiza os gráficos lvdt
    lvdt1, lvdt2, lvdt3, lvdtaxial = novo_dado_lvdt # Recebe os novos dados
    self.dados_lvdt['lvdt1'].append(lvdt1) # Adiciona os novos dados ao dicionário lvdt1

    self.dados_lvdt['lvdt2'].append(lvdt2) # Adiciona os novos dados ao dicionário lvdt2

    self.dados_lvdt['lvdt3'].append(lvdt3) # Adiciona os novos dados ao dicionário lvdt3

    self.dados_lvdt['media'].append((lvdt1 + lvdt2 + lvdt3) / 3) # Faz o cálculo da média dos 3 lvdts e os adiciona no dicionário
    self.dados_lvdt_axial['lvdt_axial'].append(lvdtaxial) # Adiciona novos dados ao dicionário lvdt axial

    self.tempos.append(frame) # Adiciona o novo tempo

    self.atualizar_dados() # Atualiza os dados

    # Retornar as linhas atualizadas (objetos Artist)
    return self.line_lvdt1, self.line_lvdt2, self.line_lvdt3, self.line_media, self.line_lvdt_axial

    #self.atualizar_dados()



    #Certifique-se de que as linhas têm dados suficientes
    #if len(self.tempos) > 0:


    def atualizar_dados(self): # Atualiza os dados

    # Re-captura o fundo da figura antes de redesenhar as linhas
    self.bg = self.fig.canvas.copy_from_bbox(self.fig.bbox)

    # Restaura o fundo (background)
    self.fig.canvas.restore_region(self.bg)

    # Atualiza os dados
    self.line_lvdt1.set_data(self.tempos, self.dados_lvdt['lvdt1'])
    self.line_lvdt2.set_data(self.tempos, self.dados_lvdt['lvdt2'])
    self.line_lvdt3.set_data(self.tempos, self.dados_lvdt['lvdt3'])
    self.line_media.set_data(self.tempos, self.dados_lvdt['media'])
    self.line_lvdt_axial.set_data(self.tempos, self.dados_lvdt_axial['lvdt_axial'])

    # Redefinir os limites do eixo X
    if len(self.tempos) > 0:
    # Definir o limite do eixo X de 0 até o valor máximo de tempo registrado
    max_tempo = max(self.tempos)
    if max_tempo > 0:
    self.ax1.set_xlim(0, max_tempo)
    self.ax2.set_xlim(0, max_tempo)

    # Redesenha as linhas
    self.ax1.draw_artist(self.line_lvdt1)
    self.ax1.draw_artist(self.line_lvdt2)
    self.ax1.draw_artist(self.line_lvdt3)
    self.ax1.draw_artist(self.line_media)
    self.ax2.draw_artist(self.line_lvdt_axial)



    """
    Excluído em 03/10/2024
    self.line_lvdt1.set_xdata(self.tempos) # Define os novos dados no eixo x , o qual será o tempo
    self.line_lvdt1.set_ydata(self.dados_lvdt['lvdt1']) # Define os novos dados no eixo y , o qual é o lvdt1


    self.line_lvdt2.set_xdata(self.tempos) # Define os novos dados no eixo x , o qual é o tempo
    self.line_lvdt2.set_ydata(self.dados_lvdt['lvdt2']) # Define os novos dados no eixo y , o qual é o lvdt2

    self.line_lvdt3.set_xdata(self.tempos) # Define os novos dados no eixo x , o qual é o tempo
    self.line_lvdt3.set_ydata(self.dados_lvdt['lvdt3']) # Define os novos dados no eixo y , o qual é o lvdt3

    self.line_media.set_xdata(self.tempos) # Define os novos dados no eixo x , o qual é o tempo
    self.line_media.set_ydata(self.dados_lvdt['media']) # Define os novos dados no eixo y , o qual é a média

    self.axs[0].relim() # Atualiza os limites dos eixos
    self.axs[0].autoscale_view() # Atualiza os limites dos eixos

    self.line_lvdt_axial.set_xdata(self.tempos) # Define os novos dados no eixo x , o qual é o tempo
    self.line_lvdt_axial.set_ydata(self.dados_lvdt_axial['lvdt_axial']) # Define os novos dados no eixo y , o qual é o lvdtaxial

    self.axs[1].relim() # Atualiza os limites dos eixos
    self.axs[1].autoscale_view() # Atualiza os limites dos eixos


    02/10/2024
    # Atualiza a tela com o novo gráfico
    self.fig.canvas.blit(self.fig.bbox)
    """

    # Atualiza a tela com as partes redesenhadas
    self.fig.canvas.blit(self.fig.bbox)

    # Opcional: flush events para garantir que o canvas seja atualizado
    self.fig.canvas.flush_events()

    """
    # Excluído em 03/10/2024 self.canvas.draw() # Atualiza o widget Tkinter com a imagem atualizada.
    self.canvas.flush_events() # Método para limpar figuras em cada iteração, para que figuras sucessivas não se sobreponham.
    """



    def iniciar_animacao(self): # Inicia a animação
    self.anim = FuncAnimation(self.fig, self.gerar_dados, blit=True, interval=10, cache_frame_data=False) # Cria a animação, que gera novos dados a cada 10 ms, e armazena os dados gerados.Blit só só redesenha partes #do gráfico que mudaram .
    #self.anim.save('Minha animação.gif')

    Continue reading...

Compartilhe esta Página