OpenSees Cloud
OpenSees AMI
Double Inverted Pendulum
Original Post - 08 Sep 2025 - Michael H. Scott
Support the blog at Buy Me a Coffee.
A double inverted pendulum (DIP) consists of two masses connected in series by rods to a pinned base. Without some form of control at the base, a DIP system is unstable.
Consider the DIP shown below in the upright (inverted) position. The DIP is controlled by vertical base excitation, e.g., imparted by a motor with adjustable amplitude and frequency.
At high frequency vertical excitation, the DIP should remain upright and stable.
Other forms of DIP control are possible, e.g., horizontal motion of the base or torque between the two rods.
You can find many sites online that first derive the equations of motion for a DIP in Lagrangian form then use an ODE solver, e.g., from Matlab, to determine the DIP response.
Instead, I will simulate DIP response in OpenSees, an approach that is far more general than the Lagrangian-then-ODE-solver route.
For simulation in OpenSees, we’ll make the following assumptions:
- Massless rods
- Frictionless pin at base
- Frictionless pin between rods
- No damping
- Initial out-of-plumbness 1/500 of the pendulum length
- Sinusoidal vertical base excitation
The model shown below uses corotational truss elements for the rods (elastic steel material with small cross-section) and default Newmark time integration (constant average acceleration).
import openseespy.opensees as ops
# Units = kg, m, sec
l1 = 0.5
l2 = 0.5
m1 = 0.25
m2 = 0.25
g = 9.81
w1 = m1*g
w2 = m2*g
E = 200e9*N/m**2 # Steel
A = 0.1*cm**2
f = 20/sec
ampl = 2*cm
Tfinal = 30*sec
dt = 0.001*sec
ops.wipe()
ops.model('basic','-ndm',2,'-ndf',2)
ops.node(0,0,0); ops.fix(0,1,1)
ops.node(1,0,l1); ops.mass(1,m1,m1)
ops.node(2,0,l1+l2); ops.mass(2,m2,m2)
ops.uniaxialMaterial('Elastic',1,E)
ops.element('corotTruss',1,0,1,A,1)
ops.element('corotTruss',2,1,2,A,1)
# Excitation
if f > 0:
T = 1/f
ops.timeSeries('Sine',1,0,Tfinal,T,'-factor',ampl)
ops.pattern('MultipleSupport',1)
ops.groundMotion(1,'Plain','-disp',1)
ops.imposedMotion(0,2,1) # node, dof, gmTag
# Self-weight
ops.timeSeries('Constant',2)
ops.pattern('Plain',2,2)
ops.load(1,0,-w1)
ops.load(2,0,-w2)
# Initial out-of-plumb
ops.setNodeDisp(1,1,l1/500,'-commit')
ops.setNodeDisp(2,1,(l1+l2)/500,'-commit')
ops.constraints('Transformation')
ops.system('UmfPack')
ops.analysis('Transient','-noWarnings')
Nsteps = int(Tfinal/dt)
for i in range(Nsteps):
ops.analyze(1,dt)
The following sections show the DIP response for various vertical excitation frequencies with 2 cm amplitude.
No Vertical Excitation
If we let the DIP fall from its slightly-out-of-plumb initial state with no base excitation, the resulting pendulum motion is chaotic.
The displacement response histories of the masses shown below follow no predictable pattern.
Note there is no energy dissipation in the simulation, so the masses will keep swinging around indefinitely.
Vertical Excitation – 10 Hz
Now, if we excite the base vertically at 10 Hz, the DIP response remains chaotic with both masses spinning rapidly toward the end of the simulation.
The rapid spinning is evident from the displacement response histories.
Vertical Excitation – 20 Hz
Increasing the vertical excitation to 20 Hz, the DIP response starts out looking more like that of a single pendulum with some “higher mode” oscillations. Then, the response becomes chaotic.
The displacement response histories show the masses largely stay in phase and follow a somewhat predictable pattern prior to chaotic response.
Vertical Excitation – 30 Hz
At a vertical excitation of 30 Hz, the DIP slowly leans prior to oscillating like a single pendulum. The masses stay in phase with some slight “higher mode” effects.
The smoother response is evident from the displacement response histories.
Vertical Excitation – 40 Hz
At a vertical excitation of 40 Hz, the DIP remains upright and stable.
The stability of the system is seen in the displacement response histories of the masses, starting at their initial out-of-plumb positions and oscillating about zero.
At higher frequency vertical excitation, the system will remain stable and the masses will vibrate horizontally at higher frequency.
For this configuration of rod lengths and masses and excitation amplitude, we’ve seen the response go from chaotic to single-pendulum-like to stable, all by increasing the vertical excitation frequency.
Play with the script. Change the masses and rod lengths. Experiment with
the frequency and amplitude of the vertical excitation. Add some mass
proportional damping via the rayleigh
command.