-
Notifications
You must be signed in to change notification settings - Fork 9
/
dp_lagrangian.py
145 lines (113 loc) · 3.77 KB
/
dp_lagrangian.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/env python3
"""Double pendulum simulator based on its Euler-Lagrange equations."""
import math
import numpy
class DoublePendulumLagrangian:
def __init__(self, g, m1, m2, t1, t2, w1, w2, L1, L2):
"""
Constructs a double pendulum simulator based on its
Euler-Lagrange equations. Bob #1 is the one attached to the
fixed pivot.
g - The gravitational acceleration.
m1 - The mass of bob #1.
m2 - The mass of bob #2.
t1 - The initial angle of bob #1.
t2 - The initial angle of bob #2.
w1 - The initial angular velocity of bob #1.
w2 - The initial angular velocity of bob #2.
L1 - The length of the rod for bob #1.
L2 - The length of the rod for bob #2.
"""
self.g = g
self.m1 = m1
self.m2 = m2
self.t1 = t1
self.t2 = t2
self.w1 = w1
self.w2 = w2
self.L1 = L1
self.L2 = L2
def potential_energy(self):
"""Computes the potential energy of the system."""
m1 = self.m1
t1 = self.t1
L1 = self.L1
m2 = self.m2
t2 = self.t2
L2 = self.L2
g = self.g
# compute the height of each bob
y1 = -L1 * math.cos(t1)
y2 = y1 - L2 * math.cos(t2)
return m1 * g * y1 + m2 * g * y2
def kinetic_energy(self):
"""Computes the kinetic energy of the system."""
m1 = self.m1
t1 = self.t1
w1 = self.w1
L1 = self.L1
m2 = self.m2
t2 = self.t2
w2 = self.w2
L2 = self.L2
# compute the kinetic energy of each bob
K1 = 0.5 * m1 * (L1 * w1)**2
K2 = 0.5 * m2 * ((L1 * w1)**2 + (L2 * w2)**2 +
2 * L1 * L2 * w1 * w2 * math.cos(t1 - t2))
return K1 + K2
def mechanical_energy(self):
"""
Computes the mechanical energy (total energy) of the
system.
"""
return self.kinetic_energy() + self.potential_energy()
def lagrange_rhs(self, t1, t2, w1, w2):
"""
Computes the right-hand side of the Euler-Lagrange equations
for the double pendulum and returns it as an array.
t1 - The angle of bob #1.
t2 - The angle of bob #2.
w1 - The angular velocity of bob #1.
w2 - The angular velocity of bob #2.
"""
m1 = self.m1
L1 = self.L1
m2 = self.m2
L2 = self.L2
g = self.g
a1 = (L2 / L1) * (m2 / (m1 + m2)) * math.cos(t1 - t2)
a2 = (L1 / L2) * math.cos(t1 - t2)
f1 = -(L2 / L1) * (m2 / (m1 + m2)) * (w2**2) * math.sin(t1 - t2) - \
(g / L1) * math.sin(t1)
f2 = (L1 / L2) * (w1**2) * math.sin(t1 - t2) - (g / L2) * math.sin(t2)
g1 = (f1 - a1 * f2) / (1 - a1 * a2)
g2 = (f2 - a2 * f1) / (1 - a1 * a2)
return numpy.array([w1, w2, g1, g2])
def time_step(self, dt):
"""
Advances one time step using RK4 (classical Runge-Kutta
method).
"""
m1 = self.m1
t1 = self.t1
w1 = self.w1
L1 = self.L1
m2 = self.m2
t2 = self.t2
w2 = self.w2
L2 = self.L2
# y is an array with the generalized coordinates (angles +
# angular velocities)
y = numpy.array([t1, t2, w1, w2])
# compute the RK4 constants
k1 = self.lagrange_rhs(*y)
k2 = self.lagrange_rhs(*(y + dt * k1 / 2))
k3 = self.lagrange_rhs(*(y + dt * k2 / 2))
k4 = self.lagrange_rhs(*(y + dt * k3))
# compute the RK4 right-hand side
R = 1.0 / 6.0 * dt * (k1 + 2.0 * k2 + 2.0 * k3 + k4)
# update the angles and angular velocities
self.t1 += R[0]
self.t2 += R[1]
self.w1 += R[2]
self.w2 += R[3]