双摆动画滑块更新异常:调整滑块后未加载新初始角度
双摆动画滑块交互问题:调整滑块后动画仍使用初始值重启
我在开发双摆动画时,添加了滑块控件用于控制两个初始角度,希望调整滑块后动画能基于新角度重新模拟并播放,但目前调整滑块后,尽管打印验证滑块值已正确传递,动画还是用原来的初始值重启,核心问题出在动画更新逻辑上。
问题根源
在update_double_pendulum函数中,新创建的animation是局部变量,没有替换全局的animation对象。原来的全局animation仍然持有旧的t和y模拟数据,所以重启后还是播放旧的动画序列。此外,直接修改_stop这类私有属性不符合规范,新动画创建后没有正确替换原实例,导致旧动画的事件源仍在运行。
修复方案
- 替换全局动画实例:在
update_double_pendulum中,使用global关键字声明要修改全局的animation变量,将新创建的动画赋值给它。 - 正确清理旧动画:停止旧动画后,断开其事件源的回调,避免残留的回调干扰新动画。
- 重置轨迹数据:每次重启动画时,将trace的xdata和ydata重置为空数组,避免旧轨迹残留。
修改后的关键代码
调整update_double_pendulum函数
def update_double_pendulum(theta1, theta2, lines, points, trace, animation): global animation # 声明要修改全局变量 animation.event_source.stop() # 断开旧动画的事件源回调,避免残留 animation.event_source.remove_callback(animation._step) clear_double_pendulum(lines, points, trace) # 重置trace的实际数据存储,避免旧轨迹残留 trace.set_xdata([]) trace.set_ydata([]) t, y = simulate_double_pendulum(theta1, theta2, L1, L2, m1, m2, g, t_span, num_points) ax7.set(title=r'Double pendulum with starting position $θ_1$'+f'={np.round(theta1, 2)}, ' +r'$θ_2$='+f'{np.round(theta2, 2)}', xlabel='X', ylabel='Y') # 创建新动画并替换全局实例 animation = FuncAnimation( fig7, animate_double_pendulum, frames=num_points, fargs=(t, y, L1, L2, lines, points, trace), interval=interval, repeat=True ) # 启动新动画 animation.event_source.start()
调整clear_double_pendulum函数(可选优化)
def clear_double_pendulum(lines, points, trace): lines[0].set_data([], []) lines[1].set_data([], []) points[0].set_data([], []) points[1].set_data([], []) # 配合trace数据重置,清空显示 trace.set_data([], [])
完整可运行代码
%matplotlib notebook import numpy as np import matplotlib.pyplot as plt import sympy as sp from scipy.integrate import solve_ivp from matplotlib.animation import FuncAnimation from ipywidgets import HBox, VBox, Layout, widgets from IPython.display import display from sympy import symbols, solve, Matrix from matplotlib import colors from matplotlib.patches import FancyArrowPatch import warnings warnings.filterwarnings("ignore") def double_pendulum(t, y, L1, L2, m1, m2, g): theta1, omega1, theta2, omega2 = y c = np.cos(theta1 - theta2) s = np.sin(theta1 - theta2) # Equations of motion for the double pendulum num1 = -g * (2 * m1 + m2) * np.sin(theta1) num2 = -m2 * g * np.sin(theta1 - 2 * theta2) num3 = -2 * s * m2 * (omega2 ** 2 * L2 + omega1 ** 2 * L1 * c) den = L1 * (2 * m1 + m2 - m2 * np.cos(2 * theta1 - 2 * theta2)) theta1_prime = (num1 + num2 + num3) / den num1 = 2 * s * (omega1 ** 2 * L1 * (m1 + m2) + g * (m1 + m2) * np.cos(theta1) + omega2 ** 2 * L2 * m2 * c) den = L2 * (2 * m1 + m2 - m2 * np.cos(2 * theta1 - 2 * theta2)) theta2_prime = num1 / den return [omega1, theta1_prime, omega2, theta2_prime] def simulate_double_pendulum(theta1, theta2, L1, L2, m1, m2, g, t_span, num_points, omega1=0, omega2=0): y0 = [theta1, omega1, theta2, omega2] sol = solve_ivp( fun=lambda t, y: double_pendulum(t, y, L1, L2, m1, m2, g), t_span=t_span, y0=y0, method='RK45', t_eval=np.linspace(t_span[0], t_span[1], num_points) ) return sol.t, sol.y def animate_double_pendulum(i, t, y, L1, L2, lines, points, trace): theta1, _, theta2, _ = y[:, i] x1 = L1 * np.sin(theta1) y1 = -L1 * np.cos(theta1) x2 = x1 + L2 * np.sin(theta2) y2 = y1 - L2 * np.cos(theta2) # Update the positions of the pendulum rods lines[0].set_data([0, x1], [0, y1]) lines[1].set_data([x1, x2], [y1, y2]) # Update the positions of the pendulum masses (points) points[0].set_data(x1, y1) points[1].set_data(x2, y2) # Update the trace of the endpoint trace.set_data(np.append(trace.get_xdata(), x2), np.append(trace.get_ydata(), y2)) return lines + points + [trace] def get_sliders_double_pendulum(): s_theta1 = widgets.FloatSlider( description=r"$θ_1$", min=0.0, max=2*np.pi, step=0.01, value=theta1, continuous_update=False) s_theta2 = widgets.FloatSlider( description=r"$θ_2$", min=0.0, max=2*np.pi, step=0.01, value=theta2, continuous_update=False) s_theta1.observe(on_theta_slider_change, names='value') s_theta2.observe(on_theta_slider_change, names='value') display(s_theta1, s_theta2) return s_theta1, s_theta2 def on_theta_slider_change(change): theta1 = s_theta1.value theta2 = s_theta2.value update_double_pendulum(theta1, theta2, lines, points, trace, animation) def clear_double_pendulum(lines, points, trace): lines[0].set_data([], []) lines[1].set_data([], []) points[0].set_data([], []) points[1].set_data([], []) trace.set_data([], []) def update_double_pendulum(theta1, theta2, lines, points, trace, animation): global animation animation.event_source.stop() animation.event_source.remove_callback(animation._step) clear_double_pendulum(lines, points, trace) trace.set_xdata([]) trace.set_ydata([]) t, y = simulate_double_pendulum(theta1, theta2, L1, L2, m1, m2, g, t_span, num_points) ax7.set(title=r'Double pendulum with starting position $θ_1$'+f'={np.round(theta1, 2)}, ' +r'$θ_2$='+f'{np.round(theta2, 2)}', xlabel='X', ylabel='Y') animation = FuncAnimation( fig7, animate_double_pendulum, frames=num_points, fargs=(t, y, L1, L2, lines, points, trace), interval=interval, repeat=True ) animation.event_source.start() L1 = 1.0 # Length of pendulum 1 L2 = 1.0 # Length of pendulum 2 m1 = 1.0 # Mass of pendulum 1 m2 = 1.0 # Mass of pendulum 2 g = 9.81 # Gravitational acceleration # Initial conditions theta1 = np.radians(100.0) theta2 = np.radians(45.0) # Time span and number of points for simulation t_span = (0, 20) num_points = 1000 t, y = simulate_double_pendulum(theta1, theta2, L1, L2, m1, m2, g, t_span, num_points) # Set up the initial plot with pendulum rods, masses, and trace fig7, ax7 = plt.subplots(figsize=(6, 6)) line1, = ax7.plot([], [], lw=2, color='b') line2, = ax7.plot([], [], lw=2, color='r') point1, = ax7.plot([], [], 'bo', label='Mass 1') point2, = ax7.plot([], [], 'ro', label='Mass 2') trace, = ax7.plot([], [], lw=1, color='gray', label='Trace') ax7.legend() ax7.set_xlim(-2, 2) ax7.set_ylim(-2, 2) # Create the initial lines, points, and trace lines = [line1, line2] points = [point1, point2] s_theta1, s_theta2 = get_sliders_double_pendulum() # Animation parameters interval = t_span[1] / num_points / 2 * 100 # milliseconds per frame animation = FuncAnimation( fig7, animate_double_pendulum, frames=num_points, fargs=(t, y, L1, L2, lines, points, trace), interval=interval, repeat=True ) plt.show()
内容的提问来源于stack exchange,提问作者W.K.
相关产品推荐
相关产品推荐

