This question already has answers here:
How to do an animated plot in matlab
(3 answers)
Closed 4 years ago.
I wrote a simple code below for animating plots, but they tend to be rather computationally-intensive, taking entire seconds longer than intended:
function animplot(t,f,ymin,ymax,dt,num_iters)
h = plot(0,0); % set initial handle for first iteration
tic % start timer
for i=2:num_iters
delete(h);
h = plot(t,f(t-dt*i),'LineWidth',2,'color','b');
axis([min(t) max(t) ymin ymax]); pause(1/num_iters)
end
toc % end timer, return time elapsed since 'tic'
end
Replacing 1/num_iters with dT = T / num_iters, and setting T = 1, computation time for 1000 iterations is 6+ secs (rather than 1). Sample animation for t = 0:.01:2*pi; f = #(t)sin(t); dt = .05; num_iters = 1000
Any more efficient methods of animating in this manner?
A significantly more efficient code, adapted per solution in related inquiry:
function animplot(t,f,ymin,ymax,dt,num_iters,T)
% ANIMPLOT(t,f,ymin,ymax,dt,num_iters) -- f must be defined w/ handle,
% e.g. f = #(t)sin(t); default T = 5, num_iters = 500, dt = .05,
% (ymax - ymin) = 1.4*range.
switch nargin
case 6; T = 5;
case 5; T = 5; num_iters = 500;
case 4; T = 5; num_iters = 500; dt = .05;
case 2; T = 5; num_iters = 500; dt = .05;
ymin = 1.2*min(f(t)) - max(f(t))/5;
ymax = 1.2*max(f(t)) - min(f(t))/5;
end
dT = T/num_iters; % set pause interval
h = plot(0,0,'LineWidth',2,'color','b'); % set initial handle
set(h, 'Xdata',t, 'Ydata', f(t)); % initialize curve plot
axis([min(t) max(t) ymin ymax]);
tic % start timer
for i=2:num_iters
pause(dT)
set(h, 'Ydata', f(t-dt*i))
end
toc % end timer, return time elapsed since 'tic'
end
Note: num_iters serves primarily as animation 'resolution'; higher comes at expense of greater deviation from set T.
Using the command: "set(h, 'Xdata',t, 'Ydata', f(t))" will be way more efficient than reusing the plot function on every loop iteration, and may be sufficient by itself to achieve the results you are looking for. For further improvement you can enable a variable frame rate. To do this, eliminate any "pause(dt)", get the time of the current loop iteration "t", evaluate your function for that time "f(t)" and set the plot x and y data to that. This will ensure that the animation is smooth does not take any longer than specified. You can also use this technique on data sets, not just functions, by using the "interp1(x,v,xq)" function to get a linear interpolation between data points.
Related
As part of the assignment, we have to plot the free vibration response of a single degree of freedom graph over the displacement vs time. The t should increase up to 3 seconds with the increment of the .05. When I try to plot the graph based on the code below I am not getting any graphs at all. Please any help would be highly appreciated. Please let me know if need to provide more information.
% Main subroutine to generate the response of a SDOF
z = 0.05;
w = 2*pi;
dt = 0.05; % dt represent the change in time which is constant
Tmax = 3;
TN = 1.0;
i = 0; % number of time step starting with 0
x = 0; % initial position
v = 0; % velocity at start time and position
x_vector = [x; v];
t = [0:0.05:3]; % defining the time vector 0 to 3 seconds
t_i = t;
% Insert the DHMAT scripts for calculating the matrix A and B
w_d = w*sqrt(1-z.^2);
DHMAT;
% Start the loop
for t_i = [0:dt:Tmax]
% Insert the EXCIT script to calculate the excitation (f_vector)
f = EXIT(t, dt);
f_vector =[1;1];
i = i+1;
x_vectornew = A*x_vector - B*f_vector;
% t_i = t_i + dt;
end
plot(t_i, x_vectornew);
I want to replicate a figure from this article. More specifically, I want to replicate Figure number 4, which I believe is the representation of Equation 9.
So far I have come up with this code:
% implementing equation 9 and figure 4
step = 0.01; t = 1:step:3600;
d = 3; % dimension
N = 8000; % number of molecules
H = 0.01; % H = [0.01,0.1,1] is in mol/micrometer^3
H = H*6.02214078^5; % hence I scaled the Avogadro's number (right or wrong?)
D = 10; % diffusion coefficient in micrometer^2/sec
u(1) = 1./(1.^(d/2)); % inner function in equation 9; first pulse
for i = 2:numel(t)/1000
u(i) = u(i-1)+(1./(i.^(d/2))); % u-> the pulse number
lmda(i) = (1/(4*pi*D))*((N/(H)).*sum(u)).^(2/d);
end
figure;plot(lmda)
But I am not able to replicate it.
Equation 9
For details on the parameters, refer to the above code. The authors did mention that the summation in equation 9 is a Reimann Zeta series. Wonder if that has anything to do with the result?
Figure 4, which I am trying to replicate:
Could someone kindly tell me the mistake I am making?
P.s: This is not a homework.
Problem 1: You think you are scaling by Avogadro's number on this line
H = H*6.02214078^5;
In fact, you're scaling by approximately 7920=6.022^5. If you wanted to scale by the Avogadro number then you should do:
H = H * 6.02214078e23 % = 6.02214078 * 10^23 : the Avogadro number
Problem 2: You aren't plotting against t, you're plotting against the sample number which doesn't really make sense (unless your t happened to be in integer seconds). Remove the /1000 from your loop
for i = 2:numel(t)
% ...
end
% Then plot
plot(t, lmda)
At this stage we can see something is really wrong. Now that we're scaling by the correct Avo number, the orders of magnitude are way out. I suggest that you trust the H in figure 4 and the H in equation 9 are the same H, it would be very confusing if the author intended anything different!
On that basis, I would suggest you are using the wrong D, N, or time between pulses. I've set up the pulse timing a bit clearer in my code below. I've also streamlined your loop somewhat using vectorisation, and removed the H scaling.
If you tweak it so dtPulses=100 as well as D=100, then the plots are almost identical. You perhaps need to consider how these two numbers affect the result...
% implementing equation 9 and figure 4
d = 3; % dimension
N = 8000; % number of molecules
D = 100; % diffusion coefficient in micrometer^2/sec
dtPulses = 10; % Seconds between pulses
tPulses = 1:dtPulses:3600; % Time array to plot against
nt = numel(tPulses);
i = 1:nt; % pulse numbers
u = 1 ./ (i.^(d/2)); % inner function in equation 9: individual pulse
for k = 2:nt % Running sum
u(k) = u(k-1)+u(k);
end
% Now plot for different H (mol/micrometer^3)
H = [0.01, 0.1, 1];
figure; hold on; linestyles = {':k', '--k', '-k'};
for nH = 1:3
lmda = ((1/(4*pi*D))*(N/H(nH)).*u).^(2/d);
plot(tPulses, lmda, linestyles{nH}, 'linewidth', 2)
end
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)
I am trying to use a finite difference method to solve the heat equation for a given function u0. I want to evolve it in time using plots to see each frame change but when I run it all I see is the same frame over and over. I am aware of the error at the ends due to me not giving "u" a first or last index which will cause more overall error in the solution. I am not very good at matlab so I am not sure if I am writing my nested piece correctly.
% set up domain in time and space
t_step = .1;
tspan = [0 :t_step:1000];
L = 10; %length of domain
n = 64;
dx = 2*pi/(n-1); %spacing
x = 0:dx:2*pi;
x = x'; %make x vertical vector
% initial conditions for the function
u0 = sin(2*pi*x/L)+0*cos(3*2*pi*x/L) + 0.2*(cos(5*2*pi*x/L))+0*randn(size(x));
plot(x,u0);title('initial condition');pause;
t = 0;
for m = 1:tspan(end)
u = u0;
t = t + t_step;
for j= 2:n-1
u(j,m) = u0(j-1) - 2*u0(j) + u0(j+1)/dx^2; %u(1) and n left out
end
plot(x,u(:,m))
pause(0.01);
end
Lets say I have a simple logistic equation
dx/dt = 2ax(1 - x/N)
where N is the carrying capacity, a is some growth rate, and both a and N are parameters I'd like to vary.
So what I want to do is to plot a 3D graph of my fixed point and the two parameters.
I understand how to find a fixed point of a single parameter.
Here is my sample code
function xprime = MyLogisticFunction(t,X) %% The ODE
% Parameters
N = 10 % Carrying Capacity
a = 0.5 % Growth Rate
x1prime = 2*a*X(1)*(1 - X(1)/N );
xprime = [x1prime ]';
end
Next my solver
% Initial Number
x0 = 0.4;
%Time Window
tspan=[0 100];
[t,x]=ode45(#MyLogisticFunction,tspan,x0);
clf
x(end,1) % This gives me the fixed point for the parameters above.
So my real question is, how do I put a for loop across two functions, that allows me to vary a and N, so that I can plot out a 3D graph of a and N and my fixed point x*.
I've tried combining both functions into one .m file but it does not seem to work
You need to pass the parameters to your function:
function xprime = MyLogisticFunction(t,X,a,N) %% The ODE
% Parameters (passed as function arguments)
% N = 10 % Carrying Capacity
% a = 0.5 % Growth Rate
x1prime = 2*a*X(1)*(1 - X(1)/N );
xprime = [x1prime ]';
end
and then when you call the ode solver:
% Initial Number
x0 = 0.4;
%Time Window
tspan=[0 100];
a = 0.1:0.1:1; % or whatever
N = 1:10; % or whatever
x_end = zeros(length(a),length(N));
for ii = 1:length(a)
for jj = 1:length(N)
[t,x]=ode45(#(t,X)MyLogisticFunction(t,X,a(ii),N(jj)),tspan,x0);
x_end(ii,jj) = x(end,1);
end
end