-- L2 - Aérodynamique --

TN n°2

Profils d'ailes

simon.marie@lecnam.net

Pour ceux qui ne seraient pas familier avec le language python, vous pouvez consulter le Notebook Introduction au Python.

Ce notebook constitue une introduction à l'aérodynamique des profils d'ailes. Il vous permettra de vous familiariser avec les différents profils et leurs construction ainsi qu'avec les grands principes de l'aérodynamique d'une aile.

In [1]:
import numpy as np
import matplotlib.pyplot as plt
import math
import copy
%matplotlib widget
import ipywidgets as widgets
#%matplotlib inline
fs=15
plt.rc('xtick',labelsize=fs)
plt.rc('ytick',labelsize=fs)

1 - Définition d'un profil¶

Profils symétriques¶

Dans le cadre des profils symétriques, les profils NACA sont définis à l'aide d'un polynôme d'ordre 4 donnant l'ordonnée du profil en fonction de l'abcisse. On normalise en général les coordonnées par la corde $c$. On a ainsi:

$$ \dfrac{y_s}{c}=a_0\left(\dfrac{x}{c}\right)^{1/2}+a_1\left(\dfrac{x}{c}\right)+a_2\left(\dfrac{x}{c}\right)^{2}+a_3\left(\dfrac{x}{c}\right)^{3}+a_4\left(\dfrac{x}{c}\right)^{4} $$

Les 5 coefficients de ce polynôme sont calculés en imposant les 5 conditions suivantes dans le cas ou l'on souhaite une épaisseur de 20% de la corde (T/c=0.2):

  1. L'épaisseur du profil est maximum à 30% de la corde. -> y(0.3)=0.1
  2. Le point d'épaisseur max est aussi le point de pente nulle -> $\dfrac{dy}{dx}(0.3)=0$
  3. L'épaisseur au bord de fuite doit être 0.2% de la corde -> y(1)=0.01
  4. La pente de la courbure au bord de fuite doit être égale à 0.234 -> $\dfrac{dy}{dx}(1)=0.234$
  5. La forme du bord d'attaque doit imposer une épaisseur de 7.8% de la corde à x=10% de la corde -> y(0.1)=0.078

Après résolution du système linéaire on trouve:

  • $a_0=0.2969$
  • $a_1=-0.1260$
  • $a_2=-0.3516$
  • $a_3=0.2843$
  • $a_4=-0.1015$

Ainsi on peut définir une fonction calculant les coordonées d'un profil d'aile symétrique d'épaisseur $T$ (Thickness en anglais):

In [2]:
A=np.array([[0.3**0.5,0.3,0.3**2,0.3**3,0.3**4],
            [0.5*0.3**-0.5,1,2*0.3,3*0.3**2,4*0.3**3],
            [1,1,1,1,1],
            [0.5,1,2,3,4],
            [0.1**0.5,0.1,0.1**2,0.1**3,0.1**4]])
B=np.array([0.1,0,0.0021/2,-0.234,0.0078])
np.linalg.solve(A,B)
Out[2]:
array([-1.01600029,  4.06808007, -8.62300818,  8.83597717, -3.26399877])
In [3]:
def naca_00TT(x,T):
    return T*(0.2969*x**0.5-0.1260*x-0.3516*x**2+0.2843*x**3-0.1015*x**4)/0.2

Pour choisir les abscisses où sont calculé les points du profil, on choisi en général une distribution non uniforme pour pouvoir représenter correctement le bord d'attaque.

In [4]:
N=50
db=np.pi/N
beta=np.arange(0,np.pi+db,db)
In [5]:
x=0.5*(1-np.cos(beta))
In [6]:
plt.figure(figsize=(8,3))
plt.plot(x,np.ones(len(x)),'o')
plt.grid()
In [7]:
plt.figure(figsize=(8,3))
plt.plot(x,naca_00TT(x,0.08),'-+k',label='NACA0008');plt.plot(x,-naca_00TT(x,0.08),'-+k')
plt.plot(x,naca_00TT(x,0.12),'-+b',label='NACA0012');plt.plot(x,-naca_00TT(x,0.12),'-+b')
plt.plot(x,naca_00TT(x,0.15),'-+r',label='NACA0015');plt.plot(x,-naca_00TT(x,0.15),'-+r')
plt.axis('equal')
plt.grid();plt.legend(fontsize=fs)
Out[7]:
<matplotlib.legend.Legend at 0x7f23adc20d60>

Profils cambrés¶

Pour pouvoir générer de la portance à incidence nulle, il est nécessaire de briser la symétrie du profil en ajoutant une légère cambrure pour augmenter la dépression à l'extrados.

On introduit alors deux nouveaux paramètres:

  • La cambrure maximum $M$ en pourcentage de la corde
  • L'abscisse de cambrure maximum $P$ en pourcentage de la corde

Le profil est alors découpé en 4 parties:

  • L'extrados avant et après la cambrure max.
  • L'intrados avant et après la cambrure max.

Pour la ligne de cambrure on défini: $$ 0<\left[\dfrac{x}{c}\right]<P ~ : ~ \dfrac{y_c}{c}=\dfrac{M}{P^2}\left(2P\left[\dfrac{x}{c}\right]-\left[\dfrac{x}{c}\right]^2\right) $$

$$ 1>\left[\dfrac{x}{c}\right]>P ~ : ~ \dfrac{y_c}{c}=\dfrac{M}{1-P^2}\left(1-2P+2P\left[\dfrac{x}{c}\right]-\left[\dfrac{x}{c}\right]^2\right) $$
In [8]:
def camb_line(x,M,P):
    yc=np.zeros(len(x))
    dyc=np.zeros(len(x))
    x1=x[x<P]
    x2=x[x>=P]
    if P>0:
        yc[x<P]=(M/P**2)*(2*P*x1-x1**2)
        dyc[x<P]=(2*M/P**2)*(P-x1)
        yc[x>=P]=(M/(1-P)**2)*(1-2*P+2*P*x2-x2**2)
        dyc[x>=P]=(2*M/(1-P)**2)*(P-x2)
    return yc,dyc
In [9]:
yc,dyc=camb_line(x,0.02,0.4)
plt.figure(figsize=(8,3))
plt.plot(x,yc,'-k')
plt.axis('equal')
plt.grid();

On peut alors définir le profil complet en partant d'un profil symétrique et en retranchant l'apaisseur à la ligne de cambrure. On aurra donc:

Pour l'extrados: $$ \left.\dfrac{y}{c}\right|_{extrados}=\dfrac{y_c}{c}+\dfrac{y_s}{c}\cos(\theta) $$

Pour l'intrados: $$ \left.\dfrac{y}{c}\right|_{intrados}=\dfrac{y_c}{c}-\dfrac{y_s}{c}\cos(\theta) $$

Avec $\theta=arctan\left(\dfrac{dy_c}{dx}\right)$

In [10]:
def naca_MPTT_extra(x,M,P,T):
    ys=naca_00TT(x,T)
    yc,dyc=camb_line(x,M,P)
    y=yc+ys*np.cos(np.arctan(dyc))
    return y

def naca_MPTT_intra(x,M,P,T):
    ys=naca_00TT(x,T)
    yc,dyc=camb_line(x,M,P)
    y=yc-ys*np.cos(np.arctan(dyc))
    return y

def plot_NACA(x,M,P,T,col='k'):
    yc,dyc=camb_line(x,M,P)
    ye=naca_MPTT_extra(x,M,P,T)
    yi=naca_MPTT_intra(x,M,P,T)
    return yc,ye,yi

Les profils NACA sont ainsi désignés par un nombre à 4 chiffres NACAMPXX ou M est la cambrure max multiplié par 100, P la position de la cambrure max multiplié par 10 et XX l'épaisseur multiplié par 100.

Ainis le profil NACA2415 sera défini par une épaisseur de 15% de la corde, une cambrure de 2% de la corde positionée à 40% de la corde.

In [11]:
# set up plot
fig, ax = plt.subplots(figsize=(8, 4))
ax.set_ylim([-0.5, 0.5])
ax.grid(True)

@widgets.interact(camb=(0, 0.2, 0.01), pos=(0.1, 0.9, .1), ep=(0.05, 0.5, 0.01))
def update(camb = 0., pos=0.1, ep=0.12):
    """Remove old lines from plot and plot new one"""
    [l.remove() for l in ax.lines]
    [l.remove() for l in ax.lines]
    name='NACA'+str(int(100*camb))+str(int(10*pos))+str(int(100*ep))
    plt.title(name,fontsize=fs)
    yc,ye,yi=plot_NACA(x,camb,pos,ep)
    ax.plot(x, yc, '--k')
    ax.plot(x,ye,'-+r')
    ax.plot(x,yi,'-+r')
    
interactive(children=(FloatSlider(value=0.0, description='camb', max=0.2, step=0.01), FloatSlider(value=0.1, d…

Comparaison aérodynamique¶

Afin d'utiliser les données aéro, il faut d'abord les télécharger en executant la cellule suivante (Attention il faut décommenter les 2 lignes avant -> supprimer le #

In [12]:
# ! wget https://hpp.education/Lessons/Aerodynamique/Files/NACA0015_polair.dat
# ! wget https://hpp.education/Lessons/Aerodynamique/Files/NACA2415_polair.dat

Airfoiltools est un site permettant de comparer les performances aérodynamique des profils d'ailes. Il permet d'obtenir des fichiers de données en colonnes sous la forme:

$\alpha$$C_L$$C_D$$C_{Dp}$$C_M$$X_{tr}^t$$X_{tr}^b$

Les colonnes corespondent à:

  • $\alpha$ incidence en °
  • $C_L$ -> Coefficient de portance (Lift)
  • $C_D$ -> Coefficient de trainée (Drag)
  • $C_{Dp}$ -> Contribution de la trainée de pression
  • $C_M$ -> Coefficient du moment de tangage de l'aile (Pitching moment)
  • $X_{tr}^t$ -> Position de la transition laminaire/turbulent sur l'extrados
  • $X_{tr}^b$ -> Position de la transition laminaire/turbulent sur l'intrados

On peut alors charger les données, on comparera ici le NACA0015 et le NACA2415:

In [13]:
NACA1=np.loadtxt('NACA0015_polair.dat')
NACA2=np.loadtxt('NACA2415_polair.dat')

Coeff aéro¶

On peut commencer par tracer l'évolution des coefficients aérodynamique $C_D$ et $C_L$ en fonction de l'incidence:

In [18]:
fig=plt.figure(figsize=(8,4),dpi=120)
fig.add_subplot(121)
plt.plot(NACA1[:,0],NACA1[:,1],linewidth=3,label='NACA0015')
plt.plot(NACA2[:,0],NACA2[:,1],linewidth=3,label='NACA2415')
plt.xlabel(r'Incidence (°)',fontsize=fs)
plt.ylabel(r'CL',fontsize=fs)
plt.grid(True)
plt.legend(fontsize=fs)
fig.add_subplot(122)
plt.plot(NACA1[:,0],NACA1[:,2],linewidth=3,label='NACA0015')
plt.plot(NACA2[:,0],NACA2[:,2],linewidth=3,label='NACA2415')
plt.xlabel(r'Incidence (°)',fontsize=fs)
plt.ylabel(r'CD',fontsize=fs)
plt.grid(True)
plt.legend(fontsize=fs)
Out[18]:
<matplotlib.legend.Legend at 0x7f593ae64640>

La polaire d'un profil est la courbe représentant le coefficient de portance en fonction du coefficient de trainée pour différentes incidences.

On peut comparer directement les polaires des deux profils:

In [16]:
fig=plt.figure(figsize=(7,3),dpi=120)
plt.plot(NACA1[:,2],NACA1[:,1],linewidth=3,label='NACA0015');plt.plot(np.arange(0,0.02,0.001),77.8*np.arange(0,0.02,0.001),'--b')
plt.plot(NACA2[:,2],NACA2[:,1],linewidth=3,label='NACA2415');plt.plot(np.arange(0,0.02,0.001),103*np.arange(0,0.02,0.001),'--r')
plt.xlabel(r'CD',fontsize=fs)
plt.ylabel(r'CL',fontsize=fs)
plt.grid(True)
plt.legend(fontsize=fs)
Out[16]:
<matplotlib.legend.Legend at 0x7f594113e2e0>

Finesse¶

La finesse d'une aile représente le rapport entre son coefficient de portance et son coefficient de trainée. Elle exprime donc une efficacité aérodynamique entre portance et trainée. La finesse maximum est obtenue pour une incidence donnée qu'il sera important de connaitre pour l'utilisation du profil.

On peut donc connaitre la finesse max en faisant simplement ici:

In [15]:
print('Finesse max NACA0015',np.max(NACA1[:,1]/NACA1[:,2]))
print('Finesse max NACA2415',np.max(NACA2[:,1]/NACA2[:,2]))
Finesse max NACA0015 77.87696924231058
Finesse max NACA2415 103.04444444444445

On peut également représenter l'évolution de la finesse en fonction de l'incidence:

In [17]:
fig=plt.figure(figsize=(7,3),dpi=120)
plt.plot(NACA1[:,0],NACA1[:,1]/NACA1[:,2],linewidth=3,label='NACA0015');plt.plot(NACA1[:,0],77.8*np.ones(len(NACA1[:,0])),'--b')
plt.plot(NACA2[:,0],NACA2[:,1]/NACA2[:,2],linewidth=3,label='NACA2415');plt.plot(NACA2[:,0],103*np.ones(len(NACA2[:,0])),'--r')
plt.ylabel(r'f=CL/CD',fontsize=fs)
plt.xlabel(r'incidence',fontsize=fs)
plt.grid(True)
plt.legend(fontsize=fs)
Out[17]:
<matplotlib.legend.Legend at 0x7f5940092a30>

Trainée de pression¶

In [21]:
fig=plt.figure(figsize=(7,3),dpi=120)
plt.plot(NACA1[:,0],100*NACA1[:,3]/NACA1[:,2],linewidth=3,label='NACA0015')
plt.plot(NACA2[:,0],100*NACA2[:,3]/NACA2[:,2],linewidth=3,label='NACA2415')
plt.xlabel(r'indicence',fontsize=fs)
plt.ylabel(r'\% $CD_p/CD$',fontsize=fs)
plt.title(r'Contribution de la trainée de pression à la trainée totale',fontsize=fs)
plt.grid(True)
plt.legend(fontsize=fs)
Out[21]:
<matplotlib.legend.Legend at 0x7f593acfc5b0>

Transition Laminaire/Turbulent¶

In [24]:
fig=plt.figure(figsize=(7,3),dpi=120)
plt.plot(NACA1[:,0],NACA1[:,5],linewidth=3,label='Top NACA0015')
plt.plot(NACA1[:,0],NACA1[:,6],linewidth=3,label='Bottom NACA0015')
plt.plot(NACA2[:,0],NACA2[:,5],linewidth=3,label='Top NACA2415')
plt.plot(NACA2[:,0],NACA2[:,6],linewidth=3,label='Bottom NACA2415')
plt.title(r'Position du point de transition par rapport à la corde',fontsize=fs)
plt.xlabel(r'indicence',fontsize=fs)
plt.ylabel(r'Xtr',fontsize=fs)
plt.grid(True)
plt.legend(fontsize=fs)
Out[24]:
<matplotlib.legend.Legend at 0x7f593a8b6eb0>

Conclusion¶

Dans ce TN, nous avons apris a représenter la géométrie d'un profil d'ail à partir de ses 4 chiffres. Nous avons ensuite pu comparer les performances aérodynamique de quelques profil à partir d'informations issues d'une base de données aérodynamique Airfoiltools.

Pour aller plus loin, on pourra consulter les documents suivant:

  • Profils d'ailes
  • Le site d'Airfoiltools
  • Le logiciel Xfoil
In [25]:
from IPython.core.display import HTML
style=open('notebooks.css', "r").read()
HTML(style)
Out[25]:
In [ ]: