import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
import plotly.graph_objects as go
import numpy as np
from scipy.integrate import solve_ivp
# Initialize the Dash app
app = dash.Dash(__name__)
# Physical constants
g = 9.81 # Acceleration due to gravity
L1 = 1.0 # Length of first pendulum
L2 = 1.0 # Length of second pendulum
m1 = 1.0 # Mass of first pendulum
m2 = 1.0 # Mass of second pendulum
# Initial conditions: [theta1, omega1, theta2, omega2]
initial_conditions = [np.pi / 4, 0, np.pi / 2, 0]
# Time array
t = np.linspace(0, 20, 800)
# Differential equations of motion
def equations(t, y):
theta1, omega1, theta2, omega2 = y
delta = theta2 - theta1
denominator1 = (m1 + m2) * L1 - m2 * L1 * np.cos(delta) * np.cos(delta)
denominator2 = (L2 / L1) * denominator1
dydt = np.zeros_like(y)
dydt[0] = omega1
dydt[1] = (m2 * L1 * omega1 * omega1 * np.sin(delta) * np.cos(delta) +
m2 * g * np.sin(theta2) * np.cos(delta) +
m2 * L2 * omega2 * omega2 * np.sin(delta) -
(m1 + m2) * g * np.sin(theta1)) / denominator1
dydt[2] = omega2
dydt[3] = (-m2 * L2 * omega2 * omega2 * np.sin(delta) * np.cos(delta) +
(m1 + m2) * g * np.sin(theta1) * np.cos(delta) -
(m1 + m2) * L1 * omega1 * omega1 * np.sin(delta) -
(m1 + m2) * g * np.sin(theta2)) / denominator2
return dydt
# Solve the differential equations
sol = solve_ivp(equations, [0, t[-1]], initial_conditions, t_eval=t, method='RK45')
theta1 = sol.y[0]
theta2 = sol.y[2]
# Convert angles to Cartesian coordinates
x1 = L1 * np.sin(theta1)
y1 = -L1 * np.cos(theta1)
z1 = np.zeros_like(x1)
x2 = x1 + L2 * np.sin(theta2)
y2 = y1 - L2 * np.cos(theta2)
z2 = np.zeros_like(x2)
# Define the layout of the Dash app
app.layout = html.Div([
html.H1("3D Double Pendulum Simulation", style={'color': 'black', 'textAlign': 'center'}),
dcc.Graph(id='pendulum-graph'),
dcc.Interval(id='interval-component', interval=50, n_intervals=0)
], style={'backgroundColor': 'white', 'padding': '20px'})
# Define the callback to update the pendulum positions
@app.callback(
Output('pendulum-graph', 'figure'),
[Input('interval-component', 'n_intervals')]
)
def update_pendulum(n):
index = n % len(t)
fig = go.Figure()
# Add the first pendulum arm
fig.add_trace(go.Scatter3d(
x=[0, x1[index]], y=[0, y1[index]], z=[0, z1[index]],
mode='lines+markers',
line=dict(color='blue', width=3),
marker=dict(size=5, color='blue')
))
# Add the second pendulum arm
fig.add_trace(go.Scatter3d(
x=[x1[index], x2[index]], y=[y1[index], y2[index]], z=[z1[index], z2[index]],
mode='lines+markers',
line=dict(color='red', width=3),
marker=dict(size=5, color='red')
))
fig.update_layout(
scene=dict(
xaxis=dict(range=[-2, 2], autorange=False),
yaxis=dict(range=[-2, 2], autorange=False),
zaxis=dict(range=[-2, 2], autorange=False),
aspectratio=dict(x=1, y=1, z=1)
),
margin=dict(l=0, r=0, t=0, b=0)
)
return fig
# Run the app
if __name__ == '__main__':
app.run_server(debug=True)