Marc BUFFAT département mécanique, université Lyon 1
"Life is like riding a bicycle.
To keep your balance, you must keep moving.”
(Albert Einstein)
%matplotlib inline
import numpy as np
import sympy as sp
import k3d
import matplotlib.pyplot as plt
from IPython.core.display import HTML
from IPython.display import display,Image
from sympy.physics.vector import init_vprinting
init_vprinting(use_latex='mathjax', pretty_print=False)
Soit le modèle de trottinette ci-dessous
La trottinette consiste en un cadre AC horizontale de longueur L, une fourche verticale CB pouvant tourné suivant l'axe CB, une roue arrière d'axe A de rayon R et une roue avant d'axe B de même rayon r. Le conducteur de la trottinette a son centre de gravité en G au milieu de AB et distant de h suivant la verticale.
La cinématique de la trottinette est donnée par la position de A et sa vitesse (fournit par le moteur), ainsi que la position des autres éléments par rapport à A et des conditions cinématiques (contacte sans glissement).
On note O un point d'origine et $R_0$ le repère fixe d'axe vertical $R_0.z$, La trottinette se déplace sur un plan horizontal ($R_0.x$,$R_0.y$) en conservant un cadre dans le plan vertical. Soit $R_1$ le repère lié au cadre en A, de position $x_A,y_A$ et orienté dans le sens de la vitesse en A est en rotation dans le plan horizontal avec un angle $\psi$ autour de la verticale $R_0.z$. On note $L$ la distance entre l'axe des 2 roues $A$ et $B$, et on suppose que $G$ est à égale distance de $A$ et $B$ et distant de $h$ en hauteur. On note $R_2$ le repère lié à la fourche qui est en rotation d'angle $\phi$ autour de $R_1.z$. On note $R_3$ le repère lié à la roue arrière qui est en rotation d'angle $\theta_1$ autour de $R_1.y$, et $R_4$ le repère lié à la roue avant qui est en rotation d'angle $\theta_2$ autour de $R_2.y$.
on se propose de déterminer la cinématique de la trottinette dans le cas général, puis dans le cas d'une trajectoire rectiligne (pour vérification) et enfin dans le cas d'une trajectoire circulaire de rayon $R$.
5 repères:
La position de la trottinette avec ses roues et son conducteur est donc donnée par:
Des paramétres r (rayon roue),h (hauteur de G),L (longueur du cadre),R (rayon de la trajectoire)
from sympy.physics.mechanics import dynamicsymbols, Point, ReferenceFrame
# parametres du problème
r, L, h, R, t = sp.symbols('r L h R t')
# degrés de liberté
xa, ya = dynamicsymbols('x_a y_a')
ua = dynamicsymbols('u_a')
psi, phi, theta1, theta2 = dynamicsymbols('psi phi theta_1 theta_2')
# relation cinématique
display(sp.Eq(xa.diff(t),ua*sp.cos(psi)))
display(sp.Eq(ya.diff(t),ua*sp.sin(psi)))
relU = [(xa.diff(t), ua*sp.cos(psi)),(ya.diff(t), ua*sp.sin(psi))]
display(relU)
# reperes et points
O = Point('O')
R0 = ReferenceFrame('R_0')
# cadre
R1 = ReferenceFrame('R_1')
R1.orient(R0,'Axis',[psi, R0.z])
A = Point('A')
A.set_pos(O,xa*R0.x + ya*R0.y + r*R0.z )
# roue arriere
R3 = ReferenceFrame('R_3')
R3.orient(R1,'Axis',[theta1, R1.y])
# point P1 de la roue arriere
P1 = Point('P1')
P1.set_pos(A,r*R3.z)
# centre de gravité
G = Point('G')
G.set_pos(A, L/2*R1.x + h*R1.z)
# fourche
R2 = ReferenceFrame('R_2')
R2.orient(R1,'Axis',[phi, R1.z])
B = Point('B')
B.set_pos(A, L*R1.x)
# roue avant
R4 = ReferenceFrame('R_4')
R4.orient(R2,'Axis',[theta2, R2.y])
# point P2 de la roue avant
P2 = Point('P2')
P2.set_pos(B,r*R4.z)
# positions dans R0 du cadre
display('OG=',G.pos_from(O).express(R0).simplify())
display('OA=',A.pos_from(O).express(R0).simplify())
display('OB=',B.pos_from(O).express(R0).simplify())
# position des roues
print("Position des roues:")
display('AP1=',P1.pos_from(A).express(R1).simplify())
display('BP2=',P2.pos_from(B).express(R1).simplify())
# vitesse de A
O.set_vel(R0,0.)
A.set_vel(R0,ua*R1.x)
display("VA=",A.vel(R0))
# vitesse du cadre
A.set_vel(R1,0.)
display("VA=",A.vel(R0))
G.set_vel(R1,0.)
display("VG=",G.v1pt_theory(A,R0,R1))
B.set_vel(R1,0.)
display("VB=",B.v2pt_theory(A,R0,R1))
# vitesse des roues
display("VP1=",P1.v2pt_theory(A,R0,R3).express(R1).simplify())
display("VP2=",P2.v2pt_theory(B,R0,R4).express(R2).simplify())
# projection dans R1
VP1=P1.vel(R0).express(R1).simplify()
display("VP1=",VP1)
VP2=P2.vel(R0).express(R2).simplify()
display("VP2=",VP2)
permet de calculer la rotation des 2 roues $\omega_1,\omega_2$, et l'angle de rotation $\phi$ de la fourche si on se donne la vitesse $u_a$ et sa direction $\psi$
#### calcul de la vitesse des roues au point de contacte
point de contacte $\dot{\theta}=\omega$ et $\theta=\pi$
# condition de roulement sans glissement roue arriere: VG1=0
omega1 = sp.symbols("omega_1")
display(VP1)
VG1=VP1.subs([(theta1.diff(t),omega1),(theta1,sp.pi)])
display("cdt de non glissement roue arriere:",VG1)
cdtsGL1=[(omega1,sp.solve(VG1.dot(R1.x),omega1)[0])]
display(cdtsGL1)
# condition de roulement sans glissement roue avant: VP2=0
omega2 = sp.symbols("omega_2")
display(VP2)
VG2=VP2.subs([(theta2.diff(t),omega2),(theta2,sp.pi)])
display("cdt de non glissement:",VG2)
# cdts de non glissement
phi0=sp.symbols("phi_0")
eq1=VG2.dot(R2.x)
display(eq1)
eq2=VG2.dot(R2.y)
display(eq2)
eq11=(sp.cos(phi)*eq1-sp.sin(phi)*eq2).simplify()
eq22=(sp.sin(phi)*eq1+sp.cos(phi)*eq2).simplify()
cdtsGL2=[(phi0,sp.solve(eq22.subs([(omega2,sp.solve(eq11,omega2)[0])]),phi)[0]),
(omega2,sp.solve(eq11,omega2)[0].subs(phi,phi0))]
display(cdtsGL2)
la vitesse est constante en module et direction
la position de A est fonction de l'angle initial de la vitesse $\psi(0) = \psi_0$
$$x_a = u_a \cos(\psi_0) t, y_a = u_a \sin(\psi_0) t$$
les conditions de roulement sans glissement impose l'alignement de la fourche et du cadre:
$$\phi = 0$$$$\dot{\theta_1} = \omega_1 = \frac{u_a}{r} = \dot{\theta_2} = \omega_2 $$# cdts de roulement sans glissement
cdts = [(psi.diff(t),0),(phi,0)]
display("VG1=",VG1)
display("VG2=",VG2.subs(cdts))
On a donc forcement $$\omega_1 = \omega_2 = \frac{u_A}{r}$$
# conditions cinematiques
cdtsNG=[(theta1,omega1*t),(theta2,omega2*t),
(omega1,ua/r),(omega2,ua/r),(xa,ua*sp.cos(psi)*t),(ya,ua*sp.sin(psi)*t)]
display(cdtsNG)
# valeurs des parametres numériques en USI
valnum=[(L,1.),(r,0.05),(h,1.),(ua,1*0.27778),(psi,np.pi/6),(phi,0)]
display(valnum)
# verification
display("OA=",A.pos_from(O).subs(cdtsNG).subs(valnum))
display("OP1",P1.pos_from(O).subs(cdtsNG).subs(valnum))
# bibliotheque de tracer des trajectoires
from Trottinette import Trottinette
trottinette = Trottinette([O,A,B,G,P1,P2], [R0,R1], [r,h,L,R], cdts, cdtsNG, valnum)
# calcul sur un temps tmax
tmax = float((2*L/ua).subs(valnum))
print("tmax=",tmax)
trottinette.traj2D([A,G,B],t,tmax)
# verification
display("VA=",A.vel(R0).subs(cdtsNG).subs(valnum))
display("VP1=",P1.vel(R0).subs(cdtsNG).subs(valnum).doit())
trottinette.trajP1P2(t,tmax)
Omega, R = sp.symbols("Omega R",positive=True)
cdts = [(psi,Omega*t+sp.pi/2),(ua,Omega*R),(xa,R*sp.cos(Omega*t)),(ya,R*sp.sin(Omega*t)),(phi,phi0)]
display(cdts)
# conditions cinematiques
cdtsGL=[(theta1,omega1*t),(theta2,omega2*t),
(omega1,omega1.subs(cdtsGL1).subs(cdts)),(phi0,phi0.subs(cdtsGL2).subs(cdts).simplify()),
(omega2,omega2.subs(cdtsGL2).subs(cdts).simplify())]
display(cdtsGL)
# position de A et B
display(A.pos_from(O).express(R1).subs(cdts).simplify())
display(B.pos_from(O).express(R1).subs(cdts).simplify())
# et vitesse de A et B
display(A.vel(R0).subs(cdts))
display(B.vel(R0).subs(cdts).simplify())
on prend pour $\omega$ une valeur donnant une vitesse $u_a$ de $\approx 10 km/h$
# parametres numériques en USI
valnum=[(L,1.),(r,0.05),(h,1.),(ua,Omega*R),(R,2),(Omega,10.*0.27/2)]
valnum.append((phi0,phi0.subs(cdtsGL).subs(valnum)))
valnum.append((omega2,omega2.subs(cdtsGL).subs(valnum)))
valnum.append((omega1,omega1.subs(cdtsGL).subs(valnum)))
display(cdts,valnum)
# verification sur la vitesse de contacte de la roue avant
VG2.subs(cdts).subs(cdtsGL).subs(valnum).simplify()
display("OA=",A.pos_from(O).express(R0).subs(cdts).subs(cdtsGL).subs(valnum))
display("OB=",B.pos_from(O).express(R0).subs(cdts).subs(cdtsGL).subs(valnum))
from Trottinette import Trottinette
trottinette = Trottinette([O,A,B,G,P1,P2], [R0,R1], [r,h,L,R], cdts, cdtsGL, valnum)
# tmax 60. pour 1 tour
tmax = float(60./(Omega*30/np.pi).subs(valnum))
print("tmax=",tmax)
trottinette.traj2D([A,G,B],t,tmax)
display("VA=",A.vel(R0).magnitude().subs(cdts).subs(cdtsGL).subs(valnum).doit())
display("VB=",B.vel(R0).magnitude().subs(cdts).subs(cdtsGL).subs(valnum).doit())
display("VP1=",P1.vel(R0).magnitude().subs(cdts).subs(cdtsGL).subs(valnum).doit())
display("VP2=",P2.vel(R0).magnitude().subs(cdts).subs(cdtsGL).subs(valnum).doit())
trottinette.vitesse([A,B,P1,P2],t,tmax/5)
trottinette.traj3D(t,tmax)
display("VA=",A.vel(R0).subs(cdts).magnitude())
display("VB=",B.vel(R0).subs(cdts).magnitude().simplify())
display("VG=",G.vel(R0).subs(cdts).magnitude().simplify())
la trottinette est soumise à une accélération centripède, et donc le conducteur en G ressent une force centrifuge de direction opposée $M\vec{\gamma}(G)$ dans le référentiel $R_1$ lié à la trottinette.
display("A(A)=",A.acc(R0).subs(cdts).simplify())
display("A(G)=",G.acc(R0).subs(cdts).simplify())
display("A(B)=",B.acc(R0).subs(cdts).simplify())
On applique les principes fondamentaux de la mécanique:
En négligeant les forces de frottement, les forces s'appliquant sur la trottinette et son conducteur sont:
En virage, on doit modifier l'orientation de la quantité de mouvement pour créer une accélération centripéde, mais aussi l'orientation du moment cinétique des roues.
Pour cela le conducteur va pencher la trottinette vers l'intérieur du virage en utilisant le guidon pour imprimer une rotation suivant $\vec{R_2.x}$ et pas uniquement tourner le guidon autour de $\vec{R_2.z}$. D'autre part, les roues ont un moment cinétique principalement suivant $\vec{R_2.y}$ dont il faut changer la direction, i.e. le faire tourner autour de $\vec{R_2.z}$. Pour cela il faut appliquer un couple gyroscopique suivant $\vec{R_2.x}$ dans le sens opposé au mouvement de rotation précédent, ce qui limite (mais dans une faible mesure) l'angle de rotation autour de $\vec{R_2.x}$. On constate donc que ce couple gyroscopique a un effet stabilisant, mais qui reste faible dans la pratique.
Pour calculer l'angle $\beta$ d'inclinaison de la trottinette,on suppose que le centre de gravité G (du conducteur) subit une rotation $\beta$ autour de la direction $R_1.x$ (direction de la vitesse), c.a.d le conducteur de masse M se penche à droite ou à gauche.
On se place dans le référentiel lié au conducteur $R_1$, dans lequel le conducteur est immobile. $R_1.x$ est dans la direction de la vitesse, $R_1.y$ est dirigée vers le centre du virage, et $R_1.z$ est vertical.
Les forces qui s'exercent sont:
la force motrice suivant $R_1.x$ (direction de la vitesse) qui permet de vaincre les frottements.
L'équilibre dans $R_1$ impose donc : $$ \vec{P} + \vec{Fr} + \vec{Fc} = \vec{0}$$
beta, M, g, F = sp.symbols('beta M g F')
# force centrifuge
Fc = -M*G.acc(R0).subs(cdts).simplify()
display('force centrifuge=',Fc)
Fc = Fc.dot(R1.y)*R1.y
display('force centrifuge=',Fc)
# force de gravité
P = - M*g*R1.z
# reaction
Fr = F*(sp.cos(beta)*R1.z + sp.sin(beta)*R1.y)
display('réaction du sol=',Fr)
# bilan: somme des forces
S = P + Fr + Fc
display("bilan=",S)
eq1 = sp.Eq(sp.tan(beta),Omega**2*R/g)
eq2 = sp.Eq(F,M*g/sp.cos(beta))
display("solution",eq1,eq2)
Donc le conducteur doit se pencher vers l'intérieur du cercle, i.e. $\beta >0 $, pour contrer la force centrifuge.
L'inclinaison optimale a été obtenue ci-dessus. En supposant que $R\gg L,h$, on peut calculer l'expression de $\beta$ en fonction des paramètres du problème, en particulier $u_a$ et $R$. Mettre le résultat dans la variable bopt, puis calculer sa valeur numérique en degré dans bval en substituant les valeurs numériques des paramètres avec $ g = 9.81$. On calculera la valeur numérique en degré avec 2 chiffres significatifs en utilisant la méthode .evalf(2)
bopt = sp.atan(ua**2/g/R)
bval = (bopt.subs(cdts).subs(valnum).subs(g,9.81)*180/np.pi).evalf(2)
display("Beta opt=",bopt)
print("valeur en degré=",bval)