I am using the code below to plot data from the serial port. Since I have two axes for plotting, how can I select a particular axes for this plot?
From similar problem, I found that they use axes(handles.axes2);. Since I have the plot declared at the start of the program, where should I place this line of code? I tried placing it before specifying the plot title etc. but it is not working.
% Serial Data Logger
% Yu Hin Hau
% 7/9/2013
% **CLOSE PLOT TO END SESSION
clear
clc
%User Defined Properties
serialPort = 'COM5'; % define COM port #
plotTitle = 'Serial Data Log'; % plot title
xLabel = 'Elapsed Time (s)'; % x-axis label
yLabel = 'Data'; % y-axis label
plotGrid = 'on'; % 'off' to turn off grid
min = -1.5; % set y-min
max = 1.5; % set y-max
scrollWidth = 10; % display period in plot, plot entire data log if <= 0
delay = .01; % make sure sample faster than resolution
%Define Function Variables
time = 0;
data = 0;
count = 0;
%Set up Plot
plotGraph = plot(time,data,'-mo',...
'LineWidth',1,...
'MarkerEdgeColor','k',...
'MarkerFaceColor',[.49 1 .63],...
'MarkerSize',2);
title(plotTitle,'FontSize',25);
xlabel(xLabel,'FontSize',15);
ylabel(yLabel,'FontSize',15);
axis([0 10 min max]);
grid(plotGrid);
%Open Serial COM Port
s = serial(serialPort)
disp('Close Plot to End Session');
fopen(s);
tic
while ishandle(plotGraph) %Loop when Plot is Active
dat = fscanf(s,'%f'); %Read Data from Serial as Float
if(~isempty(dat) && isfloat(dat)) %Make sure Data Type is Correct
count = count + 1;
time(count) = toc; %Extract Elapsed Time
data(count) = dat(1); %Extract 1st Data Element
%Set Axis according to Scroll Width
if(scrollWidth > 0)
set(plotGraph,'XData',time(time > time(count)-scrollWidth),'YData',data(time > time(count)-scrollWidth));
axis([time(count)-scrollWidth time(count) min max]);
else
set(plotGraph,'XData',time,'YData',data);
axis([0 time(count) min max]);
end
%Allow MATLAB to Update Plot
pause(delay);
end
end
%Close Serial COM Port and Delete useless Variables
fclose(s);
clear count dat delay max min plotGraph plotGrid plotTitle s ...
scrollWidth serialPort xLabel yLabel;
disp('Session Terminated...');
The trick to get reliable plotting and manipulation is to always specify the parent explicitly using the Parent parameter when creating a plot or any other graphics object. All graphics objects support this parameter.
hax = axes();
plot(x,y, 'Parent', hax);
The other alternative, as suggested by #matlabgui is to specify the parent axes as the first input to plot:
plot(hax, x, y);
I personally prefer to use the Parent parameter as a parameter value pair though, as that behavior is consistent across all graphics objects.
You should also specify the axes handle when using other functions which operate on an axes.
xlabel(hax, 'XLabel')
ylabel(hax, 'YLabel')
title(hax, 'This is a title')
axis(hax, [0 0 1 1])
grid(hax, 'on')
hold(hax, 'on')
This is particularly important if you are dealing with an interactive GUI as the user could easily click on a different axes in the middle of your plotting causing the value of gca to change unexpectedly. Also changing the current axes (using axes(hax)) can cause a poor user experience.
Summary
For your specific code, this would involve changing your initial plot call:
plotGraph = plot(time,data,'-mo',...
'LineWidth',1,...
'MarkerEdgeColor','k',...
'MarkerFaceColor',[.49 1 .63],...
'MarkerSize',2, ...
'Parent', handles.axes2);
I would also recommend adding explicit axes handles to your calls to: grid, title, axis, xlabel, and ylabel to ensure that their target is the axes you want.
Related
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.
Suppose I have a 5x5 matrix.
The elements of the matrix change (are refreshed) every second.
I would like to be able to display the matrix (not as a colormap but with the actual values in a grid) in realtime and watch the values in it change as time progresses.
How would I go about doing so in MATLAB?
A combination of clc and disp is the easiest approach (as answered by Tim), here's a "prettier" approach you might fancy, depending on your needs. This is not going to be as quick, but you might find some benefits, such as not having to clear the command window or being able to colour-code and save the figs.
Using dispMatrixInFig (code at the bottom of this answer) you can view the matrix in a figure window (or unique figure windows) at each stage.
Example test code:
fig = figure;
% Loop 10 times, pausing for 1sec each loop, display matrix
for i=1:10
A = rand(5, 5);
dispMatrixInFig(A,fig)
pause(1)
end
Output for one iteration:
Commented function code:
function dispMatrixInFig(A, fig, strstyle, figname)
%% Given a figure "fig" and a matrix "A", the matrix is displayed in the
% figure. If no figure is supplied then a new one is created.
%
% strstyle is optional to specify the string display of each value, for
% details see SPRINTF. Default is 4d.p. Can set to default by passing '' or
% no argument.
%
% figname will appear in the title bar of the figure.
if nargin < 2
fig = figure;
else
clf(fig);
end
if nargin < 3 || strcmp(strstyle, '')
strstyle = '%3.4f';
end
if nargin < 4
figname = '';
end
% Get size of matrix
[m,n] = size(A);
% Turn axes off, set origin to top left
axis off;
axis ij;
set(fig,'DefaultTextFontName','courier', ...
'DefaultTextHorizontalAlignment','left', ...
'DefaultTextVerticalAlignment','bottom', ...
'DefaultTextClipping','on');
fig.Name = figname;
axis([1, m-1, 1, n]);
drawnow
tmp = text(.5,.5,'t');
% height and width of character
ext = get(tmp, 'Extent');
dy = ext(4);
wch = ext(3);
dwc = 2*wch;
dx = 8*wch + dwc;
% set matrix values to fig positions
x = 1;
for i = 1:n
y = 0.5 + dy/2;
for j = 1:m
y = y + 1;
text(x,y,sprintf(strstyle,A(j,i)));
end
x = x + dx;
end
% Tidy up display
axis([1-dwc/2 1+n*dx-dwc/2 1 m+1]);
set(gca, 'YTick', [], 'XTickLabel',[],'Visible','on');
set(gca,'XTick',(1-dwc/2):dx:x);
set(gca,'XGrid','on','GridLineStyle','-');
end
I would have thought you could achieve this with disp:
for i=1:10
A = rand(5, 5);
disp(A);
end
If you mean that you don't want repeated outputs on top of each other in the console, you could include a clc to clear the console before each disp call:
for i=1:10
A = rand(5, 5);
clc;
disp(A);
end
If you want to display your matrix on a figure it is quite easy. Just make a dump matrix and display it. Then use text function to display your matrix on the figure. For example
randMatrix=rand(5);
figure,imagesc(ones(20));axis image;
hold on;text(2,10,num2str(randMatrix))
If you want to do it in a for loop and see the numbers change, try this:
for i=1:100;
randMatrix=rand(5);
figure(1),clf
imagesc(ones(20));axis image;
hold on;text(2,10,num2str(randMatrix));
drawnow;
end
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
I'm trying to limit x axis, i.e., frequency axis to 4 Hz in MatLab. This is the code I used:
subplot(3,1,2);
%Fse = 220;
time = 0:1/fse:secBuffer-1/fse;
%a = eegCounter;
c = eegBuffer;
wo = 50 / (1000/2);
bw = wo / 60;
[b,a] = iirnotch(wo,bw);
y = filter(b,a,c);
ydft = fft(c);
xdft = fft(y);
xlabel('Frequency');
ylabel('Signal');
xlim([1,4]);
ylim([1,4]);
plot(xdft,ydft);
However mine is live signal plotting and both x axis and y axis keep changing according to incoming packets. How to limit x axis to 4 Hz?
When plotting MATLAB automatically tries to fit the axis with the dynamic range of the data. Therefore if you want to make sure only a given range is plotted, you need to specify it AFTER the call to plot to force MATLAB to do it, otherwise it won't and you will be stuck with the whole data.
Here is a very simple code in which I call xlim either before or after the call to plot. See the difference?
clear
clc
close all
x = 1:50;
y = x.^2;
figure
subplot(1,2,1)
xlim([1 20])
plot(x,y)
title('xlim before call to plot')
subplot(1,2,2)
plot(x,y)
xlim([1 20])
title('xlim after call to plot')
Produces this:
You have to set the XLimMode (and YLimMode) properties of the axes to manual. But even if you do so every call to plot(...) will reset that to auto and mess up your axes limits.
The cleanest way is to first define your axes and your plots outside of any loop (not forgetting to get their handle), then when you update the data just update the XData and YData of the line objects, using the set method. The set method will only update the property you pass in parameters, so it will not modify the XLimMode property.
%// This part of the code should run only once
h.ax = subplot(3,1,2) ; %// get the handle of the axes
h.line = plot(0) ; %// create an empty line plot
set(h.ax , 'XLimMode','manual' , 'XLim',[1 4]) ; %// define the properties of the axes (X)
set(h.ax , 'YLimMode','manual' , 'YLim',[1 4]) ; %// define the properties of the axes (Y)
xlabel('Frequency');
ylabel('Signal');
%//
%// This part of the code is the loop where you calculate and update your plot
%// ...
%// now do your calculations
%// ...
%// when it is time to update, just call:
set( h.line, 'XData',xdft 'YData',ydft ) ;
You can use the function axis as defined there axis function matlab
I have a 576x576x150 matrix. Each 576x576 set represents an image. When I want to plot one frame I do it by using the plot command:
figure(1);
imshow(B(:,:,45),[]) % plots frame 45
title('45') % tells frame number
However I would like to add a slider to the plot, so I can move from 1-150 frame within the figure.I've seen examples of people using uicontrol but I don't know how to code it. In addition to that, I would like to have a title on top of the figure telling me the frame number.
Here is how I do it. I like to keep a single function that does the plotting so you don't recycle commands elsewhere. You could replace the first two lines by function test(B) to use you own B matrix. This code is pretty easy to extend. You will also want to play with the layout to suit your purpose.
function test
B=rand(576,576,150);
fig=figure(100);
set(fig,'Name','Image','Toolbar','figure',...
'NumberTitle','off')
% Create an axes to plot in
axes('Position',[.15 .05 .7 .9]);
% sliders for epsilon and lambda
slider1_handle=uicontrol(fig,'Style','slider','Max',150,'Min',1,...
'Value',2,'SliderStep',[1/(150-1) 10/(150-1)],...
'Units','normalized','Position',[.02 .02 .14 .05]);
uicontrol(fig,'Style','text','Units','normalized','Position',[.02 .07 .14 .04],...
'String','Choose frame');
% Set up callbacks
vars=struct('slider1_handle',slider1_handle,'B',B);
set(slider1_handle,'Callback',{#slider1_callback,vars});
plotterfcn(vars)
% End of main file
% Callback subfunctions to support UI actions
function slider1_callback(~,~,vars)
% Run slider1 which controls value of epsilon
plotterfcn(vars)
function plotterfcn(vars)
% Plots the image
imshow(vars.B(:,:,get(vars.slider1_handle,'Value')));
title(num2str(get(vars.slider1_handle,'Value')));
The idea is to use uicontrol() to enable sliding/scrolling.
The following code is for scrolling (created by Evan Brooks, you can modify it to sliding):
function scrollfigdemo
% create new figure window
f = figure;
set(f,'doublebuffer', 'on', 'resize', 'off')
% set columns of plots
cols = 2;
% create 5 data sets to plot
x=0:1e-2:2*pi;
y{1}=sin(x);
y{2}=cos(x);
y{3}=tan(x);
y{4}=x.^2;
y{5}=x.^3;
% determine required rows of plots
rows = ceil(length(y)/cols);
% increase figure width for additional axes
fpos = get(gcf, 'position');
scrnsz = get(0, 'screensize');
fwidth = min([fpos(3)*cols, scrnsz(3)-20]);
fheight = fwidth/cols*.75; % maintain aspect ratio
set(gcf, 'position', [10 fpos(2) fwidth fheight])
% setup all axes
buf = .15/cols; % buffer between axes & between left edge of figure and axes
awidth = (1-buf*cols-.08/cols)/cols; % width of all axes
aidx = 1;
rowidx = 0;
while aidx <= length(y)
for i = 0:cols-1
if aidx+i <= length(y)
start = buf + buf*i + awidth*i;
apos{aidx+i} = [start 1-rowidx-.92 awidth .85];
a{aidx+i} = axes('position', apos{aidx+i});
end
end
rowidx = rowidx + 1; % increment row
aidx = aidx + cols; % increment index of axes
end
% make plots
axes(a{1}), plot(x,y{1}), title('sine'), xlabel('x'), ylabel('sin(x)')
axes(a{2}), plot(x,y{2}), title('cosine'), xlabel('x'), ylabel('cos(x)')
axes(a{3}), plot(x,y{3}), title('tangent'), xlabel('x'), ylabel('tan(x)')
axes(a{4}), plot(x,y{4}), title('x^2'), xlabel('x'), ylabel('x^2')
axes(a{5}), plot(x,y{5}), title('x^3'), xlabel('x'), ylabel('x^3')
% determine the position of the scrollbar & its limits
swidth = max([.03/cols, 16/scrnsz(3)]);
ypos = [1-swidth 0 swidth 1];
ymax = 0;
ymin = -1*(rows-1);
% build the callback that will be executed on scrolling
clbk = '';
for i = 1:length(a)
line = ['set(',num2str(a{i},'%.13f'),',''position'',[', ...
num2str(apos{i}(1)),' ',num2str(apos{i}(2)),'-get(gcbo,''value'') ', num2str(apos{i}(3)), ...
' ', num2str(apos{i}(4)),'])'];
if i ~= length(a)
line = [line,','];
end
clbk = [clbk,line];
end
% create the slider
uicontrol('style','slider', ...
'units','normalized','position',ypos, ...
'callback',clbk,'min',ymin,'max',ymax,'value',0);