
This microsite is associated to the paper
L. Jaulin (2023).
Inertial control of the a spinning flat disk
An illustration of the change of rotation without external forces :
from roblib import * # available at https://www.ensta-bretagne.fr/jaulin/roblib.py
def draw_wheel(R,I,wr,colorwheel="black"):
clean3D(ax,-3,3,-3,3,-1,5)
w=R@wr
L=R@I@wr # angular momentum
draw_arrow3D(ax,0,0,2,0.02*L[0,0],0.02*L[1,0],0.02*L[2,0],"red")
draw_arrow3D(ax,0,0,2,0.1*w[0,0],0.1*w[1,0],0.1*w[2,0],"green") # spin vector
l2,l3=sqrt(abs(I[2,2]-I30+0.5)),sqrt(abs((I[1,1]-I20+0.5)))
M=tran3H(0,0,2)@ToH(R)@wheel3H(r)
draw3H(ax,M,colorwheel,True,1)
def draw_masses(l2,l3,col):
p=array([[0],[0],[2]])+R@array([[0],[l2],[l3]])
ax.scatter(*p,color=col)
draw_masses(l2,0,'green')
draw_masses(-l2,0,'green')
draw_masses(0, l3,'black')
draw_masses(0,-l3,'black')
pause(0.001)
def f(x,u):
w1,w2,w3,I2,I3=x[0,0],x[1,0],x[2,0],x[3,0],x[4,0]
I1=I2+I3
u1,u2=u[0,0],u[1,0]
dw1=-((I3-I2)/I1)*w2*w3-w1*(u1+u2)/I1
dw2=-w3*w1-w2*u1/I2
dw3= w1*w2-w3*u2/I3
dI2=u1
dI3=u2
return array([[dw1],[dw2],[dw3],[dI2],[dI3]])
ax= Axes3D(figure())
dt=0.002
r=1
m=10
R=eye(3)
w=array([[10],[4],[1]])
wr=R.T@w
I20,I30=1/4*m*r**2,1/4*m*r**2
x=array([[wr[0,0]],[wr[1,0]],[wr[2,0]],[I20],[I30]])
for t in arange(0,1,dt):
I2,I3=x[3,0],x[4,0]
I1=I2+I3
I=diag([I1,I2,I3])
wr=array([[x[0,0]],[x[1,0]],[x[2,0]]])
draw_wheel(R,I,wr)
u=array([[0],[0]])
R=R@expw(dt*wr)
x=array([[wr[0,0]],[wr[1,0]],[wr[2,0]],[I2],[I3]])
x=x+dt*f(x+(dt/2)*f(x,u),u)
from sympy import *
from sympy.diffgeom import *
from roblib import * # available at https://www.ensta-bretagne.fr/jaulin/roblib.py
def draw_wheel(R,I,wr,colorwheel="black"):
clean3D(ax,-3,3,-3,3,-1,5)
w=R@wr
L=R@I@wr # angular momentum
draw_arrow3D(ax,0,0,2,0.05*L[0,0],0.05*L[1,0],0.05*L[2,0],"red")
draw_arrow3D(ax,0,0,2,0.3*w[0,0],0.3*w[1,0],0.3*w[2,0],"green") # spin vector
l2,l3=sqrt(abs(I[2,2]-I30+0.5)),sqrt(abs((I[1,1]-I20+0.5)))
M=tran3H(0,0,2)@ToH(R)@wheel3H(r)
draw3H(ax,M,colorwheel,True,1)
def draw_masses(l2,l3,col):
p=array([[0],[0],[2]])+R@array([[0],[l2],[l3]])
ax.scatter(*p,color=col)
draw_masses(l2,0,'green')
draw_masses(-l2,0,'green')
draw_masses(0, l3,'black')
draw_masses(0,-l3,'black')
pause(0.001)
def Symb_build(opt):
init_printing(use_unicode=True) # allow LaTeX printing
w1,w2,w3,I2,I3=symbols("w1 w2 w3 I2 I3")
I20,I30=symbols("I20 I30")
C=CoordSystem('C',Patch('P',Manifold('M',5)),[w1,w2,w3,I2,I3])
w1,w2,w3,I2,I3 = C.coord_functions()
E=C.base_vectors()
F=-((I3-I2)/(I2+I3))*w2*w3*E[0]+-w3*w1*E[1]+w1*w2*E[2]
G1=-(w1/(I2+I3))*E[0]-(w2/I2)*E[1]+E[3]
G2=-(w1/(I2+I3))*E[0]-(w3/I3)*E[2]+E[4]
V0=1/2*(I2-I20)**2 + 1/2*(I3-I30)**2
if opt=="align": V = 1/2*(w2)**2 + 1/2*(w3)**2 + V0
if opt=="passiv": V = 1/2*(I2+I3)*w1**2+ 1/2*I2*w2**2 + 1/2*I3*w3**2 + V0
if opt=="precession": V = 1/2*((I3-I2)*w2*w3)**2 + 1/2*((I2)*w1*w3)**2 + 1/2*((-I3)*w1*w2)**2 + V0
LfV=simplify(LieDerivative(F,V))
Lg1V=simplify(LieDerivative(G1,V))
Lg2V=simplify(LieDerivative(G2,V))
print("L(F,V))=",latex(LfV))
print("L(G1,V))=",latex(Lg1V))
print("L(G2,V))=",latex(Lg2V))
λV=lambdify((w1,w2,w3,I2,I3,I20,I30),V)
λLfV=lambdify((w1,w2,w3,I2,I3,I20,I30),LfV)
λLg1V=lambdify((w1,w2,w3,I2,I3,I20,I30),Lg1V)
λLg2V=lambdify((w1,w2,w3,I2,I3,I20,I30),Lg2V)
return λV,λLfV,λLg1V,λLg2V
def f(x,u):
w1,w2,w3,I2,I3=x[0,0],x[1,0],x[2,0],x[3,0],x[4,0]
I1=I2+I3
u1,u2=u[0,0],u[1,0]
dw1=-((I3-I2)/I1)*w2*w3-w1*(u1+u2)/I1
dw2=-w3*w1-w2*u1/I2
dw3= w1*w2-w3*u2/I3
dI2=u1
dI3=u2
return array([[dw1],[dw2],[dw3],[dI2],[dI3]])
def control(x):
w1,w2,w3,I2,I3=x[0,0],x[1,0],x[2,0],x[3,0],x[4,0]
V1= λV(w1,w2,w3,I2,I3,I20,I30)
Lg1V=λLg1V(w1,w2,w3,I2,I3,I20,I30)
Lg2V=λLg2V(w1,w2,w3,I2,I3,I20,I30)
#LfV = λLfV(w1,w2,w3,I2,I3,I20,I30)
V.append(V1)
u=-(1/sqrt((Lg1V)**2+(Lg2V)**2))*array([[Lg1V],[Lg2V]])
return u
def simu(E,t1,t2,colorwheel="black"):
print("Control strategy : ",E)
global λV,λLfV,λLg1V,λLg2V
λV,λLfV,λLg1V,λLg2V=Symb_build(E)
global R,x
for t in arange(t1,t2,dt):
I2,I3=x[3,0],x[4,0]
I1=I2+I3
I=diag([I1,I2,I3])
II2.append(I2-I20)
II3.append(I3-I30)
wr=array([[x[0,0]],[x[1,0]],[x[2,0]]])
#draw_wheel(R,I,wr,colorwheel)
T.append(t)
W1.append(wr[0,0])
W2.append(wr[1,0])
W3.append(wr[2,0])
Ek.append((0.5*wr.T@I@wr)[0,0])
Ep.append(norm(ad(I@wr)@wr))
u=control(x)
R=R@expw(dt*wr)
x=array([[wr[0,0]],[wr[1,0]],[wr[2,0]],[I2],[I3]])
x=x+dt*f(x+(dt/2)*f(x,u),u)
return
if __name__ == "__main__":
ax= Axes3D(figure())
dt=0.002
r=1
m=10
R=eye(3)
#wr=R.T@w
T,W1,W2,W3=[],[],[],[]
Ek,Ep,Lz,V,Tr,II1,II2,II3=[],[],[],[],[],[],[],[]
I20,I30=1/4*m*r**2,1/4*m*r**2
opt=1 # alignment : the i vector of the well should be aligned with the angular momentum
opt=2 # passivity based control
opt=3 # precession"
opt=4 # allign followed by precession"
if opt==1:
print("\n Aligment control \n ")
x=array([[0.00001],[10],[0],[I20],[I30]])
simu("align",0,40,"red")
if opt==2:
print("\n Passivity control \n ")
x=array([[10],[4],[1],[I20],[I30]])
simu("passiv",0,40,"green")
if opt==3:
print("\n Precession control \n ")
x=array([[10],[4],[1],[I20],[I30]])
simu("precession",0,80,"blue")
if opt==4:
print("\n Alignment controlled followed by a precession control \n ")
x=array([[0.00001],[10],[0],[I20],[I30]])
simu("align",0,20,"red")
simu("precession",20,40,"blue")
print("x=",x)