Matlab 绘制动图
💡创建图形对象时,第一个输入参数传入目标坐标区对象(axes)的句柄ax
,使用变量保存返回的创建得到的图形对象的句柄,方便后续修改该对象的属性,如颜色、线型、XYZ数据。
方法一:更改属性
这种方法不用每次迭代创建新的图形对象,是官方推荐,在大规模动图绘制上性能更优。
fg = figure;
ax = axes(fg);
% 绘制第二个图形对象时,不会清除之前的图形对象,等同于hold on,相比hold on指向更加明确,针对的是句柄ax的坐标区
ax.NextPlot = 'add';
line1 = line(ax,0,0);
for t = 0:0.01:3
line1.XData = [line1.XData,t];
line1.YData = [line1.YData,sin(2*pi*t)];
pause(0.01);
end
或者
fg = figure;
ax = axes(fg);
% 绘制第二个图形对象时,不会清除之前的图形对象,等同于hold on,相比hold on指向更加明确,针对的是句柄ax的坐标区
ax.NextPlot = 'add';
line1 = line(ax,0,0);
for t = 0:0.01:3
line1.XData = 0:0.01:t;
line1.YData = sin(2*pi*line1.XData);
pause(0.01);
end
- 如果动画速度慢,可以调低
pause
的延时;如果还是慢,可以进行下采样0:0.05:3
。 line
等同于plot
,plot
函数创建的也是Line对象,不如直接调用line
更加直观。line
也可以传入xyz三个列向量绘制三维曲线,等同于plot3
。- 除了Line,其余常用的图形对象
进一步,想要设置坐标区和图形对象的一些属性。
fg = figure;
ax = axes(fg);
ax.NextPlot = 'add';
ax.XLim = [0;3]; % 坐标轴的范围
ax.YLim = [-1;1];
ax.DataAspectRatio = [1;1;1]; % 如果设置XYZ轴等比例,则坐标区实际长度的比例等于范围比
ax.XLabel = xlabel('Time(s)');
ax.YLabel = ylabel('fcn');
line1 = line(ax,0,0);
line1.Color = 'r';
line2 = line(ax,0,1,'Color',[112,222,0]/255);
ax_legend = legend(ax,[line1,line2],{'sin','cos'},'AutoUpdate','off'); % 关闭自动更新
for t = 0:0.01:3
line1.XData = 0:0.01:t;
line1.YData = sin(2*pi*line1.XData);
line2.XData = 0:0.01:t;
line2.YData = cos(2*pi*line2.XData);
pause(0.01);
end
进一步,绘制 3D 动图
fg = figure;
ax = axes(fg);
ax.NextPlot = 'add';
ax.XLim = [-1;1]; % 坐标轴的范围
ax.YLim = [-1;1];
ax.ZLim = [0;3];
ax.DataAspectRatio = [1;1;1]; % 如果设置XYZ轴等比例,则坐标区实际长度的比例等于范围比
ax.XLabel = xlabel('Time(s)');
ax.YLabel = ylabel('fcn');
view(3);
line1 = line(ax,0,0,0);
line1.Color = 'r';
line2 = line(ax,0,1,0,'Color',[112,222,0]/255);
ax_legend = legend(ax,[line1,line2],{'cur1','cur2'},'AutoUpdate','off'); % 关闭自动更新
for t = 0:0.01:3
line1.XData = cos(2*pi*(0:0.01:t)');
line1.YData = sin(2*pi*(0:0.01:t)');
line1.ZData = (0:0.01:t)';
line2.XData = sin(2*pi*(0:0.01:t)');
line2.YData = cos(2*pi*(0:0.01:t)');
line2.ZData = (0:0.01:t)';
pause(0.01);
end
方法二:重新绘制
这种方法实现简单,但是重复创建和清除图形对象与更新图例,性能开销大。
fg = figure;
ax = axes(fg);
ax.NextPlot = 'add';
ax.XLim = [0;3]; % 坐标轴的范围
ax.YLim = [-1;1];
ax.DataAspectRatio = [1;1;1]; % 如果设置XYZ轴等比例,则坐标区实际长度的比例等于范围比
ax.XLabel = xlabel('Time(s)');
ax.YLabel = ylabel('fcn');
for t = 0:0.01:3
cla(ax); % 清空上一次绘制
plot((0:0.01:t)',sin(2*pi*(0:0.01:t)'),'--g', ...
(0:0.01:t)',cos(2*pi*(0:0.01:t)'),':r');
legend(ax,{'sin','cos'},'AutoUpdate','off'); % 关闭自动更新
pause(0.01);
end
方法三:动画线条
用 Matlab 的动画线条,貌似只能画线,灵活性不高。但是是 Matlab 自带功能,可能性能更好(未测试)。
figure
a1 = animatedline('Color',[0 .7 .7]);
a2 = animatedline('Color',[0 .5 .5]);
axis([0 20 -1 1])
x = linspace(0,20,10000);
for k = 1:length(x)
% first line
xk = x(k);
ysin = sin(xk);
addpoints(a1,xk,ysin);
% second line
ycos = cos(xk);
addpoints(a2,xk,ycos);
% update screen
drawnow limitrate
pause(0.001)
end
方法四:创建影片与回放
复杂的动图,Matlab 推荐将其制作影片后再播放。
💡官方给出的建议
-
动画技术
您可以使用三种基本方法在 MATLAB 中创建动画:
- 更新图形对象的属性并在屏幕上显示更新。这一技术对于在图形大部分保持不变的情况下创建动画非常有用。例如,重复设置
XData
和YData
属性以在图形中移动对象。 - 将变换应用于对象。当您想要同时对一组对象的位置和方向进行操作时,此技术非常有用。将一些对象归组为某一变换对象的子级。使用
hgtransform
创建变换对象。设置变换对象的Matrix
属性,调整其所有子级的位置。 - 创建一个影片。如果您有一个复杂的动画无法实时快速绘制,或想要存储动画以回放时,影片就非常有用。使用
getframe
和movie
函数创建一个影片。
- 更新图形对象的属性并在屏幕上显示更新。这一技术对于在图形大部分保持不变的情况下创建动画非常有用。例如,重复设置
-
更新屏幕
在某些情况下,直到代码完成后 MATLAB 才会更新屏幕。使用
drawnow
命令中的一个命令,在动画过程中显示整个屏幕的更新。 -
优化性能
要优化性能,可考虑以下技术:
- 使用
animatedline
函数创建流数据的线条动画。 - 更新现有对象的属性,不创建新的图形对象。
- 设置坐标轴范围(
XLim
、YLim
、ZLim
)或将与之关联的模式属性改为手动模式(XLimMode
、YLimMode
、ZLimMode
),以避免 MATLAB 在每次屏幕更新时重新计算值。当您设置坐标轴范围时,与之关联的模式属性会更改成手动模式。 - 尽量避免在循环中创建图例或其他注释。可以在循环之后添加注释。
有关优化性能的详细信息,请参阅 图形性能。
- 使用
运行下面程序,可以看到动图播放了两次,第一次是更新数据,捕获关键帧,保存到结构体数组 F 中,保存时会导致一定延时,所以看到了类似使用pause
的效果;第二次是在坐标区的位置上,播放 F 中保存的帧数据,实际坐标区中的曲线还在,只是在 axes 上方覆盖了影片。
fg = figure;
ax = axes(fg);
ax.NextPlot = 'add';
ax.XLim = [0;3]; % 坐标轴的范围
ax.YLim = [-1;1];
ax.DataAspectRatio = [1;1;1]; % 如果设置XYZ轴等比例,则坐标区实际长度的比例等于范围比
ax.XLabel = xlabel('Time(s)');
ax.YLabel = ylabel('fcn');
line1 = line(ax,0,0);
line1.Color = 'r';
line2 = line(ax,0,1,'Color',[112,222,0]/255);
ax_legend = legend(ax,[line1,line2],{'sin','cos'},'AutoUpdate','off'); % 关闭自动更新
loops = length(0:0.01:3);
F(loops) = struct('cdata',[],'colormap',[]);
ti = 1;
for t = 0:0.01:3
line1.XData = 0:0.01:t;
line1.YData = sin(2*pi*line1.XData);
line2.XData = 0:0.01:t;
line2.YData = cos(2*pi*line2.XData);
drawnow
F(ti) = getframe(gcf);
ti = ti + 1;
end
n = 1; % 播放1次
m = 100; % 播放时每秒帧数,对应0.01。
% 绘制在句柄fg对应的figure上
movie(fg,F,1,100)
动图保存 GIF 文件
循环前设置 pic_num,file_name 和 delay_time,其中 delay_time 可以调整 gif 每帧间隔秒数,最好与时间曲线的采样时间相同,也不易过小。使用 drawnow 强制图形刷新之后,将每帧保存到 gif 文件中。
fg = figure;
ax = axes(fg);
ax.NextPlot = 'add';
ax.XLim = [0;3]; % 坐标轴的范围
ax.YLim = [-1;1];
ax.DataAspectRatio = [1;1;1]; % 如果设置XYZ轴等比例,则坐标区实际长度的比例等于范围比
ax.XLabel = xlabel('Time(s)');
ax.YLabel = ylabel('fcn');
line1 = line(ax,0,0);
line1.Color = 'r';
line2 = line(ax,0,1,'Color',[112,222,0]/255);
ax_legend = legend(ax,[line1,line2],{'sin','cos'},'AutoUpdate','off'); % 关闭自动更新
% 1.初始化
pic_num = 1;
delay_time = 0.02; % 每帧间隔秒数
file_name = 'myGIF.gif'; % 保存文件名
for t = 0:0.02:3
line1.XData = 0:0.02:t;
line1.YData = sin(2*pi*line1.XData);
line2.XData = 0:0.02:t;
line2.YData = cos(2*pi*line2.XData);
drawnow % 2.强制图形更新
% 3.保存gif文件
F=getframe(gcf);
I=frame2im(F);
[I,map]=rgb2ind(I,256);
if pic_num == 1
imwrite(I,map,file_name,'gif', 'Loopcount',inf,'DelayTime',delay_time);
else
imwrite(I,map,file_name,'gif','WriteMode','append','DelayTime',delay_time);
end
pic_num = pic_num + 1;
end