Tuesday, September 6, 2016

, , , , , , , , , , , , , , , , , , ,

Double Spring Pendulum

It's a small variation of a simple physics problem, the double pendulum. In this case, the wires are not rigid, but instead, they're springs, therefore, double spring  pendulum.
The problem consists in finding the motion equations of this system. Here's a representation of the system:

I chose to solve this problem with a Lagrangian approach. Of course it'd be possible with Newton, but it would be so much more "painful".
For this reason, I started by writing the Lagrangian of the system. There are 4 degrees of freedom: $\alpha$, $\beta$, $a$ and $b$. With those identified, we can write the kinetic energy followed by the potential energy.
$K = \frac{1}{2} m_{1}v_{1}^2 + \frac{1}{2} m_{2}v_{2}^2$

$\overrightarrow{v_{1}}=\overrightarrow{\dot{a}}+\overrightarrow{\dot{\alpha}a}$
$\overrightarrow{v_{2}}=\overrightarrow{v_{1}}+\overrightarrow{v'}= \overrightarrow{v_{1}} + \left ( \overrightarrow{\dot{b}}+\overrightarrow{\dot{\beta}b} \right ) $
In this case $\overrightarrow{v_{1}}$ and $\overrightarrow{v_{2}}$ stand for the velocity of each mass, and $\overrightarrow{v'}$ for the relative velocity between the second mass with respect to the first.
It will be useful to find the expression for the square of each. The first one is very easy, due to the fact that the vectors are perpendicular. The second one, unfortunately, isn't that straight forward, but I'll spare you the math in between.

$v_{1}^2=\dot{a}^2+\dot{\alpha}^2a^2$

$v_{2}^2=\dot{a}^2+\dot{\alpha}^2a^2+\dot{b}^2+\dot{\beta}^2b^2 + \\
\ \ \ \ \ \ \ + 2\ sin(\alpha - \beta)\left (\dot{a}\dot{\beta}b - \dot{b}\dot{\alpha}a \right ) + 2\ cos(\alpha - \beta)\left (\dot{a}\dot{b} + \dot{\alpha}a\dot{\beta}b \right )$

The potential energy follows as:
$U=m_{1}gy_{1}+m_{2}gy_{2}+\frac{1}{2}k_{1}\Delta l_{1} ^2+\frac{1}{2}k_{2}\Delta l_{2} ^2$
$y_{1}=-a\ cos\ \alpha$
$y_{2}=-(a\ cos\ \alpha + b\ cos\ \beta)$
$\Delta l_{1} = a - l_{1}$
$\Delta l_{2} = b - l_{2}$

Where $l_{1}$ and $l_{2}$ are the springs' free lengths.
Now that we have defined the Lagrangian, we can calculate the Euler–Lagrange equations of motion:
$$\frac{\partial L}{\partial \alpha}-\frac{\mathrm{d} }{\mathrm{d} t}\left ( \frac{\partial L}{\partial{\alpha}'} \right )=0$$
$$\frac{\partial L}{\partial \beta}-\frac{\mathrm{d} }{\mathrm{d} t}\left ( \frac{\partial L}{\partial{\beta}'} \right )=0$$
$$\frac{\partial L}{\partial a}-\frac{\mathrm{d} }{\mathrm{d} t}\left ( \frac{\partial L}{\partial{a}'} \right )=0$$
$$\frac{\partial L}{\partial b}-\frac{\mathrm{d} }{\mathrm{d} t}\left ( \frac{\partial L}{\partial{b}'} \right )=0$$
At this part, I decided to use the tools of Wolfram Mathematica to solve this equations and find the solution of this system. If you want to see those steps, click the spoiler bellow.

 

Otherwise, the result is:
$$\alpha''=-\frac{1}{a} \left (2\,a'\,\alpha' + g\,sin(\alpha)+\frac{k_{2}}{m_{1}}\,(b-l_{2})\,sin(\alpha-\beta) \right )$$
$$\beta''=-\frac{1}{b} \left(\,2\,b'\,\beta' - \frac{k_{1}}{m_{1}}\,(a-l_{1})\,sin(\alpha-\beta)\,\right )$$
$$a''=\alpha'^2a+g\,cos(\alpha)-\frac{k_{1}}{m_{1}}\,(a-l_{1})+\frac{k_{2}}{m_{1}}\,(b-l_{2})\,cos(\alpha-\beta)$$
$$b''=\beta'^2\,b-\frac{k_{2}}{m_{2}}\,(b-l_{2})\,-\frac{k_{2}}{m_{1}}\,(b-l_{2})+\frac{k_{1}}{m_{1}}\,(a-l_{1})\,cos(\alpha-\beta)$$
Having these equations, I chose to make a little simulation in Python to test the results. In the next gif, you can see how it worked (for some arbitrary initial values):

To check the Python code, click the spoiler button below:


import math
from visual import *

def calc_a_2 (alpha_0,alpha_1, beta_0, beta_1, a_0, a_1, b_0, b_1, l1, l2, k1, k2, m1, m2, g):
    return ((k1*l1+g*m1*math.cos(alpha_0)-k2*l2*math.cos(alpha_0-beta_0)+k2*b_0*math.cos(alpha_0-beta_0)+a_0*(-k1+m1*(alpha_1**2)))/m1)
    
def calc_b_2 (alpha_0,alpha_1, beta_0, beta_1, a_0, a_1, b_0, b_1, l1, l2, k1, k2, m1, m2, g):
    return ((k2*l2*m1+k2*l2*m2-k1*l1*m2*math.cos(alpha_0-beta_0)+k1*m2*a_0*math.cos(alpha_0-beta_0)-b_0*(k2*(m1+m2)-m1*m2*(beta_1**2)))/(m1*m2))

def calc_alpha_2 (alpha_0,alpha_1, beta_0, beta_1, a_0, a_1, b_0, b_1, l1, l2, k1, k2, m1, m2, g):
    return (-((g*m1*math.sin(alpha_0)-k2*l2*math.sin(alpha_0-beta_0)+k2*b_0*math.sin(alpha_0-beta_0)+2*m1*a_1*alpha_1)/(m1*a_0)))

def calc_beta_2 (alpha_0,alpha_1, beta_0, beta_1, a_0, a_1, b_0, b_1, l1, l2, k1, k2, m1, m2, g):
    return ((-k1*l1*math.sin(alpha_0-beta_0)+k1*a_0*math.sin(alpha_0-beta_0)-2*m1*b_1*beta_1)/(m1*b_0))

alpha_0 = math.pi/2
beta_0 = 0
alpha_1 = 0
beta_1 = 0
alpha_2 = 0
beta_2 = 0

a_0 = 1
b_0 = 1
a_1 = 0
b_1 = 0
a_2 = 0
b_2 = 0

l1 = 1
l2 = 1
m1 = 1
m2 = 2
k1 = 200
k2 = 300
g = 9.81

dt = 0.001
t_max = 60
t=0


scene.autoscale = 0 # stop it from zooming in and out
scene.title = 'Double pendulum'
scene.range = (3, 3, 1)

#Center
ball0 = sphere(pos=vector(0, 0, 0), radius=0.05, color=color.cyan)

#Balls
ball1 = sphere(pos=vector(a_0*math.sin(alpha_0), -a_0*math.cos(alpha_0),0), radius=0.12, color=color.blue, make_trail=True, retain=int(1.0/dt))
ball2 = sphere(pos=vector(a_0*math.sin(alpha_0) + b_0*math.sin(beta_0), -a_0*math.cos(alpha_0) - b_0*math.cos(beta_0), 0), radius=0.12, color=color.red, make_trail=True, retain=int(1.0/dt))

#Strings
arm1 = cylinder(pos=(0,0,0), axis=(a_0*math.sin(alpha_0), -a_0*math.cos(alpha_0),0), radius=.03, color=color.white)
arm2 = cylinder(pos=(a_0*math.sin(alpha_0), -a_0*math.cos(alpha_0),0), axis=(b_0*math.sin(beta_0), -b_0*math.cos(beta_0), 0), radius=.03, color=color.white)

#while t<t_max :
while 1:
    rate(1/dt)
    
    if(round(t, int(-math.log10(dt)))%1 == 0):
        print "%ds" %t
        
    alpha_2 = calc_alpha_2 (alpha_0,alpha_1, beta_0, beta_1, a_0, a_1, b_0, b_1, l1, l2, k1, k2, m1, m2, g)
    beta_2 = calc_beta_2 (alpha_0,alpha_1, beta_0, beta_1, a_0, a_1, b_0, b_1, l1, l2, k1, k2, m1, m2, g)
    a_2 = calc_a_2 (alpha_0,alpha_1, beta_0, beta_1, a_0, a_1, b_0, b_1, l1, l2, k1, k2, m1, m2, g)
    b_2 = calc_b_2 (alpha_0,alpha_1, beta_0, beta_1, a_0, a_1, b_0, b_1, l1, l2, k1, k2, m1, m2, g)

    alpha_1 = alpha_1 + alpha_2*dt
    beta_1 = beta_1 + beta_2*dt
    a_1 = a_1 + a_2*dt
    b_1 = b_1 + b_2*dt

    alpha_0 = alpha_0 + alpha_1*dt
    beta_0 = beta_0 + beta_1*dt
    a_0 = a_0 + a_1*dt
    b_0 = b_0 + b_1*dt

    alpha_0 = alpha_0%(2*math.pi)
    beta_0 = beta_0%(2*math.pi)
    
    ball1.pos.x = a_0*math.sin(alpha_0)
    ball1.pos.y = -a_0*math.cos(alpha_0)
    arm1.axis = (ball1.pos.x, ball1.pos.y, 0)
    
    ball2.pos.x = ball1.pos.x + b_0*math.sin(beta_0)
    ball2.pos.y = ball1.pos.y - b_0*math.cos(beta_0)
    arm2.pos = (ball1.pos.x, ball1.pos.y, 0)
    arm2.axis = (ball2.pos.x - ball1.pos.x, ball2.pos.y - ball1.pos.y, 0)

#    if (t==0):
#        raw_input()
    
    t=t+dt


Hope that this was helpful to you in any way! If any doubt remains, feel free to contact me or to comment bellow.

15 comments:

  1. Hello,
    Why did you use
    if(round(t, int(-math.log10(dt)))%1 == 0):
    print "%ds" %t
    in the program?
    It causes syntax errors in the program.

    ReplyDelete
    Replies
    1. It was a dumb way to print the time that had passed. round() takes two arguments, the first is the number that we want to round and the second is the number of decimals. By doing the log of dt, I got the "scale" of the increment and because of that, when I print t, it doesn't have unnecessary 0's after the precision we're working. The if sentence garantees that the time is printed second after second. It shouldn't give you an error, but if it does, you can delete those lines.

      Delete
    2. Thank you for the reply.
      That's what I thought. Commenting the lines was bringing no visible change to the oscillations.
      Have a great day.

      Delete
  2. Nice Simulation!But how do you know if it's correct? Because I tried plotting the total energy(=K+U) in python and it was not conserved AT ALL. Even if we just look at the simulation , the masses seem to swing faster and faster.
    I would be very grateful if you would reply.

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
    2. Hi! First of all, I'm glad that you enjoyed! As you can see above, I derived the equations through the Euler–Lagrange equation, so it should be correct. I say should because there is the possibility that I made an error and I didn't notice. Despite that, I verified everything more than once, so I'm almost sure that the solution is correct. In terms of the simulation, the numeric method that I used to solve the differential equation isn't the best, so there is some growing small error. Would it be possible for me to see the code that you used to plot the total energy?

      Delete
    3. Hello again, I bring some results! I ran the simulation again and this time I measured the total energy of the system. During 43 seconds, with a time interval of dt=0.0000001 (to minimize the error introduced by the numeric methods of solving the differential equation), the change in total energy was only around 0.005% (and throughout it was never greater than this value). This led me to conclude that the non-conservation that you experienced was caused by the simulation itself, in particular, the time scale chosen (dt was too large), that then influenced the error introduced when numerically solving the differential equation.

      Delete
    4. Ah yes, of course, I had noticed that after typing this, sorry for not getting back to you on time . Thank you. I planned to give this simulation in the course I'm teaching. Great job.

      Delete
    5. @unknown Excuse me?

      Delete
    6. Thank you! I'm glad this is useful to someone. May I know what's the name of the course you're teaching? And good luck with that!

      Delete
  3. Hi Gonçalo! I have a question! Can I solve numerically the system of equations using a Runge-Kutta method? help, please!

    ReplyDelete
    Replies
    1. Yes, I believe you can! You'd even get a more precise solution. Will you try to implement it?

      Delete
    2. hello, I came here to ask you something. I hope you can see this. I made simulation with processing. And after 50seconds, balls are going crazy. They move faster and faster and blow out of the screen. please help me..

      Delete
    3. Hello! The balls going crazy after 50 seconds might be caused by an error in the implementation or numerical instability. If the first, then check your implementation of the equations, namely if you didn't miss any minus sign (this has certainly happened to me :)). If your problem is caused due to numerical stability, one thing you can try first is to decrease the time step. If you get a longer stable behavior (>50 seconds), it might be the case and you should try to implement it with a more advance ODE solver, like the Runge-Kutta method mentioned above.

      Delete