Activer le mode zen
Ressource au format PDF

Mettre en évidence les deuxième et troisième lois de Kepler à l'aide de codes en langage python

13/10/2025

David Alberto

Professeur de physique-chimie au lycée Françoise de Grâce, Le Havre, membre du Comité de Liaison Enseignants et Astronomes

Delphine Chareyron

ENS de Lyon

Delphine Chareyron

ENS de Lyon

Résumé

Cet article propose des codes en langage python afin de mettre en évidence les deuxième et troisième lois de Kepler à l'aide des coordonnées astronomiques d'astres appartenant au Système solaire.


1. Kepler

Johannes Kepler est né en 1571 dans le comté de Wutemberg, dans le sud de l'Allemagne. Il fait des études de théologie et poursuit sa formation à l'université [1]. En 1591, il accepte un poste de professeur d'astronomie dans la ville de Grazt (en Autriche). C'est à partir de là qu'il va commencer à s'intéresser au système héliocentrique présenté par Copernic. Il rédige ses recherches sur les liens entre les planètes du système solaire (nombre de planètes, tailles et mouvement des planètes sur leur orbite) dans l'ouvrage « Mysterium Cosmographicum », publié en 1795. Il envoie des copies de ce livre à notamment Galilée et Tycho Brahe, même si ce dernier n'utilise pas le système copernicien. Tycho Brahe, enthousiasmé par ses travaux, lui propose de les adapter à son système, d'utiliser ses observations réalisées dans son observatoire (Uraniborg) pour obtenir plus de précision sur les orbites des planètes et de venir lui rendre visite au Danemark [2].

En 1601 Tycho Brahe introduit Kepler à l'Empereur du Danemark qui lui donne le titre de mathématicien impérial. Les deux hommes travailleront peu ensemble puisque Tycho Brahe décédera la même année. Kepler le remplace à son poste et hérite de toutes ses mesures.

Les deux premières lois de Kepler sont publiées en 1609 dans « Astronomia nova ». La première loi de Kepler, aussi appelée loi des orbites, indique que les planètes décrivent des trajectoires elliptiques autour de l'un des foyer occupé par le Soleil. La deuxième loi, encore appelée loi des aires, met en évidence que le rayon-vecteur Solaire-astre balaie des aires égales pendant des durées égales.

La troisième loi, donnant le rapport entre la longueur du demi-grand axe et la période de révolution, est publiée en 1618 dans « Harmonices Mundi » [1].

Les écrits de Kepler ont été numérisés et ont ainsi été rendus accessibles à tous, via la bibliothèque numérique patrimoniale Numistral [3]. Il sera néanmoins nécessaire de maîtriser le latin pour profiter de ces archives.

La figure 1 présente les statues de Tycho Brahe (à gauche) et Johannes Kepler (à droite). Cette œuvre est située dans la ville de Prague. Au sujet des représentations de Kepler, il est amusant d'évoquer la controverse liée à une peinture qui lui serait assimilée par erreur et largement répandue [4]. En effet, il a été montré que depuis 2013 et jusqu'à aujourd'hui, le premier portrait proposé par le moteur de recherche Google à la requête « Kepler portrait » ne lui correspond pas. On pourra retrouver les détails de la propagation de cette mauvaise attribution dans l'article « How a fake Kepler portrait became iconic » [4].

2. Mise en évidence de la deuxième loi de Kepler à l'aide d'un programme python

Dans cette partie, nous mettons en évidence la deuxième loi de Kepler.

  • Le segment de droite qui relie le soleil à une planète parcourt des aires égales dans des temps égaux.

Ce script s'appuie sur l'article de Thomas Appéré (BUP 1063) [5].

Dans un premier temps, on récupère les positions héliocentriques de différents astres (planètes, comète) à partir de données fournies par le portail d'éphémérides de l'IMCCE : https://ssp.imcce.fr/forms/ephemeris. Après avoir tracé la trajectoire d'un astre, on trace trois secteurs angulaires décrits par l'astre sur un intervalle de temps de même durée, en trois positions où sa vitesse est différente. On calcule les aires balayées par le rayon vecteur issu du Soleil.

Pour comparer les aires balayées, on choisit trois positions de l'orbite : le périhélie (position correspondant à la distance la plus proche du Soleil), l'aphélie (position correspondant à la distance la plus éloignée du Soleil) et une position quelconque. Dans les deux premiers cas, ce sont les positions donnant le plus grand écart de vitesse de l'astre et donc de distance parcourue en un temps donné.

2.1 Positions héliocentriques sur le site de l'IMCCE

Sur le portail d'éphémérides de l'IMCCE [6], on ouvre la page « Éphémérides de position », figure 2.

On sélectionne un corps du Système solaire : par exemple Mercure, Mars, ou encore la comète périodique 2P/Encke qui possède une période de quelques années adaptée à cet exercice.

On choisit la date initiale, le nombre de dates à calculer et le pas temporel. Ces paramètres sont importants pour la suite et doivent être adaptés à la période de l'astre choisi. Pour Mars : 1 calcul par jour pendant 700 jours permet d'avoir une précision correcte, car une année martienne dure environ 686,98 jours solaires. Pour Mercure, on pourra prendre un incrément de 1 heure seulement, et environ 90 jours (une année sur Mercure dure environ 87,97 jours).

Pour les autres champs à renseigner, on choisit :

  • Système de coordonnées : géocentre
  • Plan de référence : écliptique
  • Type d'éphémérides : astrométrique J2000
  • Coordonnées : cartésiennes

Après avoir cliqué sur « Calculer », on obtient un tableau des positions de l'astre, que l'on peut exporter dans un fichier au format .csv (comma separated values). On renomme ce fichier du nom de l'astre choisi pour l'enregistrer dans le dossier où se trouvera le script Python. Ce fichier comporte une ligne par date calculée, et plusieurs paramètres en colonnes, dont les coordonnées cartésiennes de la position de l'astre.

Nous proposons ici, de mettre à disposition les fichiers contenants les données astronomiques de Mercure (mercure.csv [résolution de une heure, durée de 89 jours à partir du 1er janvier 2025]), Mars (mars.csv [résolution de un jour, durée de 700 jours à partir du 1er janvier 2025]), et de la comète Encke (comete_2PEncke.csv [résolution de 6 heures, durée de 3,3 ans à partir du 1er janvier 2025]) pour lesquels les calculs et illustrations sont présentés dans la suite de l'article.

2.2 Présentation du script et calcul de l'aire en utilisant la position et l'angle de l'astre

On importe les bibliothèques python nécessaires au traitement des données et à l'affichage des graphiques. Nous importons ici les données du fichier .csv avec la bibliothèque « pandas ».

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd # import de données et gestion en tableau

On importe les données du fichier .csv (coordonnées de la planète Mercure) :

nom_astre = 'Mercure'
astre = pd.read_csv(
    'mercure.csv',
    sep=';',
    usecols=[0, 1, 2, 3, 4],  # sélection des colonnes utiles
    names = ['date', 'X', 'Y', 'Z', 'distance'],  # en-têtes
    skiprows=1,  # pour passer la 1e ligne qui contient les noms des grandeurs
    )
nom_fichier = 'Kepler_loi_aires_' + nom_astre
mi_temps = 60  # demi-durée de l'intervalle de temps, (à adapter en fonction de l'astre étudié)

#Ici on calculera l'aire balayée autour du périhélie, de l'aphélie et d'un point quelconque 
#de l'orbite de Mercure pendant 120 h à raison de 60 h avant et 60 h après.

Aperçu de la variable « astre » (un tableau de type DataFrame), contenant les éphémérides de Mercure à partir du 1er janvier 2025 pour 700 jours avec un pas de temps de 1 heure

Figure 3. Aperçu de la variable « astre » (un tableau de type DataFrame), contenant les éphémérides de Mercure à partir du 1er janvier 2025 pour 700 jours avec un pas de temps de 1 heure

La première colonne allant de 0 à 2150 est l'index du tableau ; c'est un moyen d'accéder facilement à une ligne donnée.


Pour calculer l'aire balayée par l'astre sur un temps donné, nous disposons, à l'aide du fichier des éphémérides, des positions successives de l'astre dans le temps. Pour Mercure, nous avons choisi un pas d'échantillonnage d'une heure sur sa période de révolution (un peu moins de 88 jours). L'astre passe ainsi de sa position décrite en coordonnées cartésiennes par le point (xn, yn, zn) au temps tn au point (xn+1, yn+1, zn+1) au temps tn+1 en une heure.

À l'aide du calcul de l'angle \( d\theta \) entre deux positions successives \( n \) et \( n+1 \), on peut remonter à une approximation de la longueur \( dL \) de l'arc de l'orbite balayée. On assimile la portion d'arc à celle d'un cercle de rayon \( r_n \). La longueur de la portion d'arc s'écrit alors \( dL = d\theta \times r_n\). Une telle approximation n'est raisonnable que si la variation entre les deux instants est très faible devant la période du mouvement.

Ainsi la portion d'aire \(dA\) balayée pendant la durée \(dt\) peut être assimilée à \( dA_n = \frac{1}{2} r^2_n \times d\theta\), à chaque pas de temps.

Pour le calcul des aires balayées autour des points choisis (aphélie, périhélie, point quelconque), on définit une période temporelle (pour Mercure 120 h) centrée sur chaque point (60 h avant et 60 h après).

Revenons au code, il faut maintenant définir plusieurs fonctions (calcul de l'angle \( d\theta \) et calcul de l'aire), ainsi que les coordonnées du périhélie et de l'aphélie.

def calcul_angle(x, y):
    """
   x, y : composantes des positions de l'astre.
    Renvoie l'angle en radians (float).
    Cet angle est la longitude écliptique héliocentrique de l'astre.
    """

    angle = np.arctan2(y, x)
    return angle

Notons ici que nous choisissons de travailler dans le plan de l'écliptique, ainsi, on n'utilisera pas la composante z de l'astre. Cet argument est légitime pour les astres tels que Mercure et Mars dont le plan de l'orbite dévie très peu de l'axe de l'écliptique.

Remarque : avec matplotlib, les angles du graphe polaire sont en radians, mais les graduations sont affichées en degrés.

def coord_perihelie(r):
    """
   Renvoie les coordonnées (angle, rayon, index) du périhélie (float).
    Angle : radians. rayon : ua.
    index : entier.
    """

    r_peri = min(r)
    idx_peri = np.where(y==r_peri)[0][0]
    x_peri = x.iloc[idx_peri]
    return x_peri, r_peri, idx_peri

def coord_aphelie(r):
    """
   Renvoie les coordonnées (angle, rayon) de l'aphélie (float).
    Angle : radians. rayon : ua.
    index : entier.
    """

     r_aphelie = max(r)
    idx_aphelie = np.where(y==r_aphelie)[0][0]
    x_aphelie = x.iloc[idx_aphelie]
    return x_aphelie, r_aphelie, idx_aphelie

def calcul_aire(indice_date_debut, couleur='gray'):
    """
    indice_date_debut : indice du début du comptage
    On calcule l'aire balayée par l'astre depuis la date de début pendant l'intervalle de temps.
    Le calcul cumule les aires dA des triangles formés.
    On trace les rayons vecteurs pour chaque date, pour matérialiser le secteur.
    """
    aire = 0
    dA=0
    Aires=[0]
    for i in range(indice_date_debut, indice_date_debut+2*mi_temps):
        r1 = astre.position.iloc[i]
        a =np.abs(astre.angle.iloc[i]-astre.angle.iloc[i+1])
      
        dA = 1/2 * np.linalg.norm(r1)**2*a
        
        aire = aire + dA # la variable aire cumule la somme des aires dA
        Aires.append(dA)  # la variable Aires stocke l'aire dA, calculée à chaque pas de temps
        
        # tracé du rayon vecteur :
        ax.plot([0, astre.angle.iloc[i]], [0, astre.distance.iloc[i]],
                c=couleur, zorder=0)
    return aire, Aires

On calcule les coordonnées polaires avec une commande du module pandas. On utilise les fonctions correspondantes au tableau (dataframe) ‘astre', et on stocke les résultats dans des nouvelles colonnes du même tableau :

astre['angle'] = astre.apply(lambda df: calcul_angle(df.X, df.Y), axis=1)
astre['position'] = astre.apply(lambda df: (df.X, df.Y, df.Z), axis=1)  

On utilise ‘df' pour travailler dans la dataframe et le nom de la variable temporaire pour le tableau de données.

On prépare l'affichage de la figure :

fig = plt.figure(figsize=(8, 8),
                 tight_layout = True)

ax = plt.subplot(111, projection='polar')
ax.set_yticklabels([])  # suppression des graduations radiales
ax.set_xticklabels([])  # suppression des graduations angulaires
ax.set_title(fr'2$^e$ loi de Kepler : loi des aires ({nom_astre})', fontsize=16,
             fontweight='bold', loc='left')

# point pour le Soleil :
ax.scatter(0,0, color='gold', s=50, ec='orangered', lw=0.4,
           label='Soleil')

# Paramètres à personaliser 
couleur_peri = 'mediumslateblue'  # couleur pour le périhélie
couleur_aphelie = 'darkorange'  # couleur pour l'aphélie

On trace la trajectoire :

x = astre.angle
y = astre.distance
ax.plot(x, y, lw=2, c='dodgerblue', label="orbite de l'astre")

#  Calcul et tracé du point du périhélie
theta_peri, r_peri, index_peri = coord_perihelie(astre.distance)
ax.scatter(theta_peri, r_peri, zorder=3, s=30, label='périhélie',
           c=couleur_peri, ec='k', lw=0.2)

# Calcul et tracé du point de l'aphélie
theta_aphelie, r_aphelie, index_aphelie = coord_aphelie(astre.distance)
ax.scatter(theta_aphelie, r_aphelie, zorder=3, s=30, label='aphélie',
           c=couleur_aphelie, ec='k', lw=0.2)

# Calcul et tracé d'un point quelconque de la trajectoire
signe = int(np.sign(np.random.random()-0.5))  # +1 ou -1, pour le tirage aléatoire
if index_aphelie > index_peri:
    index_rand = np.random.randint(index_peri+2*mi_temps,index_aphelie-2*mi_temps)
else:
    index_rand = np.random.randint(index_aphelie+2*mi_temps,index_peri-2*mi_temps)

# redéfinition de la position du point aléatoire d'un côté ou de l'autre de la trajectoire :
index_rand = abs(index_aphelie +2*mi_temps + signe * (abs(index_aphelie-index_rand)))

angle_rand = astre.angle.iloc[index_rand]
dist_rand = astre.distance.iloc[index_rand]
ax.scatter(angle_rand,dist_rand, s=30, label='point quelconque', c='yellowgreen', ec='k', lw=0.2,
           zorder=3)

Pour le calcul de l'aire autour d'un point quelconque, le code tire au hasard une valeur d'index dans le tableau 'astre' contenant les coordonnées au cours du temps. La variable 'signe' sert à ce que le point aléatoire soit tantôt d'un côté tantôt de l'autre de l'ellipse, mais ce n'est pas indispensable. Le tirage est fait de sorte que l'aire de ce troisième point ne se superpose pas aux deux autres.

On calcule les aires balayées autour du périhélie (indice 1), de l'aphélie (indice 2) et d'un point quelconque de l'orbite (indice 3) :

index_debut1 = index_peri - mi_temps
aire1, Aires1 = calcul_aire(index_debut1, couleur=couleur_peri)

index_debut2 = index_aphelie - mi_temps
aire2, Aires2 = calcul_aire(index_debut2, couleur=couleur_aphelie)

index_debut3 = index_rand - mi_temps
aire3, Aires3 = calcul_aire(index_debut3, couleur= 'yellowgreen')

On affiche les aires calculées sur la figure :

# Affichage des aires 
# Paramètres à personaliser 

couleur_peri = 'mediumslateblue'  # couleur pour le périhélie
couleur_aphelie = 'darkorange'  # couleur pour l'aphélie

ax.text(0.01, 0.0, f'Aire 1 : {aire1:.5} UA$^2$', c=couleur_peri,
        transform=ax.transAxes, fontweight='bold', fontsize=12)
ax.text(0.99, 0.0, f'Aire 2 : {aire2:.5} UA$^2$', c=couleur_aphelie,
        ha='right',transform=ax.transAxes, fontweight='bold', fontsize=12)
ax.text(0.01, 0.0, f'Aire 3 : {aire3:.5} UA$^2$', c='yellowgreen',
        transform=ax.transAxes, fontweight='bold', fontsize=12)

Remarque : le texte est affiché en coordonnées de la fenêtre graphique entre 0 et 1.

On finit la mise en page de la figure et on l'enregistre.

ax.grid(False)  # suppression de la grille
ax.legend(loc='upper right')  # légende
plt.show()
# Création de fichiers images :
fig.savefig(nom_fichier + '.png', dpi=400)
fig.savefig(nom_fichier + '.pdf')

Remarques : On observe que l'on a une bonne concordance des aires calculées, avec une différence à 10-6 UA2 pour les aires au périhélie et à l'aphélie. Comme on pouvait s'y attendre, l'aire calculée autour du périhélie est plus grande. En effet, la longueur d'arc assimilée à un cercle surestime la longueur d'arc de l'ellipse.

On a mis en évidence, de manière très schématique, cet effet sur la figure 6. En noir, nous avons représenté une orbite elliptique de foyers : le Soleil et le point F. Nous avons annoté le périhélie et l'aphélie. En bleu nous avons tracé un cercle de centre le Soleil et de rayon la distance Soleil-Index_p (représentant par exemple le point à partir duquel on va calculer l'aire aurour du périhélie). En violet, nous avons tracé un cercle de centre le Soleil et de rayon la distance Soleil-Index_A (même chose, autour de l'aphélie).

Pour l'aire calculée autour d'un point quelconque de la trajectoire, on trouve une valeur beaucoup plus grande, figure 5. En effet, on se rend compte qu'en fonction du point de l'orbite, l'approximation de la longueur d'arc d'ellipse par un arc de cercle va soit sous-estimer soit surestimer la portion d'ellipse, figure 7.

La figure 8 présente la trajectoire et le calcul des aires balayées pour la planète Mars sur une durée de 40 jours autour du périhélie, de l'aphélie et d'un point quelconque. Le pas de résolution est de 1 jour. Comme l'excentricité de l'orbite est assez faible, on a une bonne concordance des trois mesures d'aires.

La figure 9 présente la trajectoire et le calcul des aires balayées pour la planète comète PEncke sur une durée de 4 jours autour du périhélie, de l'aphélie et d'un point quelconque. Le pas de résolution est de 6 heures. Si l'on augmente la durée de l'aire balayée avec ce pas de résolution, on devine que l'approximation de l'aire dans la zone du périhélie sera beaucoup plus entachée d'erreur.

Avec cette approximation de la mesure d'arc d'ellipse balayé, on observe que l'on va toujours surestimer ou sous-estimer l'aire en fonction d'où on se place sur l'orbite, figure 7. On pourrait ainsi raffiner la méthode en faisant une moyenne sur le vecteur position \(r\), en prenant par exemple \(r= \frac{1}{2} (r_n+r_{n+1})\)

2.3 Calcul de l'aire avec la position et la vitesse de l'astre

Pour le calcul de l'aire, on peut aussi utiliser la vitesse de l'astre sur le long de son orbite. À l'aide des positions de l'astre sur la trajectoire, il est facile de remonter au vecteur vitesse pour chaque pas de temps.

Dans le code, dans la définition de la fonction de calcul d'aire, il suffit de rajouter une ligne pour calculer le vecteur vitesse (vit) en coordonnées cartésiennes :

r1 = astre.position.iloc[i]
r2 = astre.position.iloc[i+1]
      
vit=tuple(map(lambda i, j: i - j, r2,r1))

Les vecteurs r1 et r2 sont deux tuples \((x,y,z)\). Pour soustraire membre à membre ces deux tuples, on utilise la fonction map(fonction lambda, tuples) qui permet d'appliquer ici la fonction lambda (anonyme, que l'on définit) à chaque élément des tuples (comme une itération). La fonction lambda définie ici correspond à la soustraction membre à membre de ces deux tuples (index \(i\) du tuple r2 moins index \(j\) du tuple r1). On obtient ainsi une vitesse \((v_x,v_y,v_z)\), pour Mercure en U.A./h puisque le pas d'échantillonnage est de 1 heure.

Il faut ensuite prêter attention aux angles entre le vecteur position de l'astre et son vecteur vitesse. En effet, on ne peut pas simplement choisir de faire le produit de la norme des deux vecteurs. Nous représentons très schématiquement sur la figure 10 deux cas représentatifs.

À l'aphélie (ou au périhélie), le vecteur vitesse est quasiment perpendiculaire au vecteur position, ainsi, réaliser le calcul de l'aire en l'assimilant à sa norme \( dA = \frac{1}{2} \times \lVert \vec{v_m} \rVert \times \lVert \vec{r_m} \rVert \times dt \) est une approximation correcte.

Par contre, pour d'autres points de la trajectoire, cette approximation n'est plus du tout justifiable, car elle va surestimer ou sous-estimer la portion d'aire \(dA\) à prendre en compte.

Il est alors nécessaire de calculer l'angle alpha entre le vecteur vitesse et le vecteur position afin de pouvoir calculer l'aire correspondant à la moitié du parallélogramme, figure 11.

On a alors \( dA = \frac{1}{2} \times \lVert \vec{v_n} \rVert \times \lVert \vec{r_n} \rVert \times dt \times \sin(\alpha)\)

La fonction définissant le calcul de l'aire dans le code est alors :

def calcul_aire(indice_date_debut, couleur='gray'):
    """
    indice_date_debut : indice du début du comptage
    Calcule l'aire balayée par l'astre depuis la date de début pendant l'intervalle de temps.
    Le calcul cumule les aires des triangles formés par le rayon vecteur et par le vecteur vitesse.
    Trace les rayons vecteurs pour chaque date, pour matérialiser le secteur.
    """
  
    aire=0
    vit=0
    ang=0
    dA_vit=0
    Aires =[0]
    for i in range(indice_date_debut, indice_date_debut+2*mi_temps):
        r1 = astre.position.iloc[i]
        r2 = astre.position.iloc[i+1]
        
        vit=tuple(map(lambda i, j: i - j, r2,r1))
        ang=np.abs(np.arccos(np.dot(r1,vit)/(np.linalg.norm(vit)*np.linalg.norm(r1))))
        
        dA_vit = 1/2 * np.linalg.norm(r1)*np.linalg.norm(vit)*np.sin(ang)

        aire = aire + dA_vit
        Aires.append(dA_vit)
        
        # tracé du rayon vecteur :
        ax.plot([0, astre.angle.iloc[i]], [0, astre.distance.iloc[i]],
                c=couleur, zorder=0)
    return aire, Aires

Pour le calcul de l'aire, nous utilisons la fonction np.linalg.norm() intégrée dans la bibliothèque d'algèbre linéaire de numpy. Cette fonction permet de calculer la norme d'un vecteur.

Nous obtenons, pour le même pas de temps (1 h) et la même durée que précédemment (120 h), pour la planète Mercure, des aires proche à 10-6 UA2 près, figure 12.

De la même manière, on calcule les aires balayées avec les mêmes durées pour Mars et pour la comète PEncke, figures 13 et 14.

Sur une orbite très elliptique, telle que celle de la comète, le calcul de l'aire balayée basé sur les vecteurs position et vitesse est beaucoup plus robuste que celui présenté avec l'approximation de la longueur de l'arc de cercle (section 2.2). En effet, on peut largement augmenter la durée de l'aire balayée, par exemple à 20 jours, figure 12, on obtient encore des valeurs très proches à 10-4 UA2.

Remarque : pour les calculs présentés ici, on aurait pu directement utiliser le produit vectoriel. Ici le détail avec le calcul de l'angle est donné comme un guide pédagogique et dépendant de l'avancement en connaissances mathématiques des élèves.

L'aire du parallélogramme, (figure 11, est directement la norme du produit vectoriel entre le vecteur vitesse \( v \)dt et le vecteur position \(r\). Ainsi, dans le code en python, cela revient à écrire la portion d'aire : dA = 1/2 * np.linalg.norm(np.cross(vit,r1))

Finalement, pour le calcul de l'aire, on aurait aussi pu utiliser la formule de Heron.

Programmes python :

Télécharger le programme python pour retrouver la deuxième loi de Kepler en utilisant la position et l'angle de l'astre, application à la planète Mercure :

Télécharger le programme python pour retrouver la deuxième loi de Kepler en utilisant la position et la vitesse de l'astre, application à la planète Mercure :

Télécharger le programme python pour retrouver la deuxième loi de Kepler en utilisant la position et la vitesse de l'astre, application à la planète Mars (.py) : « 2-loi-kepler_Alberto_Chareyron_CSphysique_mars_vit.py »

Télécharger le programme python pour retrouver la deuxième loi de Kepler en utilisant la position et la vitesse de l'astre, application à la comète PEncke (.py) : « 2-loi-kepler_Alberto_Chareyron_CSphysique_comete_vit.py »

Attention, l'angle des astres est repréré en radian et varie de -\(\pi\) à \(\pi\). Dans les codes proposés ci-dessus, si le point quelconque tombe (inopinément) proche de ce changement de signe de l'angle, le calcul sera faussé.

3. Mise en évidence de la troisième loi de Kepler à l'aide d'un programme en langage python

Dans cette partie, à l'aide des paramètres astronomiques des planètes du système solaire, on cherche le demi-grand axe et la période orbitale des planètes, pour mettre en évidence la troisème loi de Kepler.

  • Le carré de la période sidérale T d'une planète est directement proportionnel au cube du demi-grand axe a de la trajectoire elliptique de la planète.

Comme pour la loi des aires, nous allons nous servir d'un fichier d'éphémérides pour accéder aux positions des planètes autour du soleil ainsi qu'à leur période de révolution. Pour cela, nous installons la bibliothèque skyfield.

Dans un terminal, pour installer cette bibliothèque, il suffit de lancer la commande :

pip install skyfield

On importe les bibliothèques python nécessaires au traitement des données et à l'affichage des graphiques.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches # importation d'un affichage de rectangle (utile pour le tableau)

Le laboratoire JPL (NASA's Jet Propulsion Laboratory) de la NASA met à disposition des tables regroupant les éphémérides des planètes et lunes du système solaire, à l'aide du module skyfield.api. On importe les données astronomiques (skyfield).

import skyfield
from skyfield.api import load
from skyfield.elementslib 
import osculating_elements_of

On utilise la bibliothèque de temps de l'objet ts. De nombreuses options sont proposées : UTC (coordinated universal time), différentes bases de temps, différents formats calendrier jours-mois-années, TAI (international atomic time), etc.

ts = load.timescale() # échelle de dates Skyfield
t = ts.now() # date de la compilation 

On charge le fichier qui contient les éphémérides de421.bsp. Télécharger : de421.bsp (16,8 Mo).

Ce fichier contient les coordonnées (x,y,z) des corps du système solaire comme le Soleil, les planètes, les lunes, figure 16. On crée les listes correspondant aux planètes du système solaire.

planets = load('de421.bsp') # charge un fichier d'éphémérides (valable 1900-2050)
soleil = planets['Sun']
listeplanetes=['mercury','venus','earth','mars']+['jupiter','saturn','uranus','neptune']
listePlanetesFR = ['Mercure','Vénus','Terre', 'Mars']+['Jupiter', 'Saturne', 'Uranus', 'Neptune']

On vient remplir deux listes correspondant aux périodes orbitales T et aux demi-grands axes a. On définit la position de chaque planète depuis le centre du Soleil. À l'aide de la fonction Osculating_elements_of, on récupère ces données que l'on stocke dans l'objet elements. Pour chacune des planètes, on a ainsi accès à ses paramètres orbitaux, figure 17.

# Création des listes de valeurs des périodes orbitales T et des demi-grands axes a :
liste_T=[] 
liste_a=[] 

for nom in listeplanetes:
    planete=planets[nom+' barycenter']
    position=soleil.at(t).observe(planete) # on définit la position de la planète depuis le centre du Soleil
    elements = osculating_elements_of(position) # calcul de a et T de la planète 
    a = elements.semi_major_axis.au
    liste_a.append(a)
    T = elements.period_in_days/365.25 # conversion de T en années terrestres
    liste_T.append(T)

On trace un tableau qui regroupe les valeurs des demi-grands axes (a en unité astronomique) des orbites des planètes du système solaire et leur période de révolution autour du Soleil (T en année terrestre), figure 18.

# Affichage d'un tableau qui regroupe les paramètres a et T des planètes du système solaire :
fig,axs = plt.subplots()

fig.suptitle('Demi-grands axes (a) et périodes de révolution des planètes (T) du système solaire',
             fontsize=14, fontweight='bold')
ax.set_ylim(-2.5,len(listePlanetesFR))
ax.invert_yaxis()  # on inverse le sens de variation en y
ax.set_xlim(-1,2.5)
ax.set_xticks([]) # on efface les graduations de l'axe x
ax.set_yticks([]) # on efface les graduations de l'axe y
ax.text(-0.5,-1.5,"Planète",fontweight='bold')
ax.text(0.5,-1.5,"a (u.a.)", fontweight='bold')
ax.text(1.5,-1.5,"T (années)",fontweight='bold')
ax.plot([-0.5,2.05],[-1,-1],lw=0.7,c='k')

for N in range(len(listePlanetesFR)):
    ax.text(-0.5,N,listePlanetesFR[N])
    ax.text(0.5,N,'%.3f'%(liste_a[N]))
    ax.text(1.5,N,'%.3f'%(liste_T[N]))
    # Affichage d'une bande bleue en fond (planètes de rang pair)
    if N%2==0:
        ax.add_patch(patches.Rectangle(
       (-0.5, N+0.25),2.55,-1,
       facecolor = 'lightsteelblue', alpha=0.5,zorder=-1,) )

Dans un premier temps, on peut tracer la période en fonction du demi-grand axe pour chaque planète, figure 19.

# Représentation de la période en fonction du demi-grand axe de chaque planète :
fig = plt.figure()
ax1 = fig.add_subplot()

prop_scatter=dict(marker='o',s=15, edgecolor='k') # choix graphique de la taille des points 
                                                 #(marqueurs ronds, bleus de taille 15 avec cerclage noir)
ax1.scatter(liste_a,liste_T,**prop_scatter)
ax1.set_xlabel("demi-grand axe a (u.a.)")
ax1.set_ylabel("période orbitale T (années terrestres)")
plt.grid()

Lorsque l'on cherche un ajustement pour une loi en puissance, il est utile de passer par une représentation logarithmique. En effet, le coefficient directeur de la droite d'ajustement donne le rapport des puissances, figure 20.

# Représentation des données à l'aide d'une échelle logarithmique pour retrouver la loi en puissance :
 
fig = plt.figure()
ax2 = fig.add_subplot()
prop_scatter=dict(marker='o',s=15, edgecolor='k') # choix graphique des points

ax2.loglog(liste_a,liste_T, color='green', marker='.', linestyle='none') # tracé des points
# Pour chaque point on ajoute l'étiquette de la planète
for i, txt in enumerate(listePlanetesFR):
       ax2.annotate(txt, (liste_a[i]+10**(liste_a[i]/50)-1, liste_T[i]), color='#1f77b4')

# Ajustement d'un modèle linaire sur des données transformées en affichage log
fit = np.polyfit(np.log(liste_a), np.log(liste_T), 1) # ajustement d'ordre 1 (linéaire)
c = 10**fit[1]*liste_a**fit[0] # équation de la droite de régession
pente = round(fit[0],5)

ax2.plot(liste_a, c, 'm--') # tracé la droite de régression en couleur magenta

ax2.set_xlabel(r"demi-grand axe a (u.a.)")
ax2.set_ylabel(r"période orbitale T (années terrestres)")
ax2.set_xlim([0.1, 80])
ax2.set_ylim([0.1,10**3])
ax2.grid()
ax2.grid(which="minor", color="0.9")
plt.text(10,0.2, "pente = " + str(pente), size=12,
bbox=dict(boxstyle="round", ec=("m"), fc=("w"))) # affichage de la pente de la régression linéaire

L'ajustement des points correspond à une fonction linéaire de pente égale à 3/2 = 1,5.

Finalement, on peut tracer T2 en fonction de a3, figure 21.

 # Représentation de T^2 en fonction de a^3 :
  
fig = plt.figure()
ax3 = fig.add_subplot()
prop_scatter=dict(marker='o',s=15, edgecolor='k') # choix graphique des points
c_regression='crimson' # choix de la couleur cramoisi pour l'affichage de la régression linéaire

# Pour calculer les puissances, il faut convertir les listes en séries Numpy :
liste_T=np.array(liste_T)
liste_a=np.array(liste_a)
#ax3.set_xticks([])
#ax3.set_yticks([])
ax3.set_xlabel(r"$a^3$")
ax3.set_ylabel(r"$T^2$")
ax3.scatter(liste_a**3,liste_T**2,**prop_scatter) # tracé des points

# Régression linéaire :
x=liste_a**3
y=liste_T**2
fit=np.polyfit(x, y, 1)
fit_fn=np.poly1d(fit)
pente=fit[0]
ordo=fit[1]

ax3.plot(x, fit_fn(x), '--',c=c_regression, zorder=0) # tracé la droite de régression

ax3.text(0.99, 0.2, 'pente : {:.5f}'.format(pente), ha='right', transform=ax3.transAxes)
ax3.text(0.99, 0.1, 'ordonnée : {:.5f}'.format(ordo), ha='right', transform=ax3.transAxes)
ax3.text(0.5,0.90,"$T^2$=f($a^3$)",c=c_regression, transform=ax3.transAxes,
         ha='center',fontsize=16)
plt.grid()

fig.savefig("Kepler3.pdf",dpi=300)
fig.savefig("Kepler3.png",dpi=300)

Le rapport T2/r3 est constant pour toutes les planètes qui appartiennent à un système solaire (étoile au centre d'un des foyers). Pour les orbites de notre Système solaire, la constante de Kepler vaut environ 1 UA-3année terrestre2 ou encore 2,97.10-19 m-3s2.

Programmes python :

Télécharger le programme python pour retrouver la troisième loi de Kepler :

Au sujet de l'auteur

David Alberto est l'auteur du blog www.astrolabe-science.fr.

Références

[1] Johannes Kepler, Wikipedia, consulté en mai 2025.

[2] The Project Gutenberg eBook of Kepler, consulté en mai 2025.

[3] Collection des écrits de Kepler, collections patrimoniales numérisées des bibliothèques de l'Université de Strasbourg, consulté en mai 2025.

[4] How a fake Kepler portrait became iconic, arxiv.org, consulté en mai 2025.

[5] Découvrir les lois de Kepler avec le langage de programmation Python, Thomas Appéré, BUP n°1063, avril 2024.

[6] Portail d'éphémérides de l' Institut de mécanique céleste et de calcul des éphémérides (IMCCE), consulté en septembre 2025.

Pour citer cet article :

Mettre en évidence les lois de Kepler à l'aide de codes en langage python, David ALberto, Delphine Chareyron, octobre 2025. CultureSciences Physique - ISSN 2554-876X, https://culturesciencesphysique.ens-lyon.fr/ressource/Lois-Kepler-python-Alberto.xml

Ressource au format PDF