Dynamics of the Planar Double-Pendulum

(C) 2004 - Roberto Lot

[Maple Metafile]

> restart: mylib :="C:/libs": libname := mylib,libname: with(MBsymba):

Description of the multibody system

revolute joints

unit vector parallel to the revolute joints axis

> cz := make_VECTOR(ground,0,0,1): show(cz);

cz = VECTOR(comps = matrix([[0], [0], [1], [0]]),fr...

fixed revolute joint

> A := make_POINT(ground,0,0,0): show(A);

A = POINT(frame = matrix([[1, 0, 0, 0], [0, 1, 0, 0...

revolute joint between bodies

> B := make_POINT(ground,xB(t),yB(t),0): show(B);

B = POINT(frame = matrix([[1, 0, 0, 0], [0, 1, 0, 0...

body 1

unit vectors fixed to the body 1

> u1 := 1/lAB*make_VECTOR(A,B): show(u1); along X axis
w1 := rotate('Z',Pi/2,u1):
w1 := project(w1,ground): show(w1);
along Y axis

u1 = VECTOR(comps = matrix([[1/lAB*xB(t)], [1/lAB*y...

w1 = VECTOR(comps = matrix([[-1/lAB*yB(t)], [1/lAB*...

frame fixed to the body

> T1 := linalg[transpose] (matrix(4,4, [ [Comps(u1),0] , [Comps(w1),0] , [Comps(cz),0] , [Comps(A),1] ]));

T1 := matrix([[1/lAB*xB(t), -1/lAB*yB(t), 0, 0], [1...

center of mass

> G1 := make_POINT(T1,a1,b1,0):

properties of the rigid body

> body1 := make_BODY(G1,m1,0,0,IZ1): show(body1);
G1 := project(G1,ground): show(G1);

body1 = BODY(mass = m1,inertia = matrix([[0, 0, 0],...

G1 = POINT(frame = matrix([[1, 0, 0, 0], [0, 1, 0, ...

weight force

> W1 := make_FORCE(ground,0,-m1*g,0,G1,body1): show(W1);

W1 = FORCE(comps = matrix([[0], [-m1*g], [0], [0]])...

body 2

unit vectors fixed to the boby 2

> u2 := make_VECTOR(ground,u2x(t),u2y(t),0): show(u2); along X axis
w2 := rotate('Z',Pi/2,u2):
w2 := project(w2,ground): show(w2);
along Y axis

u2 = VECTOR(comps = matrix([[u2x(t)], [u2y(t)], [0]...

w2 = VECTOR(comps = matrix([[-u2y(t)], [u2x(t)], [0...

frame fixed to body 2

> T2 := linalg[transpose] (matrix(4,4, [ [Comps(u2),0] , [Comps(w2),0] , [Comps(cz),0] , [Comps(B),1] ]));

T2 := matrix([[u2x(t), -u2y(t), 0, xB(t)], [u2y(t),...

center of mass

> b2:=0:
G2 := make_POINT(T2,a2,b2,0):

properties of the rigid body

> body2 := make_BODY(G2,m2,0,0,IZ2): show(body2);
G2 := project(G2,ground): show(G2);

body2 = BODY(mass = m2,inertia = matrix([[0, 0, 0],...

G2 = POINT(frame = matrix([[1, 0, 0, 0], [0, 1, 0, ...

weight force

> W2 := make_FORCE(ground,0,-m2*g,0,G2,body2): show(W2);

W2 = FORCE(comps = matrix([[0], [-m2*g], [0], [0]])...

equations of motion

variables

> q := [xB(t),yB(t),u2x(t),u2y(t)];
nq := nops(q);

q := [xB(t), yB(t), u2x(t), u2y(t)]

nq := 4

constraints

since there are 4 variables for 2 degrees of freedom only, 2 independent constraint equations must be found

1. distance between points A and B

> AB := make_VECTOR(A,B): show(AB);

> phi1 := dot_prod(AB,AB)-lAB^2;

AB = VECTOR(comps = matrix([[xB(t)], [yB(t)], [0], ...

phi1 := xB(t)^2+yB(t)^2-lAB^2

2. magnitude of vector u2

> phi2 := dot_prod(u2,u2)-1;

phi2 := u2x(t)^2+u2y(t)^2-1

assembly constraint equations

> constraints := [phi1,phi2];

constraints := [xB(t)^2+yB(t)^2-lAB^2, u2x(t)^2+u2y...

constraints - alternative solution

rigid body constraints are implicitely contained into the transformation matrices, where the rotation submatrix is orthogonal

> evalm(inv_frame(T1)*T1-1);
convert(%,set) minus {0};

matrix([[1/lAB^2*xB(t)^2+1/lAB^2*yB(t)^2-1, 0, 0, 0...

{1/lAB^2*xB(t)^2+1/lAB^2*yB(t)^2-1}

> evalm(inv_frame(T2)*T2-1);
convert(%,set) minus {0};

matrix([[u2x(t)^2+u2y(t)^2-1, 0, 0, 0], [0, u2x(t)^...

{u2x(t)^2+u2y(t)^2-1}

Lagrange's equations

kinetic energy

> KE := kinetic_energy(body1) + kinetic_energy(body2);

KE := 1/2*m1*((1/lAB*diff(xB(t),t)*a1-1/lAB*diff(yB...
KE := 1/2*m1*((1/lAB*diff(xB(t),t)*a1-1/lAB*diff(yB...

> quadratic_form(KE,diff(q,t));

matrix([[1/2*m1*(2/lAB^2*a1^2+2/lAB^2*b1^2)+IZ1/lAB...

generalixed forces

> FF := simplify(generalized_force({W1,W2},q)):
for i from 1 to nq do i=q[i],'FF'=FF[i]; end do;

1 = xB(t), FF = -b1*m1*g/lAB

2 = yB(t), FF = -g*(m2*lAB+a1*m1)/lAB

3 = u2x(t), FF = 0

4 = u2y(t), FF = -a2*m2*g

Lagrange's equations

> LL := KE + sum('lambda||k(t)'*constraints['k'],'k'=1..nops(constraints)): lagrangian

> leqns := [seq(expand(lagrange(LL,q[i],t)) - FF[i] , i=1..nq)]:

> for i from 1 to nq do i,q[i]; collect(leqns[i],diff(q,t,t)); od;

1, xB(t)

(1/lAB^2*m1*a1^2+m2+1/lAB^2*m1*b1^2+IZ1/lAB^2)*diff...

2, yB(t)

(1/lAB^2*m1*a1^2+m2+1/lAB^2*m1*b1^2+IZ1/lAB^2)*diff...

3, u2x(t)

m2*a2*diff(xB(t),`$`(t,2))+(m2*a2^2+IZ2)*diff(u2x(t...

4, u2y(t)

m2*a2*diff(yB(t),`$`(t,2))+(m2*a2^2+IZ2)*diff(u2y(t...

The result is a set on n=4 lagrange equations (2nd order differential equations) plus f=2 constraint equations (algebraic equations).
The unknowns are n=4 coordinates plus f=2 Lagrange's multipliers

Equations solution

a. Baumgarte stabilization method

in order to transfom the equations above into a standard ODE problem, the algebraic constraint equations must be derived twice with respece to the time.
Unfortunately, this operation lead to large drift error during numerical integration.

However, the Baumgarte's stabilization is sufficient for avoiding this inconvenience.

> baumgarte := convert(evalm(omega^2*constraints + 2*zeta*omega*diff(constraints,t) + diff(constraints,t,t)) ,list):
for i from 1 to nops(baumgarte) do i; baumgarte[i]; od;

1

omega^2*(xB(t)^2+yB(t)^2-lAB^2)+2*zeta*omega*(2*xB(...

2

omega^2*(u2x(t)^2+u2y(t)^2-1)+2*zeta*omega*(2*u2x(t...

The Lagrange's multipliers are algebraic unknowns, but they may be easily transformed into a differential form

> SL := [seq(lambda||i(t)= gamma||i(t)+tau*diff(gamma||i(t),t), i=1..nops(constraints))];

SL := [lambda1(t) = gamma1(t)+tau*diff(gamma1(t),t)...

assembly variables and equations

> all_vars := q union [seq(gamma||i(t), i=1..nops(constraints))];
all_eqns := subs(SL,leqns) union baumgarte:

> for i from 1 to nops(all_vars) do i,all_vars[i]; all_eqns[i]; od;

all_vars := [xB(t), yB(t), u2x(t), u2y(t), gamma1(t...

1, xB(t)

1/lAB^2*m1*diff(xB(t),`$`(t,2))*a1^2+1/lAB^2*m1*dif...

2, yB(t)

1/lAB^2*m1*diff(yB(t),`$`(t,2))*b1^2+1/lAB^2*m1*dif...

3, u2x(t)

m2*a2^2*diff(u2x(t),`$`(t,2))+m2*a2*diff(xB(t),`$`(...

4, u2y(t)

m2*a2^2*diff(u2y(t),`$`(t,2))+m2*a2*diff(yB(t),`$`(...

5, gamma1(t)

omega^2*(xB(t)^2+yB(t)^2-lAB^2)+2*zeta*omega*(2*xB(...

6, gamma2(t)

omega^2*(u2x(t)^2+u2y(t)^2-1)+2*zeta*omega*(2*u2x(t...

ODEs numerical integrations

pendulum characteristics

> pendulum_data:= {lAB=0.3, m1=2.,a1=0.2,b1=0.05, IZ1=0.2, m2=1.5,IZ2=0.3,a2=0.1, g=9.81};
baumgarte_data := {tau=0.1,omega=10,zeta=0.8};

pendulum_data := {a2 = .1, g = 9.81, lAB = .3, m1 =...

baumgarte_data := {tau = .1, omega = 10, zeta = .8}...

numerical ODEs

> ODE := convert(evalf(subs(pendulum_data,baumgarte_data,all_eqns)),set):
for i from 1 to nops(ODE) do i,ODE[i]; end;

1, .315*diff(u2x(t),`$`(t,2))+.15*diff(xB(t),`$`(t,...

2, 4.666666667*diff(xB(t),`$`(t,2))+3.270000000+.15...

3, 4.666666667*diff(yB(t),`$`(t,2))+27.79500000+.15...

4, .315*diff(u2y(t),`$`(t,2))+.15*diff(yB(t),`$`(t,...

5, 100.*u2x(t)^2+100.*u2y(t)^2-100.+32.0*u2x(t)*dif...

6, 100.*xB(t)^2+100.*yB(t)^2-9.00+32.0*xB(t)*diff(x...

initial conditions

> initA := { xB (0)=0, yB (0)=0.3, u2x (0)=1, u2y (0)=0,
D(xB)(0)=-0.1, D(yB)(0)=+0.01, D(u2x)(0)=0.01, D(u2y)(0)=.1}:
init := initA union {gamma1(0)=0, gamma2(0)=0 };
nops(init);

init := {xB(0) = 0, yB(0) = .3, u2x(0) = 1, u2y(0) ...

10

check that initial conditions are compatible with constraints

> subs(t=0,init,pendulum_data,constraints);

[0., 0]

integration parameters

> tend:=3; integration duration
np:=60; number of steps
tstep:=tend/np:

numerical solution

> FF := dsolve( ODE union init, convert(all_vars,set), type=numeric, output=array([seq(1.*i/np*tend , i=0..np)])):

> outvar := evalm(FF[1,1]);
solz := evalm(FF[2,1]):

tend := 3

np := 60

outvar := vector([t, gamma1(t), gamma2(t), u2x(t), ...

results

> i0:=1: i1:=8: i2:=10:
outvar[i0]; outvar[i1], outvar[i2];

> plot([ [[solz[n,i0],solz[n,i1]] $n=1..np+1], [[solz[n,i0],solz[n,i2]] $n=1..np+1] ] , color=[red,green,blue]);

t

xB(t), yB(t)

[Maple Plot]

results

> i0:=1: i3:=4: i4:=6:
outvar[i0]; outvar[i3], outvar[i4];

> plot([ [[solz[n,i0],solz[n,i3]] $n=1..np+1], [[solz[n,i0],solz[n,i4]] $n=1..np+1] ] , color=[red,green,blue]);

t

u2x(t), u2y(t)

[Maple Plot]

mechanism animation

> with(plots):
with(plottools):

Warning, the name changecoords has been redefined

Warning, the name arrow has been redefined

Warning, the previous bindings of the names project rotate, and translate have been removed and they now have an assigned value

extract the i-th configuration from data

> configuration := (solz,n)-> [ seq(outvar[i||k]=solz[n,i||k],k=1..4) ]:

> configuration(solz,1);

[xB(t) = 0., yB(t) = .3, u2x(t) = 1., u2y(t) = 0.]

definiton of the mechanism drawing

pin joints

> PA := disk([ 0, 0], 0.03, color=yellow):
PB := disk([xB(t),yB(t)], 0.03, color=yellow):

body 1

> lx1 := max(a1,lAB/2): lx1:=evalf(subs(pendulum_data,lx1)):

> PG1 := disk([Xcomp(G1),Ycomp(G1)], 0.02, color=red):
body1 := rectangle([-lx1,-b1], [lx1, b1], filled=true, color=cyan):

> alpha1:= arctan(yB(t),xB(t)): rotation angle

> body1 := translate(rotate(body1,alpha1),Xcomp(G1),Ycomp(G1)):

> pendulum := subs(pendulum_data,[PA,PB,PG1,body1]):

body 2

> PG2 := disk([Xcomp(G2),Ycomp(G2)], 0.02, color=red):
body2 := rectangle([-a2,-b2], [a2, b2], filled=true, color=green):

> alpha2:= arctan(u2y(t),u2x(t)): rotation angle

> body2 := translate(rotate(body2,alpha2),Xcomp(G2),Ycomp(G2)):

assembly the pendulum

> pendulum := subs(pendulum_data,[PA,PB,PG1,PG2,body1,body2]):

plot of a single configuration

> #nn := 4; display( subs(configuration(solz,nn),pendulum), scaling=constrained);

animation

> display([seq(display( subs(configuration(solz,i),pendulum)),i=1..np)], insequence=true, scaling=constrained);

[Maple Plot]

b. without constraint stabilization

The Baumgarte's constraint stabilization is indispensable for avoiding drift error during integration, as shown in this section.

ignore stabilization

> baumgarte_data := {tau=0.1,omega=0,zeta=00};

baumgarte_data := {zeta = 0, omega = 0, tau = .1}

numerical ODEs

> ODE2 := convert(evalf(subs(pendulum_data,baumgarte_data,all_eqns)),set):
for i from 1 to nops(ODE2) do i,ODE[i]; end;

1, .315*diff(u2x(t),`$`(t,2))+.15*diff(xB(t),`$`(t,...

2, 4.666666667*diff(xB(t),`$`(t,2))+3.270000000+.15...

3, 4.666666667*diff(yB(t),`$`(t,2))+27.79500000+.15...

4, .315*diff(u2y(t),`$`(t,2))+.15*diff(yB(t),`$`(t,...

5, 100.*u2x(t)^2+100.*u2y(t)^2-100.+32.0*u2x(t)*dif...

6, 100.*xB(t)^2+100.*yB(t)^2-9.00+32.0*xB(t)*diff(x...

numerical solution

> FF := dsolve( ODE2 union init, convert(all_vars,set), type=numeric, output=array([seq(1.*i/np*tend , i=0..np)])):

> outvar := evalm(FF[1,1]);
solz2 := evalm(FF[2,1]):

outvar := vector([t, gamma1(t), gamma2(t), u2x(t), ...

comparison between Baumgarte and Penalty methods

> outvar[i0], outvar[i1];
plot([ [[solz [n,1],solz [n, i1]] $n=1..np+1], [[solz2 [n,1],solz2 [n,i1]] $n=1..np+1] ] ,
color=[red], linestyle=[SOLID,DASH]);

t, xB(t)

[Maple Plot]

> outvar[i0], outvar[i2];
plot([ [[solz [n,1],solz [n, i2]] $n=1..np+1], [[solz2 [n,1],solz2 [n,i2]] $n=1..np+1] ] ,
color=[green], linestyle=[SOLID,DASH]);

t, yB(t)

[Maple Plot]

c. penalty formulation

Lagrange's multipliers represent the constraint actions on the system.
An idea for eliminating constraint equations and the corresponding Lagrange's multipliers is based on tolerating a small constraint violation and calculating the Lagrange's multipliers proportionally to this violation. Unfortunately, this trick generate stiff equations and at least some damping and inertia must be included in the constraints, as follows:

> alpha, constraints stiffness

> omega, zeta; constraints natural frequency and damping ratio

> penalty := [seq( lambda||i(t) = -alpha*(omega**2*constraints[i] + 2*zeta*omega*diff(constraints[i],t) + diff(constraints[i],t,t)), i=1..nops(constraints))];

alpha, omega, zeta

penalty := [lambda1(t) = -alpha*(omega^2*(xB(t)^2+y...
penalty := [lambda1(t) = -alpha*(omega^2*(xB(t)^2+y...

the new equations are

> penalty_eqns := subs(penalty,leqns):
for i from 1 to nq do i,q[i]; penalty_eqns[i]; od;

1, xB(t)

1/lAB^2*m1*diff(xB(t),`$`(t,2))*a1^2+1/lAB^2*m1*dif...
1/lAB^2*m1*diff(xB(t),`$`(t,2))*a1^2+1/lAB^2*m1*dif...

2, yB(t)

1/lAB^2*m1*diff(yB(t),`$`(t,2))*b1^2+1/lAB^2*m1*dif...
1/lAB^2*m1*diff(yB(t),`$`(t,2))*b1^2+1/lAB^2*m1*dif...
1/lAB^2*m1*diff(yB(t),`$`(t,2))*b1^2+1/lAB^2*m1*dif...

3, u2x(t)

m2*a2^2*diff(u2x(t),`$`(t,2))+m2*a2*diff(xB(t),`$`(...
m2*a2^2*diff(u2x(t),`$`(t,2))+m2*a2*diff(xB(t),`$`(...

4, u2y(t)

m2*a2^2*diff(u2y(t),`$`(t,2))+m2*a2*diff(yB(t),`$`(...
m2*a2^2*diff(u2y(t),`$`(t,2))+m2*a2*diff(yB(t),`$`(...

ODEs numerical integrations

numerical ODEs

> penalty_data:= {alpha= 1e8, omega=10, zeta=0.7};

> ODEP := convert(evalf(subs(pendulum_data,penalty_data,penalty_eqns)),set):
#for i from 1 to nops(ODEP) do i,ODEP[i]; end;

penalty_data := {omega = 10, zeta = .7, alpha = .1e...

initial conditions

> initP := initA;
nops(initP);

check that initial conditions are compatible with constraints

> subs(t=0,initP,pendulum_data,constraints);

initP := {xB(0) = 0, yB(0) = .3, u2x(0) = 1, u2y(0)...

8

[0., 0]

numerical solution

> FFP := dsolve( ODEP union initP, convert(q,set), type=numeric, stiff=true, output=array([seq(1.*i/np*tend , i=0..np)])):

> outvarP := evalm(FFP[1,1]);
solzP := evalm(FFP[2,1]):

outvarP := vector([t, u2x(t), diff(u2x(t),t), u2y(t...

results

> iP1:=6: iP2:=8:
outvarP[iP1], outvarP[iP2];

> plot([ [[solzP[n,1],solzP[n,iP1]] $n=1..np+1], [[solzP[n,1],solzP[n,iP2]] $n=1..np+1] ] , color=[red,green,blue] , linestyle=[3]);

xB(t), yB(t)

[Maple Plot]

comparison between Baumgarte and Penalty methods

> plot([ [[solz [n,1],solz [n, i1]] $n=1..np+1], [[solz [n,1],solz [n, i2]] $n=1..np+1],
[[solzP[n,1],solzP[n,iP1]] $n=1..np+1], [[solzP[n,1],solzP[n,iP2]] $n=1..np+1] ] ,
color=[red,green,red,green], style=[line,line,point,point]);

[Maple Plot]

>