Skip to content

Commit 797b011

Browse files
committed
Adds a three link conical pendulum example.
This adds the example problem Tarun developed and used when designing the visualization package. I've updated it to run with the current version of PyDy. The original pull request can be found here: pydy/pydy_examples#11
1 parent 20c8ca9 commit 797b011

File tree

4 files changed

+249
-0
lines changed

4 files changed

+249
-0
lines changed

Diff for: examples/three_link_conical_pendulum/README.rst

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
This example shows how to derive, simulate, and visualize the motion of a three
2+
link `conical pendulum`_. To run the complete example, execute `visualize.py`
3+
with the Python interpreter::
4+
5+
$ python visualize.py
6+
7+
Your default web browser should open with an interactive 3D visualization of
8+
the pendulum's motion.
9+
10+
.. _conical pendulum: http://en.wikipedia.org/wiki/Conical_pendulum

Diff for: examples/three_link_conical_pendulum/derive.py

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#!/usr/bin/env python
2+
3+
from sympy import symbols
4+
import sympy.physics.mechanics as me
5+
6+
print("Defining the problem.")
7+
8+
# The conical pendulum will have three links and three bobs.
9+
n = 3
10+
11+
# Each link's orientation is described by two spaced fixed angles: alpha and
12+
# beta.
13+
14+
# Generalized coordinates
15+
alpha = me.dynamicsymbols('alpha:{}'.format(n))
16+
beta = me.dynamicsymbols('beta:{}'.format(n))
17+
18+
# Generalized speeds
19+
omega = me.dynamicsymbols('omega:{}'.format(n))
20+
delta = me.dynamicsymbols('delta:{}'.format(n))
21+
22+
# At each joint there are point masses (i.e. the bobs).
23+
m_bob = symbols('m:{}'.format(n))
24+
25+
# Each link is modeled as a cylinder so it will have a lenght, mass, and a
26+
# symmetric inertia tensor.
27+
l = symbols('l:{}'.format(n))
28+
m_link = symbols('M:{}'.format(n))
29+
Ixx = symbols('Ixx:{}'.format(n))
30+
Iyy = symbols('Iyy:{}'.format(n))
31+
Izz = symbols('Izz:{}'.format(n))
32+
33+
# Acceleration due to gravity will be used when prescribing the forces
34+
# acting on the links and bobs.
35+
g = symbols('g')
36+
37+
# Now defining an inertial reference frame for the system to live in. The Y
38+
# axis of the frame will be aligned with, but opposite to, the gravity
39+
# vector.
40+
41+
I = me.ReferenceFrame('I')
42+
43+
# Three reference frames will track the orientation of the three links.
44+
45+
A = me.ReferenceFrame('A')
46+
A.orient(I, 'Space', [alpha[0], beta[0], 0], 'ZXY')
47+
48+
B = me.ReferenceFrame('B')
49+
B.orient(A, 'Space', [alpha[1], beta[1], 0], 'ZXY')
50+
51+
C = me.ReferenceFrame('C')
52+
C.orient(B, 'Space', [alpha[2], beta[2], 0], 'ZXY')
53+
54+
# Define the kinematical differential equations such that the generalized
55+
# speeds equal the time derivative of the generalized coordinates.
56+
kinematic_differentials = []
57+
for i in range(n):
58+
kinematic_differentials.append(omega[i] - alpha[i].diff())
59+
kinematic_differentials.append(delta[i] - beta[i].diff())
60+
61+
# The angular velocities of the three frames can then be set.
62+
A.set_ang_vel(I, omega[0] * I.z + delta[0] * I.x)
63+
B.set_ang_vel(I, omega[1] * I.z + delta[1] * I.x)
64+
C.set_ang_vel(I, omega[2] * I.z + delta[2] * I.x)
65+
66+
# The base of the pendulum will be located at a point O which is stationary
67+
# in the inertial reference frame.
68+
O = me.Point('O')
69+
O.set_vel(I, 0)
70+
71+
# The location of the bobs (at the joints between the links) are created by
72+
# specifiying the vectors between the points.
73+
P1 = O.locatenew('P1', -l[0] * A.y)
74+
P2 = P1.locatenew('P2', -l[1] * B.y)
75+
P3 = P2.locatenew('P3', -l[2] * C.y)
76+
77+
# The velocities of the points can be computed by taking advantage that
78+
# pairs of points are fixed on the referene frames.
79+
P1.v2pt_theory(O, I, A)
80+
P2.v2pt_theory(P1, I, B)
81+
P3.v2pt_theory(P2, I, C)
82+
points = [P1, P2, P3]
83+
84+
# Now create a particle to represent each bob.
85+
Pa1 = me.Particle('Pa1', points[0], m_bob[0])
86+
Pa2 = me.Particle('Pa2', points[1], m_bob[1])
87+
Pa3 = me.Particle('Pa3', points[2], m_bob[2])
88+
particles = [Pa1, Pa2, Pa3]
89+
90+
# The mass centers of each link need to be specified and, assuming a
91+
# constant density cylinder, it is equidistance from each joint.
92+
P_link1 = O.locatenew('P_link1', -l[0] / 2 * A.y)
93+
P_link2 = P1.locatenew('P_link2', -l[1] / 2 * B.y)
94+
P_link3 = P2.locatenew('P_link3', -l[2] / 2 * C.y)
95+
96+
# The linear velocities can be specified the same way as the bob points.
97+
P_link1.v2pt_theory(O, I, A)
98+
P_link2.v2pt_theory(P1, I, B)
99+
P_link3.v2pt_theory(P2, I, C)
100+
101+
points_rigid_body = [P_link1, P_link2, P_link3]
102+
103+
# The inertia tensors for the links are defined with respect to the mass
104+
# center of the link and the link's reference frame.
105+
inertia_link1 = (me.inertia(A, Ixx[0], Iyy[0], Izz[0]), P_link1)
106+
inertia_link2 = (me.inertia(B, Ixx[1], Iyy[1], Izz[1]), P_link2)
107+
inertia_link3 = (me.inertia(C, Ixx[2], Iyy[2], Izz[2]), P_link3)
108+
109+
# Now rigid bodies can be created for each link.
110+
link1 = me.RigidBody('link1', P_link1, A, m_link[0], inertia_link1)
111+
link2 = me.RigidBody('link2', P_link2, B, m_link[1], inertia_link2)
112+
link3 = me.RigidBody('link3', P_link3, C, m_link[2], inertia_link3)
113+
links = [link1, link2, link3]
114+
115+
# The only contributing forces to the system is the force due to gravity
116+
# acting on each particle and body.
117+
forces = []
118+
119+
for particle in particles:
120+
mass = particle.get_mass()
121+
point = particle.get_point()
122+
forces.append((point, -mass * g * I.y))
123+
124+
for link in links:
125+
mass = link.get_mass()
126+
point = link.get_masscenter()
127+
forces.append((point, -mass * g * I.y))
128+
129+
# Make a list of all the particles and bodies in the system.
130+
total_system = links + particles
131+
132+
# Lists of all generalized coordinates and speeds.
133+
q = alpha + beta
134+
u = omega + delta
135+
136+
# Now the equations of motion of the system can be formed.
137+
print("Generating equations of motion.")
138+
kane = me.KanesMethod(I, q_ind=q, u_ind=u, kd_eqs=kinematic_differentials)
139+
fr, frstar = kane.kanes_equations(forces, total_system)
140+
print("Derivation complete.")

Diff for: examples/three_link_conical_pendulum/simulate.py

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/usr/bin/env python
2+
3+
# external
4+
from numpy import radians, linspace, hstack, zeros, ones
5+
from scipy.integrate import odeint
6+
from pydy.codegen.code import generate_ode_function
7+
8+
# local
9+
from derive import l, m_bob, m_link, Ixx, Iyy, Izz, g, kane
10+
11+
param_syms = []
12+
for par_seq in [l, m_bob, m_link, Ixx, Iyy, Izz, (g,)]:
13+
param_syms += list(par_seq)
14+
15+
# All of the links and bobs will have the same numerical values for the
16+
# parameters.
17+
18+
link_length = 10.0 # meters
19+
link_mass = 10.0 # kg
20+
link_radius = 0.5 # meters
21+
link_ixx = 1.0 / 12.0 * link_mass * (3.0 * link_radius**2 + link_length**2)
22+
link_iyy = link_mass * link_radius**2
23+
link_izz = link_ixx
24+
25+
particle_mass = 5.0 # kg
26+
particle_radius = 1.0 # meters
27+
28+
# Create a list of the numerical values which have the same order as the
29+
# list of symbolic parameters.
30+
param_vals = [link_length for x in l] + \
31+
[particle_mass for x in m_bob] + \
32+
[link_mass for x in m_link] + \
33+
[link_ixx for x in list(Ixx)] + \
34+
[link_iyy for x in list(Iyy)] + \
35+
[link_izz for x in list(Izz)] + \
36+
[9.8]
37+
38+
# A function that evaluates the right hand side of the set of first order
39+
# ODEs can be generated.
40+
print("Generating numeric right hand side.")
41+
right_hand_side = generate_ode_function(kane.mass_matrix_full,
42+
kane.forcing_full,
43+
param_syms, kane._q, kane._u)
44+
45+
# To simulate the system, a time vector and initial conditions for the
46+
# system's states is required.
47+
t = linspace(0.0, 60.0, num=600)
48+
x0 = hstack((ones(6) * radians(10.0), zeros(6)))
49+
50+
print("Integrating equations of motion.")
51+
state_trajectories = odeint(right_hand_side, x0, t,
52+
args=({'constants': param_vals},))
53+
print("Integration done.")

Diff for: examples/three_link_conical_pendulum/visualize.py

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env python
2+
3+
# external
4+
from pydy.viz.shapes import Cylinder, Sphere
5+
from pydy.viz.scene import Scene
6+
from pydy.viz.visualization_frame import VisualizationFrame
7+
8+
# local
9+
from derive import I, O, links, particles, kane
10+
from simulate import param_syms, state_trajectories, param_vals
11+
from simulate import link_length, link_radius, particle_radius
12+
13+
# A cylinder will be attached to each link and a sphere to each bob for the
14+
# visualization.
15+
16+
viz_frames = []
17+
18+
for i, (link, particle) in enumerate(zip(links, particles)):
19+
20+
link_shape = Cylinder(name='cylinder{}'.format(i),
21+
radius=link_radius,
22+
length=link_length,
23+
color='red')
24+
25+
viz_frames.append(VisualizationFrame('link_frame{}'.format(i), link,
26+
link_shape))
27+
28+
particle_shape = Sphere(name='sphere{}'.format(i),
29+
radius=particle_radius,
30+
color='blue')
31+
32+
viz_frames.append(VisualizationFrame('particle_frame{}'.format(i),
33+
link.frame,
34+
particle,
35+
particle_shape))
36+
37+
# Now the visualization frames can be passed in to create a scene.
38+
scene = Scene(I, O, *viz_frames)
39+
40+
# And the motion of the shapes is generated by providing the scene with the
41+
# state trajectories.
42+
print('Generating transform time histories.')
43+
scene.generate_visualization_json(kane._q + kane._u, param_syms,
44+
state_trajectories, param_vals)
45+
print('Done.')
46+
scene.display()

0 commit comments

Comments
 (0)