【问题标题】:MATLAB - best way to dynamically update a line handles' XData and YData?MATLAB - 动态更新线句柄的 XData 和 YData 的最佳方法?
【发布时间】:2011-11-16 19:39:00
【问题描述】:

我正在收集数据并实时绘制数据。数据由动作捕捉系统产生。我有一个类DynamicDataset,它只是一个围绕 2 列矩阵的包装器(尽管它比那更细微),并添加了一个用于添加新数据的事件通知器;另一个类DynamicPlotter 监听数据添加事件并动态更新绘图。对应代码sn-ps:

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 Code Profile,updatePlot() 中的 set 命令相当昂贵。我想知道是否有更好的方法来绘制各个点?理想情况下,我会将单点推入 XDataYData 并仅绘制该点,但我不知道这是否可能。

请注意,可能有多个线序列对象(即,同一图上的多个图形); plot() 将轴句柄作为参数,因此它不会考虑先前绘制的线句柄的属性(或者有没有办法让它这样做?);我想只做plot(x,y);hold all;,但这每次都会给我单独的线句柄,每个句柄对应一个点。

可能没有办法更快地绘制传入点,但我想我会问。

编辑:使用我正在使用的实际代码更新 OP,而不是使用容易引起误解的通用示例。

【问题讨论】:

  • 我不知道你是否看过这个,但看看stackoverflow.com/questions/1693429/…。基本上在matlab中使用类通常会导致性能不佳
  • 谢谢,我以前见过。我的项目需要使用类,因为我不会进入,所以没有任何办法解决这个问题.. 但是 set 调用会因为在方法内部调用而变慢吗?
  • @strictlyrude27:您应该将 addlistener 行更正为:addlistener(obj.(props{i}), 'DataAdded', @(src,ev) obj.updatePlot(i));。您可能还想在updatePlot 函数的末尾添加drawnow
  • @Amro - 哎呀,应该是 props,转录错误。添加@(src,ev) - 如果我不使用这些参数,它们真的需要在那里吗?最后,我之前确实有drawnow,但这并不影响设置XDataYData 的绘制方式..
  • @strictlyrude27:还要注意收集的数据会随着时间的推移而变大。因此,如果您不必在每次追加后从头开始绘制整个事物,那么我发布的 previous solution 可以适应您的情况(OOP 风格),我在其中以滑动窗口方式显示数据,使用一个循环缓冲区...

标签: matlab figures


【解决方案1】:

您在每次更新中处理的数据量很大(尽管实际上只有一个点在变化),使您的代码 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

【讨论】:

  • 这大大缩短了情节时间!如果您有任何进一步减少时间的想法,请告诉我..
【解决方案2】:

您的代码可能需要很长时间才能运行的部分原因是您正在使用 for 循环来分配变量。根据您使用的 Matlab 版本,这将显着减慢您的进程。我建议使用矢量化来为您的 x 和 y 分配值,如下所示:

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

然后您可以分配数据中的第一个点。

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

绘制时,分配 XDataSource 和 YDataSource。

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

现在,当您循环更改值时,使用 refreshdata 更新 Xdata 和 Ydata 值。使用drawow函数更新图形窗口。

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

【讨论】:

  • 不幸的是,这不能满足我的需要——我的数据实际上并不是在 for 循环中生成的,而是由动作捕捉系统实时生成的。上面的代码 sn-p 只是演示了我正在动态地绘制我的数据。我将更新 OP 以反映我的具体情况;我试图更笼统,但我的例子显然具有误导性。
【解决方案3】:

您的代码很慢,因为您每次调用 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
    

【讨论】:

  • 这可能会奏效——我确信必须重新复制属性不会是世界上最昂贵的事情。但是,我确实想知道这将如何干扰我拥有的主轴手柄?我手动调整绘图窗口和其他坐标轴句柄属性,所以不断写这可能也不是最好的选择。
  • 代码没有重新复制任何属性。它所做的只是将最新的句柄添加到句柄列表中——这是一个非常便宜的操作。您的手动轴调整是一个单独的问题,您还没有发布代码。
猜你喜欢
  • 1970-01-01
  • 2018-03-28
  • 2021-06-17
  • 1970-01-01
  • 2010-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-01
相关资源
最近更新 更多