MATLAB - лучший способ динамически обновлять строки дескрипторов XData и YData? - PullRequest
6 голосов
/ 14 сентября 2011

Я собираю данные и строю эти данные в режиме реального времени. Данные создаются системой захвата движения. У меня есть один класс DynamicDataset, который является просто оберткой вокруг матрицы из 2 столбцов (хотя это более нюансировано, чем это) с добавлением уведомителя о событиях для новых данных; другой класс DynamicPlotter, который прослушивает событие с добавленными данными и динамически обновляет график. Соответствующие фрагменты кода:

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

На основании профиля кода MATLAB команда set в updatePlot() довольно дорогая. Мне интересно, есть ли лучший способ построить отдельные точки по мере их появления? В идеале я бы вставил одну точку в XData и YData и нарисовал только эту точку, но я не знаю, возможно ли это.

Обратите внимание, что может быть несколько объектов линейной серии (то есть несколько графиков на одном графике); plot() принимает дескриптор оси в качестве аргумента, поэтому он не учитывает свойства ранее нарисованных дескрипторов линии (или есть способ сделать это?); Я думал просто сделать plot(x,y);hold all;, но это дало бы мне каждый раз отдельные линейные дескрипторы, каждый из которых соответствует отдельной точке.

Возможно, нет способа ускорить построение входящих точек, но я решил спросить.

РЕДАКТИРОВАТЬ : обновлен OP с реальным кодом, с которым я работаю, вместо использования общего примера, который предназначен для неправильной интерпретации.

Ответы [ 3 ]

4 голосов
/ 18 сентября 2011

Объем данных, которые вы обрабатываете в каждом обновлении, велик (хотя фактически меняется только одна точка), что делает ваш код O (N ^ 2).

Используя вторую серию строк для создания большой группы данных, вы можете чередовать добавление каждой точки к короткой «активной» строке и нечастое добавление больших блоков в основную серию строк. Хотя это точно не исключает O (N ^ 2), оно позволяет значительно уменьшить константу.

Если вы сделаете это, не забудьте перекрыть «старые» серии строк и «активные» серии строк на одну точку, чтобы они соединялись.

По существу:

    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
1 голос
/ 23 сентября 2011

Ваш код работает медленно, потому что вы перераспределяете все значения при каждом вызове updatePlot .Поэтому я хотел бы только построить самую последнюю точку в updatePlot (Это также проблема, которую вы заявили: В идеале, я бы вставил одну точку в XData и YData и нарисовал только эту точку, но яне знаю, возможно ли это. )

  1. добавить свойство 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. изменить 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
    
1 голос
/ 14 сентября 2011

Частично причина, по которой вашему коду может потребоваться много времени, заключается в том, что вы используете цикл for для присваивания переменных.В зависимости от того, какую версию Matlab вы используете, это значительно замедлит ваш процесс.Я предлагаю использовать векторизацию для назначения значений вашим x и y следующим образом:

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

Затем вы можете назначить первые точки в ваших данных.XDataSource и YDataSource.

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

Теперь, когда вы переходите к циклу для изменения значений, используйте refreshdata для обновления значений Xdata и Ydata.Используйте функцию drawnow для обновления окна рисунка.

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