2011-09-14 7 views
6

Zbieram dane i kreślę te dane w czasie rzeczywistym. Dane są produkowane przez system przechwytywania ruchu. Mam jedną klasę DynamicDataset, która jest po prostu opakowaniem wokół matrycy 2-kolumnowej (chociaż jest bardziej zniuansowana) z powiadomieniem o zdarzeniu dla nowych dodanych danych; inna klasa DynamicPlotter, która nasłuchuje zdarzenia dodanego do danych i dynamicznie aktualizuje wykres. Odpowiednie fragmenty kodu:MATLAB - najlepszy sposób dynamicznej aktualizacji linii obsługuje "XData i YData?

classdef DynamicDataset < handle 
    properties 
     newestData = []; 
     data = [] 
    end 
    events 
     DataAdded 
    end 
    methods 
     function append(obj, val) 
      obj.data(end+1,:) = val; 
      obj.newestData = val; 
      notify(obj, 'DataAdded'); 
     end 
    end 
end 

classdef DynamicPlotter < dynamicprops 
    properties 
     FH %# figure handle 
     AH %# axes handle 
     LH %# array of line handles - may have multiple lines on the plot 

     dynProps = {} %# cell array of dynamic property names - 
         %# use to access individual datasets 
    end 
    methods 
     function obj = DynamicPlotter(props) %# props is a cell array of dynamic 
              %# properties to store information 
      for i = 1:length(props) 
       addprop(obj, props{i}); 
       obj.(props{i}) = DynamicDataset; 
       obj.dynProps = [obj.dynProps props{i}]; 

       addlistener(obj.(props{i}), 'DataAdded', @obj.updatePlot(i)); 
      end 
      obj.createBlankPlot(); 
     end 

     function createBlankPlot(obj) 
      obj.FH = figure; 
      obj.AH = axes; 

      hold all; 

      for i = 1:length(obj.dynProps) 
       obj.LH(i) = plot(nan); %# only used to produce a line handle 
        set(obj.LH(i), 'XData', [], 'YData', []); 
      end 
     end 

     function updatePlot(obj, propNum) 
      X = get(obj.LH(propNum), 'XData'); 
      Y = get(obj.LH(propNum), 'YData'); 

      X(end+1) = obj.(dynProps{propNum}).newestData(1); 
      Y(end+1) = obj.(dynProps{propNum}).newestData(2); 

      set(obj.LH(propNum), 'XData', X, 'YData', Y); 
     end 
    end 
end 

oparciu o profil Kod MATLAB, polecenie w updatePlot()set jest raczej drogie. Zastanawiam się, czy istnieje lepszy sposób na wytypowanie poszczególnych punktów, gdy nadejdą? Najlepiej byłoby przesłać pojedynczy punkt do XData i YData i narysować tylko ten punkt, ale nie wiem, czy to możliwe.

Należy pamiętać, że może istnieć wiele obiektów lineserii (tj. Wiele wykresów na tym samym wykresie); plot() przyjmuje argument osi, jako argument, więc nie bierze pod uwagę właściwości wcześniej narysowanych linii (lub czy istnieje sposób, aby to zrobić?); Myślałem o zrobieniu plot(x,y);hold all;, ale to dałoby mi osobne linie za każdym razem, z których każdy odpowiadałby jednemu punktowi.

Może to być tak, że nie da się przyspieszyć wykreślania punktów, ale pomyślałem, że zapytam.

EDIT: Zaktualizowałem OP za pomocą kodu, nad którym pracuję, zamiast używać ogólnego przykładu, który jest źle interpretowany.

+0

Nie wiem czy widzieliście to jednak przyjrzeć http://stackoverflow.com/questions/1693429/matlab-oop-is-it-slow-or-am- I-doing-something-wrong. Zasadniczo korzystanie z klas w Matlab generalnie skutkuje słabą wydajnością – Marm0t

+0

dzięki, widziałem to już wcześniej. Mój projekt wymaga użycia klas z powodów, dla których nie wejdę, więc nie ma na to żadnego sposobu, ale czy wywołanie 'set' będzie powolne, ponieważ jest wywoływane wewnątrz metody? –

+0

@ strictlyrude27: powinieneś poprawić linię addlistener jako: 'addlistener (obj. (Props {i}), 'DataAdded', @ (src, ev) obj.updatePlot (i));'. Możesz również chcieć dodać 'drawnow' na końcu funkcji' updatePlot' – Amro

Odpowiedz

4

Ilość danych jesteś obsługi w każdej aktualizacji, jest duża (choć tylko jeden punkt rzeczywiście się zmienia), dzięki czemu swój kod O (n^2).

Używając drugiej linii line do utworzenia dużej grupy danych, można na przemian dodawać każdy punkt do krótkiej "aktywnej" linii i rzadko dodawać duże bloki do głównej linii. Chociaż to nie eliminuje dokładnie O (N^2), pozwala znacznie zmniejszyć stałą.

Jeśli to zrobisz, pamiętaj, aby nakładać "stare" lineseries i "aktywne" linie liniowe o jeden punkt, aby się połączyć.

Zasadniczo:

function updatePlot(obj, propNum) 
     X = get(obj.LHactive(propNum), 'XData'); 
     Y = get(obj.LHactive(propNum), 'YData'); 

     X(end+1) = obj.(dynProps{propNum}).newestData(1); 
     Y(end+1) = obj.(dynProps{propNum}).newestData(2); 

     if numel(X) > 100 
      Xold = [get(obj.LH(propNum), 'XData'); X(2:end)]; 
      Yold = [get(obj.LH(propNum), 'YData'); Y(2:end)]; 
      set(obj.LH(propNum), 'XData', Xold, 'YData', Yold); 

      X = X(end); 
      Y = Y(end); 
     end 

     set(obj.LHactive(propNum), 'XData', X, 'YData', Y); 
    end 
+0

to drastycznie zmniejszyło czas fabuły! jeśli masz więcej pomysłów na dalszą redukcję czasu, daj mi znać .. –

1

Jednym z powodów, dla których wykonywanie kodu może być długotrwałe, jest fakt, że do przypisania zmiennych używasz pętli for. W zależności od używanej wersji Matlaba spowoduje to znaczne spowolnienie procesu. Proponuję za pomocą wektoryzacji przypisać wartości do swojej xiy tak:

x = 1:1000; 
y = cosd(x); 

Następnie można przypisać pierwsze punkty w Twoich danych.

xi = x(1); 
yi = y(1); 

Podczas drukowania należy przypisać XDataSource i YDataSource.

h = plot(xi, yi, 'YDataSource', 'yi', 'XDataSource', 'xi'); 

Teraz, gdy przechodzisz przez pętlę, aby zmienić wartości, użyj refreshdata, aby zaktualizować wartości Xdata i Ydata. Użyj funkcji drawnow, aby zaktualizować okno figur.

for k = 2:1000, 
xi = x(1:k); 
yi = y(1:k); 
refreshdata(h, 'caller') 
drawnow; 
end 
+0

To niestety nie zadziała na moje potrzeby - moje dane nie są faktycznie generowane w pętli for, ale są produkowane przez system przechwytywania ruchu w czasie rzeczywistym. Powyższy fragment kodu pokazuje tylko, że dynamicznie kreślę dane. Zaktualizuję OP, aby odzwierciedlić moją konkretną sprawę; Starałem się być bardziej ogólny, ale mój przykład był wyraźnie mylący. –

1

Twój kod jest powolny, ponieważ jesteś replotting wszystkie wartości, które za każdym razem wywołać updatePlot. Chciałbym tylko wykreślić ostatni punkt w updatePlot (Jest to również problem, który napisałeś: Idealnie popchnąłbym pojedynczy punkt do XData i YData i narysowałem tylko ten punkt, ale nie wiem, czy to jest możliwe.)

  1. dodaj właściwość LH_point_counter

    classdef DynamicPlotter < dynamicprops 
        properties 
         FH %# figure handle 
         AH %# axes handle 
         LH %# cell array of line handles - may have multiple lines on the plot 
    
         % counter that counts home many points we have for each dynProps 
         LH_point_counter = []; 
    
         dynProps = {} %# cell array of dynamic property names - 
           %# use to access individual datasets 
        end 
    
  2. modyfikowania updatePlot

    function updatePlot(obj, propNum) 
        % plot new point 
        new_x = obj.(dynProps{propNum}).newestData(1); 
        new_y = obj.(dynProps{propNum}).newestData(2); 
        new_handle = plot(new_x, new_y); 
    
        % add new handle to list of handles of this property 
        counter_this_prop = obj.LH_point_counter(propNum); 
        counter_this_prop = counter_this_prop + 1; 
        obj.LH{propNum}(counter_this_prop) = new_handle; 
    
        % save new counter value 
        obj.LH_point_counter(propNum) = counter_this_prop; 
    end 
    
+0

To może zadziałać - jestem pewny, że konieczność przeskanowania nieruchomości nie byłaby najdroższą rzeczą na świecie. Zastanawiam się jednak, jak to wpłynie na uchwyt osi głównej? Ręcznie dopasowuję właściwości wydruku i inne właściwości uchwytów osi, więc ciągłe pisanie tego może nie być najlepszą opcją. –

+1

kod nie jest recopying żadnych właściwości. Wystarczy dodać najnowszy uchwyt do listy uchwytów - jest to bardzo tania operacja. Ręczna regulacja osi to osobny problem i nie opublikowałeś tego kodu. – memyself

Powiązane problemy