Jupyter Notebook中无法在循环中更新Matplotlib绘图(保留历史轨迹),也无法用animation子函数实现动画的问题
Jupyter Notebook中无法在循环中更新Matplotlib绘图(保留历史轨迹),也无法用animation子函数实现动画的问题
嘿,我看你在做粒子轨道模拟的时候卡在绘图更新这一步了——循环里得等跑完才能看到结果,用animation模块也没搞定是吧?先把你给出的代码整理好(看起来有点截断,我先补全关键逻辑),然后咱们一步步解决问题~
首先先看你现有的代码框架(补全了截断的受力计算部分):
# Import necessary Python libraries import matplotlib.pyplot as plt import numpy as np %matplotlib notebook from matplotlib import animation # Define constants and initialize variables: # All quantities are directly in SI units # (To begin, I will take the Earth-Moon system as a reference) G = 6.67e-11 R_12 = 384399 * 1000 + 3474 / 2 * 1000 + 6371 * 1000 # Attracting particle 1 (P1), kept fixed. m1 = 5.97e24 x1 = 0; y1 = 0 # Attracted particle 2 (P2), moving around the fixed particle. m2 = 7.35e22 x2_0 = x1 + R_12; y2_0 = y1 + 0 Vx2_0 = 0; Vy2_0 = 1000 # Simulation parameters dt = 3600 # Time step N = 24 * 30 # Number of iterations # 补全你截断的受力计算逻辑 # 这里直接把计算逻辑写在循环里更直观
问题原因分析
- 循环里不更新绘图:Jupyter的Matplotlib默认是静态渲染,直接在循环里画完不触发刷新,就只能等整个循环结束才会一次性显示。而且如果每次循环都重新调用
plt.plot(),会叠加很多图层,既卡又乱。 - animation模块用不对:可能是没正确定义初始化函数和帧更新函数,或者没设置
blit=True优化渲染,再或者notebook的渲染模式没匹配上。
解决方案1:循环实时更新并保留轨迹
这种方法适合你想边跑模拟边看轨迹变化的场景,核心是复用同一个绘图对象,每次更新数据后手动刷新画布:
# 导入库(保持你的配置) import matplotlib.pyplot as plt import numpy as np %matplotlib notebook from matplotlib import animation # 常量和初始化参数(和你的一致) G = 6.67e-11 R_12 = 384399 * 1000 + 3474 / 2 * 1000 + 6371 * 1000 m1 = 5.97e24 x1 = 0; y1 = 0 m2 = 7.35e22 x2_0 = x1 + R_12; y2_0 = y1 + 0 Vx2_0 = 0; Vy2_0 = 1000 dt = 3600 N = 24 * 30 # 初始化绘图窗口,设置等比例坐标轴(不然轨道会变形) fig, ax = plt.subplots() ax.set_aspect('equal') ax.set_xlim(-R_12*1.2, R_12*1.2) # 留一点边距 ax.set_ylim(-R_12*1.2, R_12*1.2) # 初始化绘图对象:轨迹线、当前粒子点、固定粒子 trajectory, = ax.plot([], [], 'b-', alpha=0.5) # 半透明蓝色轨迹 current_particle, = ax.plot([], [], 'ro', markersize=5) # 红色当前点 ax.plot(x1, y1, 'go', markersize=10, label='P1 (Earth)') # 绿色固定粒子 ax.legend() # 存储轨迹的数组,初始值是粒子初始位置 x2_history = [x2_0] y2_history = [y2_0] vx2, vy2 = Vx2_0, Vy2_0 # 开始循环模拟 for i in range(N): # 计算粒子P2的加速度(万有引力公式) r = np.sqrt((x2_history[-1] - x1)**2 + (y2_history[-1] - y1)**2) ax_x = -G * m1 * (x2_history[-1] - x1) / r**3 ax_y = -G * m1 * (y2_history[-1] - y1) / r**3 # 更新速度和位置 vx2 += ax_x * dt vy2 += ax_y * dt new_x = x2_history[-1] + vx2 * dt new_y = y2_history[-1] + vy2 * dt # 追加到轨迹数组 x2_history.append(new_x) y2_history.append(new_y) # 更新绘图对象的数据 trajectory.set_data(x2_history, y2_history) current_particle.set_data(new_x, new_y) # 刷新画布并暂停一小段时间(让渲染跟上) fig.canvas.draw() plt.pause(0.01) # 数值越小,动画越快,根据需要调整
解决方案2:用Matplotlib Animation模块实现更稳定的动画
如果需要更流畅的动画或者想保存动画文件,用FuncAnimation是更好的选择。建议先预计算所有轨迹数据,再逐帧渲染:
# 导入库 import matplotlib.pyplot as plt import numpy as np %matplotlib notebook from matplotlib import animation # 常量和初始化参数(和你的一致) G = 6.67e-11 R_12 = 384399 * 1000 + 3474 / 2 * 1000 + 6371 * 1000 m1 = 5.97e24 x1 = 0; y1 = 0 m2 = 7.35e22 x2_0 = x1 + R_12; y2_0 = y1 + 0 Vx2_0 = 0; Vy2_0 = 1000 dt = 3600 N = 24 * 30 # 先预计算所有轨迹数据 x2_history = [x2_0] y2_history = [y2_0] vx2, vy2 = Vx2_0, Vy2_0 for i in range(N): r = np.sqrt((x2_history[-1] - x1)**2 + (y2_history[-1] - y1)**2) ax_x = -G * m1 * (x2_history[-1] - x1) / r**3 ax_y = -G * m1 * (y2_history[-1] - y1) / r**3 vx2 += ax_x * dt vy2 += ax_y * dt new_x = x2_history[-1] + vx2 * dt new_y = y2_history[-1] + vy2 * dt x2_history.append(new_x) y2_history.append(new_y) # 初始化绘图窗口 fig, ax = plt.subplots() ax.set_aspect('equal') ax.set_xlim(-R_12*1.2, R_12*1.2) ax.set_ylim(-R_12*1.2, R_12*1.2) trajectory, = ax.plot([], [], 'b-', alpha=0.5) current_particle, = ax.plot([], [], 'ro', markersize=5) ax.plot(x1, y1, 'go', markersize=10, label='P1 (Earth)') ax.legend() # 初始化函数:设置初始空数据 def init(): trajectory.set_data([], []) current_particle.set_data([], []) return trajectory, current_particle # 帧更新函数:每帧渲染到当前帧的轨迹和粒子位置 def update(frame): trajectory.set_data(x2_history[:frame+1], y2_history[:frame+1]) current_particle.set_data(x2_history[frame], y2_history[frame]) return trajectory, current_particle # 创建动画:frames是总帧数,interval是每帧间隔毫秒,blit=True优化渲染 anim = animation.FuncAnimation( fig, update, init_func=init, frames=N+1, interval=50, blit=True ) # 如果需要保存动画,取消下面的注释(需要安装ffmpeg或pillow) # anim.save('moon_orbit.mp4', writer='ffmpeg', fps=20)
几个关键注意事项
- 确保
%matplotlib notebook(或新版的%matplotlib widget)是在导入Matplotlib之前运行的,不然渲染模式不会生效。 - 循环更新时,
plt.pause()是必须的——它会触发画布刷新,没有这一步的话,绘图窗口会一直卡住。 - 用
FuncAnimation时,blit=True会只重绘变化的部分,动画会更流畅;如果显示有问题,可以试试把blit改成False。 - 如果你用的是新版JupyterLab,推荐用
%matplotlib widget替代%matplotlib notebook,交互体验更好。
备注:内容来源于stack exchange,提问作者MyDePain
相关产品推荐
相关产品推荐

