# Simulation 105: Double Pendulum Modeling with Numerical Integration | by Le Nguyen | Aug, 2023

The pendulum is a classical physics system that we’re all accustomed to. Be it a grandfather clock or a baby on a swing, we’ve seen the common, periodic movement of the pendulum. A single pendulum is effectively outlined in classical physics, however the double pendulum (a pendulum hooked up to the tip of one other pendulum) is literal chaos. On this article, we’re going to construct on our intuitive understanding of pendulums and mannequin the chaos of the double pendulum. The physics is attention-grabbing and the numerical strategies wanted are a vital instrument in anybody’s arsenal.

On this article we’ll:

- Find out about harmonic movement and mannequin the conduct of a single pendulum
- Study the basics of chaos idea
- Mannequin the chaotic conduct of a double pendulum numerically

## Easy Harmonic Movement

We describe the periodic oscillating motion of a pendulum as harmonic motion. Harmonic movement happens when there may be motion in a system that’s balanced out by a proportional restoring power in the other way of mentioned motion. We see an instance of this in determine 2 the place a mass on a spring is being pulled down as a consequence of gravity, however this places vitality into the spring which then recoils and pulls the mass again up. Subsequent to the spring system, we see the peak of the mass going round in a circle known as a phasor diagram which additional illustrates the common movement of the system.

Harmonic movement will be damped (reducing in amplitude as a consequence of drag forces) or pushed (growing in amplitude as a consequence of outdoors power being added), however we’ll begin with the only case of indefinite harmonic movement with no outdoors forces appearing on it (undamped movement). That is sort of movement is an efficient approximation for modeling a single pendulum that swings at a small angle/low amplitude. On this case we will mannequin the movement with equation 1 beneath.

We are able to simply put this operate into code and simulate a easy pendulum over time.

`def simple_pendulum(theta_0, omega, t, phi):`

theta = theta_0*np.cos(omega*t + phi)

return theta#parameters of our system

theta_0 = np.radians(15) #levels to radians

g = 9.8 #m/s^2

l = 1.0 #m

omega = np.sqrt(g/l)

phi = 0 #for small angle

time_span = np.linspace(0,20,300) #simulate for 20s cut up into 300 time intervals

theta = []

for t in time_span:

theta.append(simple_pendulum(theta_0, omega, t, phi))

#Convert again to cartesian coordinates

x = l*np.sin(theta)

y = -l*np.cos(theta) #detrimental to ensure the pendulum is dealing with down

## Full Pendulum Movement with Lagrangian Mechanics

A easy small angle pendulum is an efficient begin, however we wish to transcend this and mannequin the movement of a full pendulum. Since we will now not use small angle approximations it’s best to mannequin the pendulum utilizing Lagrangian mechanics. That is a vital instrument in physics that switches us from wanting on the forces in a system to wanting on the vitality in a system. We’re switching our body of reference from driving power vs restoring power to kinetic vs potential vitality.

The Lagrangain is the distinction between kinetic and potential vitality given in equation 2.

Substituting within the Kinetic and Potential of a pendulum given in equation 3 yields the Lagrangain for a pendulum seen is equation 4

With the Lagrangian for a pendulum we now describe the vitality of our system. There may be one final math step to undergo to rework this into one thing that we will construct a simulation on. We have to bridge again to the dynamic/power oriented reference from the vitality reference utilizing the Euler-Lagrange equation. Utilizing this equation we will use the Lagrangian to get the angular acceleration of our pendulum.

After going by the mathematics, we’ve angular acceleration which we will use to get angular velocity and angle itself. This may require some numerical integration that shall be specified by our full pendulum simulation. Even for a single pendulum, the non-linear dynamics means there isn’t a analytical resolution for fixing for *theta, *thus the necessity for a numerical resolution. The combination is sort of easy (however highly effective), we use angular acceleration to replace angular velocity and angular velocity to replace *theta *by including the previous amount to the latter and multiplying this by a while step. This will get us an approximation for the realm beneath the acceleration/velocity curve. The smaller the time step, the extra correct the approximation.

`def full_pendulum(g,l,theta,theta_velocity, time_step):`

#Numerical Integration

theta_acceleration = -(g/l)*np.sin(theta) #Get acceleration

theta_velocity += time_step*theta_acceleration #Replace velocity with acceleration

theta += time_step*theta_velocity #Replace angle with angular velocity

return theta, theta_velocityg = 9.8 #m/s^2

l = 1.0 #m

theta = [np.radians(90)] #theta_0

theta_velocity = 0 #Begin with 0 velocity

time_step = 20/300 #Outline a time step

time_span = np.linspace(0,20,300) #simulate for 20s cut up into 300 time intervals

for t in time_span:

theta_new, theta_velocity = full_pendulum(g,l,theta[-1], theta_velocity, time_step)

theta.append(theta_new)

#Convert again to cartesian coordinates

x = l*np.sin(theta)

y = -l*np.cos(theta)

Now we have simulated a full pendulum, however that is nonetheless a effectively outlined system. It’s now time to step into the chaos of the double pendulum.

Chaos, within the mathematical sense, refers to methods which can be extremely delicate to their preliminary circumstances. Even slight adjustments within the system’s begin will result in vastly completely different behaviors because the system evolves. This completely describes the movement of the double pendulum. Not like the one pendulum, it isn’t a effectively behaved system and can evolve in a vastly completely different method with even slight adjustments in beginning angle.

To mannequin the movement of the double pendulum, we’ll use the identical Lagrangian method as earlier than (see full derivation).

We will even be utilizing the identical numerical integration scheme as earlier than when implementing this equation into code and discovering theta.

`#Get theta1 acceleration `

def theta1_acceleration(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g):

mass1 = -g*(2*m1 + m2)*np.sin(theta1)

mass2 = -m2*g*np.sin(theta1 - 2*theta2)

interplay = -2*np.sin(theta1 - theta2)*m2*np.cos(theta2_velocity**2*l2 + theta1_velocity**2*l1*np.cos(theta1 - theta2))

normalization = l1*(2*m1 + m2 - m2*np.cos(2*theta1 - 2*theta2))theta1_ddot = (mass1 + mass2 + interplay)/normalization

return theta1_ddot

#Get theta2 acceleration

def theta2_acceleration(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g):

system = 2*np.sin(theta1 - theta2)*(theta1_velocity**2*l1*(m1 + m2) + g*(m1 + m2)*np.cos(theta1) + theta2_velocity**2*l2*m2*np.cos(theta1 - theta2))

normalization = l1*(2*m1 + m2 - m2*np.cos(2*theta1 - 2*theta2))

theta2_ddot = system/normalization

return theta2_ddot

#Replace theta1

def theta1_update(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g,time_step):

#Numerical Integration

theta1_velocity += time_step*theta1_acceleration(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g)

theta1 += time_step*theta1_velocity

return theta1, theta1_velocity

#Replace theta2

def theta2_update(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g,time_step):

#Numerical Integration

theta2_velocity += time_step*theta2_acceleration(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g)

theta2 += time_step*theta2_velocity

return theta2, theta2_velocity

#Run full double pendulum

def double_pendulum(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g,time_step,time_span):

theta1_list = [theta1]

theta2_list = [theta2]

for t in time_span:

theta1, theta1_velocity = theta1_update(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g,time_step)

theta2, theta2_velocity = theta2_update(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g,time_step)

theta1_list.append(theta1)

theta2_list.append(theta2)

x1 = l1*np.sin(theta1_list) #Pendulum 1 x

y1 = -l1*np.cos(theta1_list) #Pendulum 1 y

x2 = l1*np.sin(theta1_list) + l2*np.sin(theta2_list) #Pendulum 2 x

y2 = -l1*np.cos(theta1_list) - l2*np.cos(theta2_list) #Pendulum 2 y

return x1,y1,x2,y2

`#Outline system parameters`

g = 9.8 #m/s^2m1 = 1 #kg

m2 = 1 #kg

l1 = 1 #m

l2 = 1 #m

theta1 = np.radians(90)

theta2 = np.radians(45)

theta1_velocity = 0 #m/s

theta2_velocity = 0 #m/s

theta1_list = [theta1]

theta2_list = [theta2]

time_step = 20/300

time_span = np.linspace(0,20,300)

x1,y1,x2,y2 = double_pendulum(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g,time_step,time_span)

We’ve lastly carried out it! Now we have efficiently modeled a double pendulum, however now it’s time to look at some chaos. Our closing simulation shall be of two double pendulums with barely completely different beginning situation. We’ll set one pendulum to have a *theta 1 *of 90 levels and the opposite to have a *theta 1* of 91 levels. Let’s see what occurs.

We are able to see that each pendulums begin off with comparable trajectories however shortly diverge. That is what we imply after we say chaos, even a 1 diploma distinction in angle cascades into vastly completely different finish conduct.

On this article we realized about pendulum movement and mannequin it. We began from the only harmonic movement mannequin and constructed as much as the advanced and chaotic double pendulum. Alongside the best way we realized concerning the Lagrangian, chaos, and numerical integration.

The double pendulum is the only instance of a chaotic system. These methods exist all over the place in our world from population dynamics, climate, and even billiards. We are able to take the teachings we’ve realized from the double pendulum and apply them each time we encounter a chaotic methods.

## Key Take Aways

- Chaotic methods are very delicate to preliminary circumstances and can evolve in vastly other ways with even slight adjustments to their begin.
- When coping with a system, particularly a chaotic system, is there one other body of reference to take a look at it that makes it simpler to work with? (Just like the power reference body to the vitality reference body)
- When methods get too sophisticated we have to implement numerical options to unravel them. These options are easy however highly effective and provide good approximations to the precise conduct.

All figures used on this article have been both created by the writer or are from Math Images and full beneath the GNU Free Documentation License 1.2

Classical Mechanics, John Taylor https://neuroself.files.wordpress.com/2020/09/taylor-2005-classical-mechanics.pdf

## Easy Pendulum

`def makeGif(x,y,identify):`

!mkdir framescounter=0

photographs = []

for i in vary(0,len(x)):

plt.determine(figsize = (6,6))

plt.plot([0,x[i]],[0,y[i]], "o-", colour = "b", markersize = 7, linewidth=.7 )

plt.title("Pendulum")

plt.xlim(-1.1,1.1)

plt.ylim(-1.1,1.1)

plt.savefig("frames/" + str(counter)+ ".png")

photographs.append(imageio.imread("frames/" + str(counter)+ ".png"))

counter += 1

plt.shut()

imageio.mimsave(identify, photographs)

!rm -r frames

def simple_pendulum(theta_0, omega, t, phi):

theta = theta_0*np.cos(omega*t + phi)

return theta

#parameters of our system

theta_0 = np.radians(15) #levels to radians

g = 9.8 #m/s^2

l = 1.0 #m

omega = np.sqrt(g/l)

phi = 0 #for small angle

time_span = np.linspace(0,20,300) #simulate for 20s cut up into 300 time intervals

theta = []

for t in time_span:

theta.append(simple_pendulum(theta_0, omega, t, phi))

x = l*np.sin(theta)

y = -l*np.cos(theta) #detrimental to ensure the pendulum is dealing with down

## Pendulum

`def full_pendulum(g,l,theta,theta_velocity, time_step):`

theta_acceleration = -(g/l)*np.sin(theta)

theta_velocity += time_step*theta_acceleration

theta += time_step*theta_velocity

return theta, theta_velocityg = 9.8 #m/s^2

l = 1.0 #m

theta = [np.radians(90)] #theta_0

theta_velocity = 0

time_step = 20/300

time_span = np.linspace(0,20,300) #simulate for 20s cut up into 300 time intervals

for t in time_span:

theta_new, theta_velocity = full_pendulum(g,l,theta[-1], theta_velocity, time_step)

theta.append(theta_new)

#Convert again to cartesian coordinates

x = l*np.sin(theta)

y = -l*np.cos(theta)

#Use similar operate from easy pendulum

makeGif(x,y,"pendulum.gif")

## Double Pendulum

`def theta1_acceleration(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g):`

mass1 = -g*(2*m1 + m2)*np.sin(theta1)

mass2 = -m2*g*np.sin(theta1 - 2*theta2)

interplay = -2*np.sin(theta1 - theta2)*m2*np.cos(theta2_velocity**2*l2 + theta1_velocity**2*l1*np.cos(theta1 - theta2))

normalization = l1*(2*m1 + m2 - m2*np.cos(2*theta1 - 2*theta2))theta1_ddot = (mass1 + mass2 + interplay)/normalization

return theta1_ddot

def theta2_acceleration(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g):

system = 2*np.sin(theta1 - theta2)*(theta1_velocity**2*l1*(m1 + m2) + g*(m1 + m2)*np.cos(theta1) + theta2_velocity**2*l2*m2*np.cos(theta1 - theta2))

normalization = l1*(2*m1 + m2 - m2*np.cos(2*theta1 - 2*theta2))

theta2_ddot = system/normalization

return theta2_ddot

def theta1_update(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g,time_step):

theta1_velocity += time_step*theta1_acceleration(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g)

theta1 += time_step*theta1_velocity

return theta1, theta1_velocity

def theta2_update(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g,time_step):

theta2_velocity += time_step*theta2_acceleration(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g)

theta2 += time_step*theta2_velocity

return theta2, theta2_velocity

def double_pendulum(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g,time_step,time_span):

theta1_list = [theta1]

theta2_list = [theta2]

for t in time_span:

theta1, theta1_velocity = theta1_update(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g,time_step)

theta2, theta2_velocity = theta2_update(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g,time_step)

theta1_list.append(theta1)

theta2_list.append(theta2)

x1 = l1*np.sin(theta1_list)

y1 = -l1*np.cos(theta1_list)

x2 = l1*np.sin(theta1_list) + l2*np.sin(theta2_list)

y2 = -l1*np.cos(theta1_list) - l2*np.cos(theta2_list)

return x1,y1,x2,y2

`#Outline system parameters, run double pendulum`

g = 9.8 #m/s^2m1 = 1 #kg

m2 = 1 #kg

l1 = 1 #m

l2 = 1 #m

theta1 = np.radians(90)

theta2 = np.radians(45)

theta1_velocity = 0 #m/s

theta2_velocity = 0 #m/s

theta1_list = [theta1]

theta2_list = [theta2]

time_step = 20/300

time_span = np.linspace(0,20,300)

for t in time_span:

theta1, theta1_velocity = theta1_update(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g,time_step)

theta2, theta2_velocity = theta2_update(m1,m2,l1,l2,theta1,theta2,theta1_velocity,theta2_velocity,g,time_step)

theta1_list.append(theta1)

theta2_list.append(theta2)

x1 = l1*np.sin(theta1_list)

y1 = -l1*np.cos(theta1_list)

x2 = l1*np.sin(theta1_list) + l2*np.sin(theta2_list)

y2 = -l1*np.cos(theta1_list) - l2*np.cos(theta2_list)

`#Make Gif`

!mkdir framescounter=0

photographs = []

for i in vary(0,len(x1)):

plt.determine(figsize = (6,6))

plt.determine(figsize = (6,6))

plt.plot([0,x1[i]],[0,y1[i]], "o-", colour = "b", markersize = 7, linewidth=.7 )

plt.plot([x1[i],x2[i]],[y1[i],y2[i]], "o-", colour = "b", markersize = 7, linewidth=.7 )

plt.title("Double Pendulum")

plt.xlim(-2.1,2.1)

plt.ylim(-2.1,2.1)

plt.savefig("frames/" + str(counter)+ ".png")

photographs.append(imageio.imread("frames/" + str(counter)+ ".png"))

counter += 1

plt.shut()

imageio.mimsave("double_pendulum.gif", photographs)

!rm -r frames