1
votes

I'm plotting real-time data in Matlab (received through a serial port), and I found a curious way to affect the refresh rate: changing the size of the plot. When the plot is "normal" it'll get ~35fps, and as I shrink it, it'll go over 120fps. However, when I use the exact same plot setup, but just plot data generated randomly for each loop, the refresh rate is unaffected by the size of the plot. Unfortunately, profile viewer (2017a) is unable to see inside the plotting, and just attributes all of the wait time to the serial port - which isn't possible, given the variable refresh rate described above (also, I've tested a bare-bones serial call and response, and the roundtrip for Matlab is under 8ms (~120fps)).

The two loop examples are not meant to get the same framerate, they are merely to demonstrate that when one changes the plot size (just manually grab a corner and adjust it, or use the position parameter) that the first example (serial) is hugely affected by the size, and the second example (random) is not.

Addition:

After playing with a modified version of this where the plot is updated by a timer callback, I see that I can either make the serial read loop fast (~170Hz) or update the plot fast (~170Hz). However, the other always suffers. And if I change the timer period iteratively to get the same update rate for both, not surprisingly, it is almost exactly the same rate as I get with loop shown below. It would be nice to put the plot update on a separate thread, without jumping through the hoops of calling a separate Matlab process and communicating between the two.

Plot setup:

figure_h = figure();
set(figure_h, 'KeyPressFcn', @key_press_function);
set(figure_h, 'WindowButtonDownFcn', {@click_callback, obj})

%some random things that drastically speed up the plotting
set(figure_h, 'Position', [100, 100, 350, 350]);    %the size has a huge effect on performance
set(figure_h, 'MenuBar', 'none');
set(figure_h, 'GraphicsSmoothing', 'Off');
set(figure_h, 'DockControls', 'Off');
set(figure_h, 'NumberTitle', 'Off');
set(figure_h, 'ToolBar', 'none');

%create plots
scatter_h = plot(number_array(:,1)*scale, number_array(:,2)*scale, 'r.');
set(scatter_h, 'MarkerSize',8);
set(scatter_h, 'Clipping','off');
hold on
axis square
grid on
ylim([min_value, max_value*scale]);  
xlim([min_value, max_value*scale]);
set(figure_h.CurrentAxes, 'xtick', [min_value:max_value/10:max_value]); %#ok<NBRAK>
set(figure_h.CurrentAxes, 'ytick', [min_value:max_value/10:max_value]); %#ok<NBRAK>
set(figure_h.CurrentAxes, 'XTickLabel',[]);
set(figure_h.CurrentAxes, 'YTickLabel',[]);
set(figure_h.CurrentAxes, 'ZTickLabel',[]);
set(figure_h.CurrentAxes, 'ZGrid', 'off');
set(figure_h.CurrentAxes, 'XLimMode', 'manual');
set(figure_h.CurrentAxes, 'YLimMode', 'manual');
set(figure_h.CurrentAxes, 'ZLimMode', 'manual');
set(figure_h.CurrentAxes, 'ALimMode', 'manual');
set(figure_h.CurrentAxes, 'CLimMode', 'manual');
set(figure_h.CurrentAxes, 'DataAspectRatioMode', 'manual');
set(figure_h.CurrentAxes, 'CameraPositionMode', 'manual');
set(figure_h.CurrentAxes, 'CameraPosition', [max_value/2,max_value/2,1]);
set(figure_h.CurrentAxes, 'CameraTargetMode', 'manual');
set(figure_h.CurrentAxes, 'CameraTarget', [max_value/2,max_value/2,0]);
set(figure_h.CurrentAxes, 'CameraUpVectorMode', 'manual');
set(figure_h.CurrentAxes, 'CameraUpVector', [0,1,0]);
set(figure_h.CurrentAxes, 'CameraViewAngleMode', 'manual');
set(figure_h.CurrentAxes, 'CameraViewAngle', 180);
set(figure_h.CurrentAxes, 'GridAlphaMode', 'manual');
set(figure_h.CurrentAxes, 'Clipping', 'off');
circle_h = line((radius*cos(theta) + center_x)*scale,(radius*sin(theta) + center_y)*scale);
hold off

Simplified loop for serial:

while keep_running

    %must wait for next packet to arrive
    bytes = obj.bytes_available;
    while keep_running && (bytes == 0)
        bytes = obj.bytes_available;  //function call actually directly uses the java serial objects for speed        
    end

    %send request for next packet
    obj.send_command(obj.cmd_id.cmd_history, true);  //sends synchronously, otherwise, with async, an additional wait loop is necessary to prevent an async call if serial is still transmitting (Matlab throws an error otherwise - why they can't be queued, I don't know)

    %grab the available buffer
    new_buffer = obj.buffer_available(bytes);

    %append new characters to running buffer
    char_buffer = strcat(char_buffer, new_buffer);

    %process buffer for all arrays
    while (~isempty(strfind(char_buffer, '[')) && ~isempty(strfind(char_buffer, ']'))) %#ok<STREMP>

        %extract a number array
        [number_array, char_buffer] = extract_hex_array(char_buffer,3,2);

        if ~isempty(number_array)
            %remove any duplicate points
            number_array = unique(number_array, 'rows', 'stable');

            frames = frames + 1;

            %update data
            set(scatter_h, 'xdata', number_array(:,1)*scale, 'ydata', number_array(:,2)*scale);

            %update header information (after stabilization, and every 10 frames)
            if frames > 25 && mod(frames, 10) == 0
                avg_framerate = frames/toc(total_time);
                set(figure_h, 'Name', strcat('rate: ', num2str(avg_framerate, '%.1f'), ' Hz  buffer: ',num2str(length(char_buffer), '%05d')));
            end

            %just a small delay, but not too small the callbacks stop working
            pause(0.005);
        end
    end
end

Dummy random-data loop:

start_tic = tic;
for i=1:200
    array1 = 4095*rand(82,2);
    set(scatter_h, 'xdata', array1(:,1), 'ydata', array1(:,2));
    pause(0.005);
end
end_tic = toc(start_tic);
fprintf("fps: %f\n", i/end_tic);
1
Just a note on setting the properties. you can set multiple properties in on set function, especially when their values are the same: set(figure_h.CurrentAxes, {'XLimMode','YLimMode','ZLimMode',... 'ALimMode','CLimMode','DataAspectRatioMode','CameraPositionMode',... 'CameraTargetMode','CameraUpVectorMode', 'CameraViewAngleMode',... 'GridAlphaMode'},repmat({'manual'},1,11)); - Anthony

1 Answers

0
votes

After submitting a service request with Mathworks, the official answer is that Matlab 2017a and before do not use OpenGL on Mac (even though the "opengl info" command says it is supported). Therefore the plotting will not be as fast on macOS as Windows until they fix their plotting libraries.