How to speed up a very slow animated plot in MATLAB - matlab

I'm trying to create an animated plot but my code is very slow, perhaps the method I'm using is too naive. In the below example, I have 4 subplots each with 3 lines, which I update in a 'time' loop.
clc;clear;close all;
state = {'$x-Position$','$x-Velocity$','$y-Position$','$y-Velocity$'};
ylabels = {'$x$','$\dot{x}$','$y$','$\dot{y}$'};
options1 = {'interpreter','latex'};
options2 = {'interpreter','latex','fontsize',20};
maxT = 300;
for pp = 1:4
hh1(pp)=subplot(2,2,pp);
xlabel('$t$',options2{:});
ylabel(ylabels{pp},options2{:});
title(state{pp},options1{:})
xlim([0 maxT])
hold on
end
x = randn(4,300);
z = randn(4,300);
x_est = randn(4,300);
for k = 2:maxT
for p = 1:4
plot(hh1(p),k-1:k,x(p,k-1:k),'b','linewidth',2)
plot(hh1(p),k-1:k,z(p,k-1:k),'m')
plot(hh1(p),k-1:k,x_est(p,k-1:k),':k','linewidth',2)
end
drawnow;
end
As can be seen from the profiler output, the drawnow is killing the time. Is there any way I can be more efficient in creating this animation?

Because you want an animation, there is no alternative to using drawnow to update the frame. However, it's not drawnow in particular which is slowing you down - the profiler can be misleading... drawnow simply updates all of the graphics changes since the last re-draw, which in your case is a dozen new plots!
You'll find that hold is pretty slowing. For instance if you're wiser about your holding, remove the existing hold on and only hold when actually plotting
% ... above code the same but without 'hold on'
for p = 1:4
hold(hh1(p), 'on');
% plots
hold(hh1(p), 'off');
end
This saves ~10% time on my PC (12.3sec down to 11.3sec).
The real speed up comes from removing hold entirely, along with all of the individual plot calls! This method also doesn't touch the line formatting which will help with speed. See a previous question about updating plot data here.
Simply update the plot data instead of adding plots. This gives me a speedup of ~68% (12.3sec down to 4.0sec).
% ... your same setup
% Initialise plot data
x = randn(4,300);
z = randn(4,300);
x_est = randn(4,300);
plts = cell(4,3);
hh1 = cell(4,1);
% Loop over subplots and initialise plot lines
for p = 1:4
hh1{p}=subplot(2,2,p);
xlabel('$t$',options2{:});
ylabel(ylabels{p},options2{:});
title(state{p},options1{:})
xlim([0 maxT])
% Hold on to make 3 plots. Create initial points and set line styles.
% Store the plots in a cell array for later reference.
hold on
plts{p,1} = plot(hh1{p},1:2,x(p,1:2),'b','linewidth',2);
plts{p,2} = plot(hh1{p},1:2,z(p,1:2),'m');
plts{p,3} = plot(hh1{p},1:2,x_est(p,1:2),':k','linewidth',2);
hold off
end
% March through time. No replotting required, just update XData and YData
for k = 2:maxT
for p = 1:4
set(plts{p,1}, 'XData', 1:k, 'YData', x(p,1:k) );
set(plts{p,2}, 'XData', 1:k, 'YData', z(p,1:k) );
set(plts{p,3}, 'XData', 1:k, 'YData', x_est(p,1:k) );
end
drawnow;
end
Now the plotting is pretty optimised. If you want the animation to be even quicker then just plot every 2nd, 3rd, ..., nth timestep instead of every timestep by using for k = 2:n:maxT.

Related

How to plot an array using a for loop?

I want to plot inside a for loop. I tried the following code also used hold on but the plot is still blank. I don't know where I am getting wrong.
M2 = true(21, 6);
M2(1:2, 3:5) = false;
R = [0.5:0.1:2.5];
H=[0:5:25];
m=21,
n=6,
for i=1:m
for j=1:n
if M(i,j)==0
plot(H(i),R(j), 'color', 'r')
drawnow();
end
end
end
As Ander Biguri already said, the plot function actually does not draw anything when you provide only one point and do not specify any markers.
But, if you want to create some animated plot, that one of the points is displayed at a time:
h = scatter(0, 0, 'r');
xlim([min(H) max(H)]);
ylim([min(R) max(R)]);
for i=1:m
for j=1:n
if M(i,j)==0
set(h, 'xdata', H(i), 'ydata', R(j));
pause(0.5);
drawnow();
end
end
end
Or if you want to show all point together, you don't even need a loop:
[HH, RR] = meshgrid(H, R);
scatter(HH(~M), RR(~M), 'r');
xlim([min(H) max(H)]);
ylim([min(R) max(R)]);

How do i produce an animated GIF in MATLAB?

I want to produce a animated gif of a solution to a partial differential equation. That is the gif should show the solution at specific time.
Currently I can only make pictures in which all times are plotted.
Below is my entire program, with figure(3) being my attempt of making a gif.
clear all;
close all;
%%%%%%%%%%%%
% For slide 27 of Diffusion 1D
% The equation to be graphed in latex form is
% u(x,t)=\frac{1}{L}+\frac{2}{L}\sum^{\infty}_{n=1}cos(\frac{n\pi x_0}{L})cos(\frac{n\pi x}{L})e^{-k(\frac{n\pi}{L})^2t}
%%%%%%%%%%%%
%define constants
%note that the constants listed in the file are arbitrary
L = 2; %length of the rod
k= 0.01; % Diffusivity, which is assume to be constant but can be a function of x
x0 = 1; %location of the inital condition i.e. f(x)=delta(x-x0)
tmax= 50; %maximum amount of time the simulation runs
nmax = 200; % maximum value for n, increase to accuracy
tgrid = 21; %The number of points to be evaluated in the time domain
xgrid = 51; %The number of points to be evaluated in the space domain
%initialize variables
u=zeros(tgrid,xgrid); %preallocate array used for storing values of the solution
t=linspace(0,tmax,tgrid);%We assume that time is evenly distributed
x=linspace(0,L,xgrid); %We assume that space is evenly distributed
%Plotting variables
figure(1);
hold on;
axis([0 L -inf inf]);
xlabel('x');
ylabel('u(x,t)');
%Calculation,
for i=1:tgrid
for j=1:xgrid
seriesSum=0;
%Calculate the fourier series up to nmax for each point u(x,t)
for n= 1:nmax
seriesSum= seriesSum + cos(n*pi*x0/L)*cos(n*pi*x(j)/L)*exp(-k*t(i)*(n*pi/L)^2);
end
%Finish calcuation for solution at a specific point
u(i,j)= 1/L+(2/L)*seriesSum;
end
%After we have calculated all points at time t, we graph it for time t
plot(x,u(i,:),'linewidth',4);
end
saveas(gcf,'PDE_sol.png')%Save figure as png in current directory
%run a second loop that does not include the initial condition to get a
%better view of the long term behaviour.
%Plotting variables
figure(2);
hold on;
axis([0 L -inf inf]);
xlabel('x');
ylabel('u(x,t)');
for i=2:tgrid
plot(x,u(i,:),'linewidth',4);
end
saveas(gcf,'PDE_sol_without_inital.png')%Save figure as png in current directory
%Create a gif verison of figure 2
figure(3);
axis([0 L -inf inf]);
xlabel('x');
ylabel('u(x,t)');
filename = 'PDE_sol.gif';
for i=2:tgrid
plot(x,u(i,:),'linewidth',4);
drawnow
frame = getframe(1);
im = frame2im(frame);
[imind,cm] = rgb2ind(im,256);
if i == 2;
imwrite(imind,cm,filename,'gif', 'Loopcount',inf);
else
imwrite(imind,cm,filename,'gif','WriteMode','append');
end
end
The output gif that I get is
which is clearly not animated.
Note: If you think there is a better place to post this question please direct me to it. As my issue is with the MATLAB programming language and not the math involved I thought this would be the best place to post my question.
The first input to getframe is the handle of the figure that you'd like to take a screenshot of. As you have it written, you are grabbing figure 1 which is actually referring to the first figure that you create that you aren't updating within your loop.
You have assigned a numeric handle of 3 to the figure that you create right before your last loop so you'll want to tell getframe to use that figure instead.
Additionally, I would create one plot object and update the XData and YData rather than continuously creating new plot objects. The issue with calling plot continuously is that it's slow AND it completely resets all of your axes settings such as x and y labels as well as x and y limits.
% Store the handle to the figure in hfig
hfig = figure(3);
% Create the initial plot object
hplot = plot(NaN, NaN, 'LineWidth', 4);
axis([0 L 0 2]);
xlabel('x');
ylabel('u(x,t)');
filename = 'PDE_sol.gif';
for i=2:tgrid
% Update the plot appearance
set(hplot, 'XData', x, 'YData', u(i,:));
drawnow
% Get a screenshot of THIS figure
frame = getframe(hfig);
im = frame2im(frame);
[imind,cm] = rgb2ind(im,256);
if i == 2;
imwrite(imind,cm,filename,'gif', 'Loopcount',inf);
else
imwrite(imind,cm,filename,'gif','WriteMode','append');
end
end

Slowing Speed with each for loop iteration in matlab

I have written a while loop in Matlab that is supposed to send each value in an array from Matlab to arduino at a specified time interval using a tic toc delay in Matlab and then read values and store them in a variable and graph them.
The output of the while-loop slows down with each successive iteration.
I increased the buffersize which helped it a lot, but it still slows down too much. Is there another way to increase the speed to print the values on time. I have included another tic toc and graph to show the execution speed here is the code:
max = 80;
min = 40;
amp = (max-min)/2;
offset = amp + min;
btime = 5;
bpm = 12;
spb = 60/bpm;
sapb = spb/.05;
tosd = sapb*bpm*btime;
time1 = btime*60;
x = linspace(0,time1,tosd)';
x1 = amp*sin(x*(2*pi/20)) + offset;
pause(1);
fprintf(handles.UltraM,(['<P' num2str(offset) '>']))
pause(5);
y = [];
i = 1;
figure(1);
hold on;
title('Pressure Data');
xlabel('Data Number');
ylabel('Analog Voltage (0-1023)');
t1 = [];
figure(2);
hold on;
title('Time to execute task');
xlabel('iteration number');
ylabel('time taken');
while (i<=length(x))
t2 = tic;
t = tic;
fprintf(handles.UltraM,(['<P' num2str(x1(i)) '>']));
%disp((['<P' num2str(x1(i)) '>']));
y(i) = fscanf(handles.UltraM,'%d');
figure(1);
hold on;
plot(i, y(i), 'b*');
drawnow;
hold off;
while toc(t) < 0.05
continue
end
t1(i) = toc(t2);
figure(2);
hold on;
plot(i,t1(i),'b*');
drawnow;
hold off;
i = i + 1;
end
After a bit of back and forth I think I know what you're trying to achieve and what stands in your way.
I have edited your code to make it slightly more fast and readable. Most of the time the operations take just slightly above 0.05 seconds, and at several time points it can take about 5 milliseconds longer than expected. Your millage may vary of course. Since I do not have an arduino, I cannot know if there is a bottle neck there. You should also try profiling your code using the builtin Matlab profiler (it's very useful) to see what exactly slows your code down.
The main thing I found to slow your code is that you used the plot function to add one point at a time to your figure. Each time you call this function, it creates a new graphics object. After a few hundred of those, things get sluggish. Instead you should simply update the already plotted data and redraw it using drawnow.
In short, the solution is this:
1) Initialize you plot with a single point and save the graphics handle for later use:
p1 = plot(0,0,'b*');
2) Then, inside the loop, once your data arrays have been updated, replace the data in your existing plot with the new arrays.
set(p1, 'XData', 1:i, 'YData', y(1:i));
3) Redraw the plots to reflect the latest update.
drawnow;
drawnow will eventually slow down your code also since it has to redraw increasingly large plots at every iteration. To make things work faster, you might want to refresh your plot after a longer interval. For example, the following will refresh every 10 iterations:
if rem(i,10) == 0
drawnow;
end
Full code below. Let me know if you have any more issues.
max = 80;
min = 40;
amp = (max-min)/2;
offset = amp + min;
btime = 5;
bpm = 12;
spb = 60/bpm;
sapb = spb/.05;
tosd = sapb*bpm*btime;
time1 = btime*60;
x = linspace(0,time1,tosd)';
x1 = amp*sin(x*(2*pi/20)) + offset;
pause(1);
%fprintf(handles.UltraM,(['<P' num2str(offset) '>']))
disp(['<P' num2str(offset) '>']); % replacing with disp (I don't have an arduino)
pause(5);
%y = []; % unnecessary here, preallocated before loop
figure(1);
p1 = plot(0,0,'b*'); % plotting one dot to create an object, data will be overwritten
hold on;
title('Pressure Data');
xlabel('Data Number');
ylabel('Analog Voltage (0-1023)');
%t1 = []; % unnecessary here, preallocated before loop
figure(2);
p2 = plot(0,0,'b*'); % plotting one dot to create an object, data will be overwritten
hold on;
title('Time to execute task');
xlabel('iteration number');
ylabel('time taken');
% preallocate t1 and y arrays for faster operation
t1 = zeros(size(x));
y = zeros(size(x));
i = 1; % moved closer to loop beginning for better readability
while i <= length(x) % parentheses unnecessary in Matlab
t2 = tic;
t = tic;
%fprintf(handles.UltraM,(['<P' num2str(x1(i)) '>']));
disp((['<P' num2str(x1(i)) '>'])); % replacing with disp (I don't have an arduino)
%y(i) = fscanf(handles.UltraM,'%d');
y(i) = randn; % replacing with random number (I don't have an arduino)
%figure(1); % unnecessary
%hold on; % unnecessary
%plot(i, y(i), 'b*');
% replacing the above with a slightly faster version
set(p1, 'XData', 1:i, 'YData', y(1:i));
%drawnow; % first one is annecessary
%hold off; % unnecessary
while toc(t) < 0.05
continue
end
t1(i) = toc(t2);
%figure(2); % unnecessary
%hold on; % unnecessary
%plot(i,t1(i),'b*');
% replacing the above with a slightly faster version
set(p2, 'XData', 1:i, 'YData', t1(1:i));
if rem(i,10) == 0 % refreshing every 10 iterations
drawnow;
end
%hold off; % unnecessary
i = i + 1;
end
ANSWER TO PREVIOUS VERSION OF QUESTION
You can vectorize your loop by replacing it entirely with the following two statements:
% vectorizing num-to-string conversion
y4 = cellstr(strcat('<P',num2str(x1), '>'));
% deleting all spaces
y4 = cellfun(#(u) u(~isspace(u)), y4, 'UniformOutput', false)
This small adjustment makes your program run x4 faster on my PC.
Displaying/printing the results could also be done using the cellfun iterator: cellfun(#disp, y4)

MATLAB - hold on for subplot between different figures (Solved, but may have issues on Version R2012a)

I'm new to MATLAB, and I've been searching around for what I'm trying to do, but the results don't fit quite well.
I'm graphing plots of variations of transfer functions, the code I've done is below:
omega = 3;
K = omega * omega;
for zeta = 0.1:0.1:2
sys = tf(K,[1 2*zeta*omega omega]);
figure();
subplot(1,2,1);
step(sys);
title('Step response');
[num,den] = tfdata(sys, 'v');
disp(den);
r = roots(den);
subplot(1,2,2);
%hold (subplot(1,2,2), 'on');
plot(real(r), imag(r), 'o');
title('Pole Locations in Complex Plane');
end
Each time the loop runs, it will create a new figure. The first subplot should be unique for every figure, but the second subplot should plot the accumulation of all points (roots of denominator of all transfer functions) from figures before it. I tried to use hold (subplot(1,2,2), 'on'); to keep the second subplot, but it didn't work. My thought is that because the subplots are different figures, hold on cannot be used.
How can I solve this problem? Any help will be great.
A solution is to use 'Tag' in your subplot. I am using your code to edit:
omega = 3;
K = omega * omega;
for zeta = 0.1:0.1:2
sys = tf(K,[1 2*zeta*omega omega]);
figure();
sb = subplot(1,2,1);
set(sb, 'Tag', 'daddy') % Something to tag with - memorable
step(sys);
title('Step response');
[num,den] = tfdata(sys, 'v');
disp(den);
r = roots(den);
sb = subplot(1,2,2);
set(sb, 'Tag', 'child')
sb = findobj('Tag','child'); % Use MATLAB methods to find your tagged obj
set(sb,'NextPlot','add'); % set 'NextPlot' property to 'add'
plot(real(r), imag(r), 'o');
title('Pole Locations in Complex Plane');
end
DOes this work for you? btw. This is also in MATLAB central. You should use that too.

Creating a point moving along a graph in MATLAB

I am looking to create a simple log(x) graph within MATLAB in which the model shows the point moving along the curve with time.
The overall aim is to have two of these graphs alongside one another and to apply an algorithm to them. I am really unsure where to start here.
I am relatively new at MATLAB coding so any help would be very useful!
Thanks
Luke
Here is a variation on #Jacob's solution. Instead of redrawing everything at each frame (clf) we simply update the point's location:
%# control animation speed
DELAY = 0.01;
numPoints = 600;
%# create data
x = linspace(0,10,numPoints);
y = log(x);
%# plot graph
figure('DoubleBuffer','on') %# no flickering
plot(x,y, 'LineWidth',2), grid on
xlabel('x'), ylabel('y'), title('y = log(x)')
%# create moving point + coords text
hLine = line('XData',x(1), 'YData',y(1), 'Color','r', ...
'Marker','o', 'MarkerSize',6, 'LineWidth',2);
hTxt = text(x(1), y(1), sprintf('(%.3f,%.3f)',x(1),y(1)), ...
'Color',[0.2 0.2 0.2], 'FontSize',8, ...
'HorizontalAlignment','left', 'VerticalAlignment','top');
%# infinite loop
i = 1; %# index
while true
%# update point & text
set(hLine, 'XData',x(i), 'YData',y(i))
set(hTxt, 'Position',[x(i) y(i)], ...
'String',sprintf('(%.3f,%.3f)',[x(i) y(i)]))
drawnow %# force refresh
%#pause(DELAY) %# slow down animation
i = rem(i+1,numPoints)+1; %# circular increment
if ~ishandle(hLine), break; end %# in case you close the figure
end
A simple solution is:
x = 1:100;
y = log(x);
DELAY = 0.05;
for i = 1:numel(x)
clf;
plot(x,y);
hold on;
plot(x(i),y(i),'r*');
pause(DELAY);
end
You may want to have a look at the COMET function, which will make an animation of the curve.
For example (using the same numbers as #Jacob)
x = 1:100;
y = log(x);
comet(x,y)
If you want to show the point moving on the line (not 'drawing' it), you simply plot the line before
x = 1:100;
y = log(x);
plot(x,y,'r')
hold on %# to keep the previous plot
comet(x,y,0) %# 0 hides the green tail
a little more complex solution along the same lines as #Jacob. Here I add some optimization using handle graphics and a MATLAB movie object for playback.
x=1:100;
y=log(x);
figure
plot(x,y);
hold on; % hold on so that the figure is not cleared
h=plot(x(1),y(1),'r*'); % plot the first point
DELAY=.05;
for i=1:length(x)
set(h,'xdata',x(i),'ydata',y(i)); % move the point using set
% to change the cooridinates.
M(i)=getframe(gcf);
pause(DELAY)
end
%% Play the movie back
% create figure and axes for playback
figure
hh=axes;
set(hh,'units','normalized','pos',[0 0 1 1]);
axis off
movie(M) % play the movie created in the first part
solution can be this way
x = .01:.01:3;
comet(x,log(x))