使用matplotlib制作动态图

文章介绍了如何利用matplotlib的FuncAnimation类来制作动态图形,包括一步法和两步法。一步法直接在初始绘图基础上修改数据生成动态图片,而两步法则先生成一系列静态图片再组合成动态图。文章还详细讲解了FuncAnimation的主要参数和更新函数的定义方法。

一、简介

matplotlib(https://matplotlib.org/)是一个著名的python绘图库,由于其灵活强大的绘图功能使得在python中可视化变得非常容易,关于matplotlib的基础知识这里不再介绍,有疑问可以去官网翻Tutorialsexample学习。由于我们实际使用时常常是绘制静态图,忽略了matplotlib的动态图生成功能,同时matplotlib生成动态图的功能不是非常友善,因此大部分人在真的需要制作动态图时都会选择先用matplotlib生成一系列静态图片,然后再用其它相对比较容易使用的第三方python库生成动态图,如imageio(https://imageio.readthedocs.io/en/stable/#), 或者使用其它工具,如Matlab。这里打算简单介绍一下在matplotlib库中制作动态图的方法。

二、模块简介

matplotlib的animation模块提供了动态图制作功能,animation类提供了两个方法来生成动态图,即FuncAnimationArtistAnimation,这里我们使用FuncAnimation方法重复调用函数来生成图片。

1. FuncAnimation类介绍

FuncAnimation类的主要参数包括:

  • fig: 每一帧画面绘制使得Figure对象
  • func: 定义动画每一帧的更新函数,通常这一函数需要包含额外参数,此时可以用functools.partial来生成。
  • frames:可以是可迭代对象,整数,或者生成函数或者缺省。
  • init_func:初始化函数
  • inteval:每一帧画面的停留时间
  • repeat:当动态图中所有帧都播放完了之后是否重复播放
  • bilt:是否使用blitting来优化绘图

2. 定义动画更新函数

FunAnimation类中,更新函数在每一帧中都会被重新调用,通过在更新函数中更改一些绘图函数的数据,在每一帧我们就能得到不同的图片,然后FunAnimation的Writer(后端)将这些图片组合就能得到动态图片。关于更新函数的一些需要注意的地方是:

  • 如果设置了bilt == True,更新函数的最后就需要返回所有被修改或创建的Artists的引用变量
  • 生成函数的第一个传入参数必须是当前的帧数,其具体值可以通过frames参数定义,可以是可迭代类型或整数

三、使用matplotlib制作动画

1.一步法制作动态图片

由于matplotlib本身自带强大的绘图功能,因此我们可以不用生成图片,直接在初始绘图的基础上通过更新函数来修改绘图数据,一步直接生成动态图片,方便快捷,以下是代码:

import numpy as np
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
from functools import partial

### 绘制y=sin(2pi(x+t/t_0))*sin(2pi(t/t_0))
def getSinx_t(t=0, t_0=120, x_count=1e5):
    x = np.linspace(0.0, 1.0, int(x_count))
    y = np.sin(2.0*np.pi*(x + t/t_0))*np.sin(t/t_0*2.0*np.pi)
    return x, y


### 图片初始化
fig, ax = plt.subplots(dpi=100)
ax.set_aspect('auto')
ax.set_xlim((0.0, 1.0))
ax.set_ylim((-1.0, 1.0))
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title(r'$y=sin[2\pi(x+t/t_0)]*sin(2\pi t/t_0)$')


### 绘制初始曲线
x, y = getSinx_t()
y_up = y[np.where(y>0)]
x_up = x[np.where(y>0)]
x_dn = x[np.where(y<0)]
y_dn = y[np.where(y<0)]
plot_line = ax.plot(x, y)
plot_hline = ax.hlines(y=0.0, xmin=0.0, xmax=1.0, linestyles='dashed', colors='grey')
fill_xy_up = ax.fill_between(x=x_up, y1=y_up, y2=0, color='red', alpha=0.3)
fill_xy_dn = ax.fill_between(x=x_dn, y1=y_dn, y2=0, color='green', alpha=0.3)
plot_text = ax.text(x=0.8, y=0.75, s='t=0', fontsize=16, fontfamily='cursive')


### 定义动画更新函数
def UpdateFigure(num, f_plot_line, f_fill_xy_up, f_fill_xy_dn, f_plot_text):
    x_update, y_update = getSinx_t(t=num)
    f_plot_line[0].set_data(x_update, y_update)
    f_plot_text.set_text('t={}'.format(num))
    x_up = x_update[np.where(y_update>0)]
    y_up = y_update[np.where(y_update>0)]
    xy_up1 = np.column_stack((x_up, y_up))
    xy_up2 = np.column_stack((x_up[::-1], np.zeros(x_up.shape)))
    x_dn = x_update[np.where(y_update<0)]
    y_dn = y_update[np.where(y_update<0)]
    xy_dn1 = np.column_stack((x_dn, y_dn))
    xy_dn2 = np.column_stack((x_dn[::-1], np.zeros(x_dn.shape)))
    f_fill_xy_up.set_verts([np.vstack((xy_up1, xy_up2))])
    f_fill_xy_dn.set_verts([np.vstack((xy_dn1, xy_dn2))])
    return [f_plot_line[0], f_fill_xy_up, f_fill_xy_dn, f_plot_text]


### 创建FunAnimation对象
ani = FuncAnimation(fig, partial(
        UpdateFigure, 
        f_plot_line=plot_line, 
        f_fill_xy_up=fill_xy_up,
        f_fill_xy_dn=fill_xy_dn,
        f_plot_text=plot_text),
    np.arange(120),
    blit=True)


### 保存动态图片
ani.save('sinxt.gif', fps=60)  

以下为得到的动态图片:
在这里插入图片描述

2. 两步法制作动态图片

所谓两步法是指,首先用matplotlib生成一系列静态图片,然后结合matplotlib.image.imread读取图片功能和matplotlib.axes.Axes.imshow展示图片功能,来动态地更新图片,这种方法相比于上一种方法稍微复杂,但是这种方法灵活性更高,同时也可以用来组合一些非matplotlib生成的图片。以下为代码:

import matplotlib.pyplot as plt
import matplotlib.image as mimg
from matplotlib.animation import FuncAnimation
import numpy as np
from functools import partial
import os


### 绘制y=cos(2pi(x+t/t_0))*cos(2pi(t/t_0))
def getCosx_t(t=0, t_0=120, x_count=1e5):
    x = np.linspace(0.0, 1.0, int(x_count))
    y = np.cos(2.0*np.pi*(x + t/t_0))*np.cos(t/t_0*2.0*np.pi)
    return x, y


fig_count = 120             # 图片总数


### 定义生成所有图片的函数
def getFigrues(fig_count):
    try:
        os.mkdir('fig')
    except FileExistsError:
        print("Dir Exist!")
    for i in range(fig_count):
        fig, ax = plt.subplots(dpi=100)
        ax.set_aspect('auto')
        ax.set_xlim((0.0, 1.0))
        ax.set_ylim((-1.0, 1.0))
        ax.set_xlabel('x')
        ax.set_ylabel('y')
        ax.set_title(r'$y=cos[2\pi(x+t/t_0)]*cos(2\pi t/t_0)$')
        x, y = getCosx_t(t=i)
        y_up = y[np.where(y>0)]
        x_up = x[np.where(y>0)]
        x_dn = x[np.where(y<0)]
        y_dn = y[np.where(y<0)]
        ax.plot(x, y)
        ax.hlines(y=0.0, xmin=0.0, xmax=1.0, linestyles='dashed', colors='grey')
        ax.fill_between(x=x_up, y1=y_up, y2=0, color='red', alpha=0.3)
        ax.fill_between(x=x_dn, y1=y_dn, y2=0, color='green', alpha=0.3)
        ax.text(x=0.8, y=0.75, s='t={}'.format(i), fontsize=16, fontfamily='cursive')
        fig.show(False)
        fig.savefig('./fig/{}.jpg'.format(i))
        plt.close(fig)
getFigrues(fig_count) 


### 读取图片尺寸
def GetFigSize(fig_path='./fig/0.jpg'):
    now_img = mimg.imread(fname=fig_path)
    img_pxy = now_img.shape
    return img_pxy[1], img_pxy[0]


### 绘图初始化
img_px, img_py = GetFigSize()
img_dpi=100
fig, ax = plt.subplots(figsize=[img_px/img_dpi, img_py/img_dpi], dpi=img_dpi)
ax.set_aspect('equal')
ax.set_position([0.0, 0.0, 1.0, 1.0])
ax.set_axis_off()
plot_img = ax.imshow(X=np.zeros((img_py, img_px, 3)))


### 定义动画更新函数
def UpdateImages(num, f_plot_img):
    now_img_path = './fig/{}.jpg'.format(num)
    now_img = mimg.imread(fname=now_img_path)
    f_plot_img.set_data(now_img)
    return [f_plot_img]


### 创建FunAnimation对象
ani = FuncAnimation(
    fig, partial(UpdateImages, f_plot_img=plot_img),
    np.arange(fig_count),
    blit=True)


### 保存动态图片
ani.save('cosxt.gif', fps=60)  

得到的动态图片:
在这里插入图片描述

<think>好的,用户想用Matplotlib制作动态物理演示动画。我得先理清楚步骤和方法。 首先,Matplotlib动画模块应该是关键。我记得有FuncAnimation和ArtistAnimation两种主要方法,其中FuncAnimation更常用。需要导入animation模块。 然后,动态动画的基本流程是初始化图像,然后定义更新函数,每一帧更新数据,最后用FuncAnimation生成动画。比如,抛体运动可能需要更新位置坐标。 接下来,用户可能需要一个例子来理解。比如模拟简谐振动或者抛物线运动。简谐振动比较简单,适合示例。坐标随时间变化,可以用正弦函数。 然后要考虑绘图元素的更新。比如线对象或者散点对象,每次在update函数里用set_data方法更新坐标。 还要注意设置坐标轴范围,避免图像跳动。使用set_xlim和set_ylim固定范围。 保存动画的话,可能需要安装ffmpeg,使用anim.save,指定writer参数。但用户可能更关心实时显示,所以用plt.show()先展示。 另外,帧数和间隔时间也需要设置,interval参数控制速度,frames决定总帧数。 可能的问题包括动画不显示,这时候检查是否用了正确的后端,或者是否需要调用plt.show()。另外,性能问题,如果数据量大,可能需要降低帧数或优化代码。 总结步骤:导入模块,创建初始图,定义更新函数,创建动画对象,显示或保存。然后给出一个简谐振动的例子,再扩展一个抛物线运动的例子,展示不同的物理场景。 最后,相关问题应该涉及FuncAnimation参数、实时更新方法、保存格式、性能优化等,这样用户可以进一步学习。</think><< 使用Matplotlib制作动态物理演示动画主要通过`matplotlib.animation`模块实现。以下是完整实现方法和示例代码: 1. **核心工具 - FuncAnimation** ```python import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation import numpy as np # 初始化画布和坐标轴 fig, ax = plt.subplots() ax.set_xlim(0, 10) ax.set_ylim(-2, 2) line, = ax.plot([], [], lw=3) # 创建空线条对象 # 初始化函数 def init(): line.set_data([], []) return line, # 更新函数(核心动画逻辑) def update(frame): x = np.linspace(0, 10, 1000) y = np.sin(2 * np.pi * (x - 0.1 * frame)) # 波动方程 line.set_data(x, y) return line, # 创建动画对象 anim = FuncAnimation( fig, update, init_func=init, frames=200, # 总帧数 interval=20, # 帧间隔(ms) blit=True ) plt.show() ``` 2. **物理演示案例 - 抛物线运动** ```python fig, ax = plt.subplots() ax.set_xlim(0, 10) ax.set_ylim(0, 5) ball, = ax.plot([], [], 'o', markersize=20) def init(): ball.set_data([], []) return ball, def update(frame): t = frame * 0.1 v0 = 10 # 初速度 theta = np.pi/4 # 45度 g = 9.8 x = v0 * np.cos(theta) * t y = v0 * np.sin(theta) * t - 0.5 * g * t**2 ball.set_data(x, y) return ball, anim = FuncAnimation(fig, update, frames=100, init_func=init, blit=True) plt.show() ``` **原理说明:** - `FuncAnimation`通过反复调用update函数生成动画帧 - `frames`参数决定动画持续时间(总帧数= frames参数值) - `interval`控制帧率(1000/interval = 帧率,如20ms间隔对应50fps) - `blit=True`使用增量渲染优化性能 - 物理运动通过数学公式在update函数中实时计算坐标 **常见问题解决方案:** 1. 动画不显示:确保最后调用plt.show() 2. 图像闪烁:使用ax.clear()前保留背景元素 3. 性能优化:减少绘图元素数量,使用set_data代替重新绘制 **高级技巧:** - 多物体动画:在update中更新多个artist对象 - 实时轨迹绘制:保留历史数据在列表中 - 3D动画使用mplot3d工具包 - 交互控制:结合matplotlib事件系统
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值