Quantcast
Viewing all articles
Browse latest Browse all 14040

Updating animation with slider

I'm currently developing an animation featuring a double pendulum, and I'd like to incorporate sliders to enhance user interaction. These sliders are designed to control the two initial angles of the animation. However, I've encountered an issue: when I update the sliders, the animation restarts with the same initial values, despite the slider values being correctly reflected in the updated plot (verified through a print statement). I suspect there might be an issue with how I'm updating the animation.

To animate the double pendulum, I use this code:

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]

The initial plot is made using this:

L1 = 1.0  # Length of pendulum 1L2 = 1.0  # Length of pendulum 2m1 = 1.0  # Mass of pendulum 1m2 = 1.0  # Mass of pendulum 2g = 9.81  # Gravitational acceleration# Initial conditionstheta1_initial = np.radians(60.0)theta2_initial = np.radians(5.0)# Time span and number of points for simulationt_span = (0, 20)num_points = 1000t, y = simulate_double_pendulum(theta1_initial, theta2_initial, L1, L2, m1, m2, g, t_span, num_points)# Set up the initial plot with pendulum rods, masses, and tracefig7, 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 tracelines = [line1, line2]points = [point1, point2]s_theta1, s_theta2 = get_sliders_double_pendulum()# Animation parametersinterval = t_span[1] / num_points / 2 * 100  # milliseconds per frameanimation = FuncAnimation(    fig7, animate_double_pendulum, frames=num_points, fargs=(t, y, L1, L2, lines, points, trace),    interval=interval, repeat=True)plt.show()

And the code that tracks the sliders and the changes regarding those is the following:

def get_sliders_double_pendulum():    s_theta1 = widgets.FloatSlider(        description=r"$\theta_1$",        min=0.0, max=2*np.pi,        step=0.01,        value=theta1_initial,        continuous_update=False)    s_theta2 = widgets.FloatSlider(        description=r"$\theta_2$",        min=0.0, max=2*np.pi,        step=0.01,        value=theta2_initial,        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_theta2def 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):    animation.event_source.stop()    clear_double_pendulum(lines, points, trace)    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 $\theta_1$'+f'={np.round(theta1, 2)}, '+r'$\theta_2$='+f'{np.round(theta2, 2)}', xlabel='X', ylabel='Y')    # Update the animation data    animation = FuncAnimation(        fig7, animate_double_pendulum, frames=num_points, fargs=(t, y, L1, L2, lines, points, trace),        interval=interval, repeat=True    )    animation.frame_seq = animation.new_frame_seq()    animation._stop = False    # Restart the animation    animation.event_source.start()

In summary, my goal is for the animation to restart and showcase a new sequence when the sliders are adjusted. I aim to have the animation reflect the updated values of time (t) and position/angular velocity (y). Any help would be greatly appreciated!

Edit: To copy and paste the code to run it:

%matplotlib notebookimport numpy as npimport matplotlib.pyplot as pltimport sympy as spfrom scipy.integrate import solve_ivpfrom matplotlib.animation import FuncAnimationfrom ipywidgets import HBox, VBox, Layout, widgetsfrom IPython.display import displayfrom sympy import symbols, solve, Matrixfrom matplotlib import colorsfrom matplotlib.patches import FancyArrowPatchimport warningswarnings.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.ydef 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"$\theta_1$",        min=0.0, max=2*np.pi,        step=0.01,        value=theta1,        continuous_update=False)    s_theta2 = widgets.FloatSlider(        description=r"$\theta_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_theta2def 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):    animation.event_source.stop()    clear_double_pendulum(lines, points, trace)    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 $\theta_1$'+f'={np.round(theta1, 2)}, '+r'$\theta_2$='+f'{np.round(theta2, 2)}', xlabel='X', ylabel='Y')    # Update the animation data    animation = FuncAnimation(        fig7, animate_double_pendulum, frames=num_points, fargs=(t, y, L1, L2, lines, points, trace),        interval=interval, repeat=True    )    animation.frame_seq = animation.new_frame_seq()    animation._stop = False    # Restart the animation    animation.event_source.start()L1 = 1.0  # Length of pendulum 1L2 = 1.0  # Length of pendulum 2m1 = 1.0  # Mass of pendulum 1m2 = 1.0  # Mass of pendulum 2g = 9.81  # Gravitational acceleration# Initial conditionstheta1 = np.radians(100.0)theta2 = np.radians(45.0)# Time span and number of points for simulationt_span = (0, 20)num_points = 1000t, 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 tracefig7, 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 tracelines = [line1, line2]points = [point1, point2]s_theta1, s_theta2 = get_sliders_double_pendulum()# Animation parametersinterval = t_span[1] / num_points / 2 * 100  # milliseconds per frameanimation = FuncAnimation(    fig7, animate_double_pendulum, frames=num_points, fargs=(t, y, L1, L2, lines, points, trace),    interval=interval, repeat=True)plt.show()

This is part of a bigger jupyter notebook, so if not all imports are used in this code, that's why.


Viewing all articles
Browse latest Browse all 14040

Trending Articles