第一堂课布置了一个Mission Impossible作业,要求学生们用Matlab制作一个动画,模拟小球的自由落体运动。
以下将整个任务的问题解决的过程分享如下:
步骤一,这是一个动画的制作过程,以 “Matlab” + "动画"为关键词先问一声度娘,得到线索“怎样用Matlab做动画演示”。
这个资料里有提到一种制作动画的方法getframe,每画一幅图片就getframe一次赋给一个变量,比如说M,最后的时候用movie(M,1)播放这个动画,或者用movie2avi函数将动画保存成一个avi文件。
步骤二,这时候问题转移到,如何制作每一帧图片。我们需要画一个画布,同时在画布上生成一个坐标对象,并且借助坐标把一个实心或者空心圆画上去。以 “Matlab” + "画图"为关键词再度娘,得到两个线索,第一个是Figure的机制,第二个是类似火星十一郎-张朋飞写的“Matlab绘图”,通过这些线索可以了解画图一般都会先设置一个Figure,然后在规定好X轴和Y轴的axis的指定位置上plot一个实心或者空心圆。
很显然,这一步是非常难的,因为大部分分享Matlab信息的网友很难从一个Matlab初学者的角度来思考,经常一上来就大谈特谈plot函数,很少有人会一开始就讲Figure机制(我们是需要一个画布的呀!),在Matlab里边作图需要借助特定的对象axis,然后才是plot函数的使用。
代码写出来会很简单:
figure;
axis([0 20 0 20]);
plot(10, 10, '-or');%坐标轴X为10,和Y为10的位置,画一个实线的(用'-'来表示)空心圆(用'o'来表示),颜色为红色(用'r'来表示)
这个坐标轴是动态的,需要借助一个set函数,将当前的axis的坐标轴属性固定住,代码如下:
set(gca,'nextplot','replacechildren','box','off','color','w','xgrid','off')
步骤三,掌握了画单个图片的方法,接下来就是要画自由落体的每一帧的图片了。这里的X可以固定起来,设为定值10;Y一开始是在最高的位置,设为H0 = 20,当球开始下落的时候这个Y值是随着时间t的变化而变化的,变化的速度又和加速度a有关,它具体指向自由落体的小球的高度:currentHeight = H0 - 0.5 * a * t * t。这部分是高中物理知识,假定不考虑空气摩擦力的话,这里的加速度就可以用重力加速度g来表示,一般可以简单设为定值10。
这样,整个程序就写出来了:
% FileName is :: movieExample.m
% clear
clc; clear; close all;
figure('toolbar','none','menubar','none','NumberTitle','off','name','自由落体动画录制');
axis([0 20 0 20])
set(gca,'nextplot','replacechildren','box','off','color','w','xgrid','off')
%initialize, based on about 2s movie, 20 frame/s
tEnd = 2;
frameN_s = 20;
nFrame = tEnd * frameN_s;
zyltMovie = moviein(nFrame, gcf);%初始化一个zyltMovie的矩阵
g = 10;
v0 = 0;
t0 = 0;
dt = 0.05;
h0 = 20;
% LOOP
for iFrame = 1:nFrame
t = t0 + dt * iFrame;
v = v0 + g * t;
currentHeight = h0 - 1/2 * v * t;
plot(10, currentHeight, '-or');
zyltMovie(:,iFrame) = getframe(gcf);
end
movie2avi(zyltMovie, 'AnimateEllipse.avi', 'compression', 'None');
如果能够让小球触地后反弹,动画会更生动有趣。在算法上,它只是涉及到对高中物理知识的运用,(1)当小球下落的时候,当高度currentH < 0 的时候,说明小球已经触地,需要将作用于小球的加速度反向,a = -g,同时小球的速度也要重新初始化,比如设 v0 = -0.67v;(2)当小球上升的时候,当速度 v > 0 的时候,说明小球已经到达最高点并开始下落,这个时候加速度再次变成 a = g。
修改过的版本如下:
% FileName is :: movieExample.m
% clear
clc; clear; close all;
figure('toolbar','none','menubar','none','NumberTitle','off','name','自由落体动画录制');
axis equal;
axis([0 20 0 20]);
set(gca,'nextplot','replacechildren','box','off','color','w','xgrid','off')
%initialize, based on about 8s movie, 20 frame/s
tEnd = 10;
frameN_s = 20;
nFrame = tEnd * frameN_s;
zyltMovie = moviein(nFrame, gcf);
g = 10;
a = g;
dt = 0.05;
v0 = 0;
t0 = 0;
h0 = 20;
Flag_Down = 1;
Flag_Up = 0;
currentFlag = Flag_Down;
for iFrame = 1:nFrame
if currentFlag == Flag_Down
t = dt * iFrame - t0;
v = v0 + a * t;
currentH = h0 - 0.5 * v * t;
if currentH < 0
plot(10, currentH, '.r', 'markersize', 50);
zyltMovie(:,iFrame) = getframe(gcf);
currentFlag = Flag_Up;
a = -g;
t0 = dt * iFrame;
v0 = -0.67 * v;
continue;
else
plot(10, currentH, '.r', 'markersize', 50);
zyltMovie(:,iFrame) = getframe(gcf);
end
end
if currentFlag == Flag_Up
t = dt * iFrame - t0;
v = v0 - a * t;
currentH = -v0 * t + 0.5 * a * t * t;
if v > 0
plot(10, currentH, '.r', 'markersize', 50);
zyltMovie(:,iFrame) = getframe(gcf);
currentFlag = Flag_Down;
a = g;
t0 = dt * iFrame;
v0 = 0;
h0 = currentH;
continue;
else
plot(10, currentH,'.r', 'markersize', 50);
zyltMovie(:,iFrame) = getframe(gcf);
end
end
end
movie2avi(zyltMovie, 'AnimateEllipse.avi', 'compression', 'None');