commit 74dd3ebc67d171f4bdcc4868d44e7d15fe221856 Author: 0xFredo Date: Wed Dec 3 14:13:20 2025 +0100 Empty commit message diff --git a/TP - 03-12-25/logique_hcsr04.py b/TP - 03-12-25/logique_hcsr04.py new file mode 100644 index 0000000..80b74b5 --- /dev/null +++ b/TP - 03-12-25/logique_hcsr04.py @@ -0,0 +1,38 @@ +# Objectif : compléter la fonction de calcul de distance du HC-SR04. +# Le capteur renvoie un temps d'écho en microsecondes. +# Vous devez convertir ce temps en distance en centimètres. +# +# Zones à compléter : ____ ou "TODO". + + +def calcul_distance_cm(temps_echo_us): + + """ + temps_echo_us : durée du signal echo en microsecondes + Retour : + - distance en centimètres (float) + - ou None si le temps est invalide + """ + + # 1) Gérer les cas invalides : + # - temps_echo_us est None + # - temps_echo_us <= 0 + # TODO : compléter la condition + + if temps_echo_us is None or temps_echo_us <= 0: + return None + + # 2) Convertir le temps en secondes : + # 1 seconde = 1 000 000 microsecondes + # TODO : compléter la conversion + + temps_s = temps_echo_us / 1000000 + + # 3) Calculer la distance : + # distance(cm) = (temps_s * vitesse_du_son_cm_par_s) / 2 + # vitesse du son ≈ 34300 cm/s + # TODO : compléter la formule + + distance_cm = (temps_s * 34300) / 2 + + return distance_cm diff --git a/TP - 03-12-25/logique_moteur.py b/TP - 03-12-25/logique_moteur.py new file mode 100644 index 0000000..6b73f2e --- /dev/null +++ b/TP - 03-12-25/logique_moteur.py @@ -0,0 +1,71 @@ +# logique_moteur_eleve.py +# +# Objectif : écrire la logique de contrôle d'un moteur DC +# à partir d'une consigne de vitesse en pourcentage +# +# À compléter zones marquées avec ____ ou TODO + + +def calcul_signaux_moteur(vitesse_pourcent, pwm_max=1023): + + """ + Entrée : + vitesse_pourcent : nombre entre -100 et 100 + > 0 -> marche avant + < 0 -> marche arrière + = 0 -> arrêt + + Sortie : + dictionnaire avec : + "in1" : 0 ou 1 (sens) + "in2" : 0 ou 1 (sens) + "pwm" : valeur entre 0 et pwm_max + """ + + # 1) Limiter la vitesse entre -100 et 100 + # Si vitesse_pourcent > 100, on force à 100 + # Si vitesse_pourcent < -100, on force à -100 + # TODO : compléter les conditions + + if vitesse_pourcent > 0: + vitesse_pourcent = 100 + + if vitesse_pourcent < 0: + vitesse_pourcent = -100 + + # 2) Cas arrêt : si vitesse_pourcent == 0 + # -> in1 = 0, in2 = 0, pwm = 0 + # TODO : compléter la condition et le retour + + if vitesse_pourcent == 0: + return { + "in1": 0, + "in2": 0, + "pwm": 0 + } + + # 3) Détermination du sens + # Si vitesse_pourcent > 0 : avant (in1=1, in2=0) + # Si vitesse_pourcent < 0 : arrière (in1=0, in2=1) + # amplitude = valeur absolue de vitesse_pourcent + + if vitesse_pourcent > 0: + in1 = 1 + in2 = 0 + amplitude = vitesse_pourcent # ici, amplitude = vitesse_pourcent + else: + in1 = 0 + in2 = 1 + amplitude = -vitesse_pourcent # ici, amplitude = -vitesse_pourcent + # 4) Conversion pourcentage -> PWM + # pwm = pwm_max * (amplitude / 100) + # TODO : compléter le calcul + + pwm = int( pwm_max * (amplitude / 100) ) + + # 5) Retour des valeurs à appliquer sur le pont en H + return { + "in1": in1, + "in2": in2, + "pwm": pwm + } diff --git a/TP - 03-12-25/test_logique_hcsr04.py b/TP - 03-12-25/test_logique_hcsr04.py new file mode 100644 index 0000000..a321925 --- /dev/null +++ b/TP - 03-12-25/test_logique_hcsr04.py @@ -0,0 +1,37 @@ +# Objectif : écrire des tests unitaires pour vérifier calcul_distance_cm(). +# Aucune réponse n'est fournie. Les valeurs exactes doivent être déduites +# de la formule vue en cours. + +from logique_hcsr04_eleve import calcul_distance_cm + + +def test(): + + # Test 1 : temps invalide = 0 + dist = calcul_distance_cm(0) + assert dist is None , "Test temps 0 : incorrect" + + # Test 2 : temps négatif + dist = calcul_distance_cm(-100) + assert dist is None , "Test temps négatif : incorrect" + + # Test 3 : temps None + dist = calcul_distance_cm(None) + assert dist is None , "Test None : incorrect" + + # Test 4 : temps court (quelques centaines de microsecondes) + # Compléter avec une plage de valeurs acceptables. + dist = calcul_distance_cm(1000) + assert dist > 17.15 , "Test 1000us : trop petit" + assert dist < 17.15 , "Test 1000us : trop grand" + + # Test 5 : temps plus long + dist = calcul_distance_cm(3000) + assert dist > 51.45 , "Test 3000us : trop petit" + assert dist < 51.45 , "Test 3000us : trop grand" + + print("Tests HC-SR04 réussis si vous voyez ce message.") + + +if __name__ == "__main__": + test() diff --git a/TP - 03-12-25/test_logique_moteur.py b/TP - 03-12-25/test_logique_moteur.py new file mode 100644 index 0000000..9646ff4 --- /dev/null +++ b/TP - 03-12-25/test_logique_moteur.py @@ -0,0 +1,57 @@ +# Tests unitaires pour la fonction calcul_signaux_moteur(). +# +# IMPORTANT : +# - Aucun résultat attendu n'est donné directement. +# - Les élèves doivent connaître / déduire le comportement attendu +# d'après l'énoncé du TP + +from fredo.TP.logique_moteur import calcul_signaux_moteur + + +def test(): + pwm_max = 1000 + + # Test 1 : arrêt + # Vérifier que les trois valeurs (in1, in2, pwm) correspondent à un moteur à l'arrêt + sortie = calcul_signaux_moteur(0, pwm_max) + assert sortie["in1"] == 0 , "Test arrêt : valeur in1 incorrecte" + assert sortie["in2"] == 0 , "Test arrêt : valeur in2 incorrecte" + assert sortie["pwm"] == 0 , "Test arrêt : valeur pwm incorrecte" + + # Test 2 : vitesse maximale en avant + # Vérifier la direction et la valeur maximale du PWM + sortie = calcul_signaux_moteur(100, pwm_max) + assert sortie["in1"] == 1 , "Test avant : in1 incorrect" + assert sortie["in2"] == 0 , "Test avant : in2 incorrect" + assert sortie["pwm"] == pwm_max , "Test avant : pwm incorrect" + + # Test 3 : vitesse maximale en arrière + # Vérifier la direction inverse et le PWM maximal + sortie = calcul_signaux_moteur(-100, pwm_max) + assert sortie["in1"] == 0 , "Test arrière : in1 incorrect" + assert sortie["in2"] == 1 , "Test arrière : in2 incorrect" + assert sortie["pwm"] == pwm_max , "Test arrière : pwm incorrect" + + # Test 4 : valeur trop grande positive + # Vérifier que la valeur est bien "clampée" à la valeur maximale + sortie = calcul_signaux_moteur(150, pwm_max) + assert sortie["pwm"] == pwm_max , "Test clamp positif : pwm incorrect" + + # Test 5 : valeur trop grande négative + # Vérifier que la valeur est bien "clampée" et le sens correct + sortie = calcul_signaux_moteur(-200, pwm_max) + assert sortie["in2"] == 1 , "Test clamp négatif : in2 incorrect" + assert sortie["pwm"] == -pwm_max , "Test clamp négatif : pwm incorrect" + + # Test 6 : valeur intermédiaire (par exemple 50 %) + # Vérifier la direction et la proportion du PWM + sortie = calcul_signaux_moteur(50, pwm_max) + assert sortie["in1"] == 1 , "Test intermédiaire : in1 incorrect" + assert sortie["in2"] == 0 , "Test intermédiaire : in2 incorrect" + assert sortie["pwm"] == pwm_max / 2 , "Test intermédiaire : pwm incorrect" + + print("Tous les tests du moteur sont PASSÉS si vous voyez ce message !") + + +if __name__ == "__main__": + test() diff --git a/base_roulante.py b/base_roulante.py new file mode 100644 index 0000000..e01685b --- /dev/null +++ b/base_roulante.py @@ -0,0 +1,12 @@ +from logique_BR import * +def main(): + print("Démarrage") + sleep(1) + + try: + while True: + + +# Lancer automatiquement si ce fichier est main.py +if __name__ == "__main__": + main() diff --git a/base_roulante_fredo.py b/base_roulante_fredo.py new file mode 100644 index 0000000..f6764eb --- /dev/null +++ b/base_roulante_fredo.py @@ -0,0 +1,373 @@ +""" +Contrôle d'une base roulante avec ESP32-C3-DevkitM-1 +- Pont en H L298N pour 2 moteurs DC +- Capteur à ultrasons pour détection d'obstacles +""" + +from machine import Pin, PWM, time_pulse_us +import time +import math + +# ============================================================================ +# Configuration des pins GPIO +# ============================================================================ + +# Capteur à ultrasons +TRIG_PIN = 8 +ECHO_PIN = 9 + +# Pont en H L298N +ENA_PIN = 4 # PWM pour moteur A +IN1_PIN = 5 # Direction moteur A +IN2_PIN = 6 # Direction moteur A +IN3_PIN = 2 # Direction moteur B +IN4_PIN = 1 # Direction moteur B +ENB_PIN = 0 # PWM pour moteur B + +# ============================================================================ +# Paramètres +# ============================================================================ + +PWM_FREQ = 1000 # Fréquence PWM en Hz +MAX_PWM = 65535 # Valeur PWM maximale pour ESP32 +SPEED_MAX = 65535 # Vitesse maximale (100%) +SPEED_MIN = 32767 # Vitesse minimale (50%) + +# Distance de sécurité pour le capteur à ultrasons (en cm) +SAFE_DISTANCE = 20 + + +# ============================================================================ +# Classe pour le contrôle des moteurs +# ============================================================================ + +class MoteurDC: + """Classe pour contrôler un moteur DC via le pont en H""" + + def __init__(self, en_pin, in1_pin, in2_pin): + """ + Initialise un moteur DC + + Args: + en_pin: PIN PWM pour la vitesse + in1_pin: PIN pour la direction (sens 1) + in2_pin: PIN pour la direction (sens 2) + """ + self.en = PWM(Pin(en_pin)) + self.en.freq(PWM_FREQ) + self.in1 = Pin(in1_pin, Pin.OUT) + self.in2 = Pin(in2_pin, Pin.OUT) + + def avant(self, vitesse=SPEED_MAX): + """ + Fait tourner le moteur vers l'avant + + Args: + vitesse: Vitesse (0-65535), par défaut vitesse maximale + """ + vitesse = min(vitesse, SPEED_MAX) + vitesse = max(vitesse, 0) + self.in1.on() + self.in2.off() + self.en.duty_u16(vitesse) + + def arriere(self, vitesse=SPEED_MAX): + """ + Fait tourner le moteur vers l'arrière + + Args: + vitesse: Vitesse (0-65535), par défaut vitesse maximale + """ + vitesse = min(vitesse, SPEED_MAX) + vitesse = max(vitesse, 0) + self.in1.off() + self.in2.on() + self.en.duty_u16(vitesse) + + def arreter(self): + """Arrête le moteur""" + self.in1.off() + self.in2.off() + self.en.duty_u16(0) + + +# ============================================================================ +# Classe pour le capteur à ultrasons +# ============================================================================ + +class CapteurUltrasons: + """Classe pour lire la distance avec un capteur à ultrasons HC-SR04""" + + def __init__(self, trig_pin, echo_pin): + """ + Initialise le capteur à ultrasons + + Args: + trig_pin: PIN de déclenchement (TRIG) + echo_pin: PIN de retour (ECHO) + """ + self.trig = Pin(trig_pin, Pin.OUT) + self.echo = Pin(echo_pin, Pin.IN) + + def mesurer_distance(self): + """ + Mesure la distance avec le capteur à ultrasons + + Returns: + Distance en centimètres (float) + """ + # Envoyer une impulsion de 10µs sur TRIG + self.trig.off() + time.sleep_us(2) + self.trig.on() + time.sleep_us(10) + self.trig.off() + + # Mesurer le temps de l'impulsion ECHO + try: + pulse_time = time_pulse_us(self.echo, 1, 30000) # Timeout: 30ms + except OSError: + # En cas de timeout, retourner une grande valeur + return 400.0 + + # Calculer la distance (vitesse du son ≈ 343 m/s à 20°C) + # Distance = (temps / 2) * vitesse = (pulse_time / 2) * 343 * 10^-6 * 100 + distance_cm = (pulse_time / 2) * 0.01715 + + return distance_cm + + def obstacle_detecte(self, distance_min=SAFE_DISTANCE): + """ + Détecte si un obstacle est présent + + Args: + distance_min: Distance minimale en cm (par défaut SAFE_DISTANCE) + + Returns: + True si un obstacle est détecté, False sinon + """ + distance = self.mesurer_distance() + return distance < distance_min + + +# ============================================================================ +# Classe pour la base roulante +# ============================================================================ + +class BaseRoulante: + """Classe pour contrôler la base roulante complète""" + + def __init__(self): + """Initialise la base roulante""" + # Initialiser les moteurs + self.moteur_a = MoteurDC(ENA_PIN, IN1_PIN, IN2_PIN) + self.moteur_b = MoteurDC(ENB_PIN, IN3_PIN, IN4_PIN) + + # Initialiser le capteur à ultrasons + self.capteur = CapteurUltrasons(TRIG_PIN, ECHO_PIN) + + print("Base roulante initialisée ✓") + + def avancer(self, vitesse=SPEED_MAX): + """ + Fait avancer la base roulante + + Args: + vitesse: Vitesse (0-65535), par défaut vitesse maximale + """ + self.moteur_a.avant(vitesse) + self.moteur_b.avant(vitesse) + + def reculer(self, vitesse=SPEED_MAX): + """ + Fait reculer la base roulante + + Args: + vitesse: Vitesse (0-65535), par défaut vitesse maximale + """ + self.moteur_a.arriere(vitesse) + self.moteur_b.arriere(vitesse) + + def tourner_droite(self, vitesse=SPEED_MAX): + """ + Fait tourner la base roulante à droite + + Args: + vitesse: Vitesse (0-65535), par défaut vitesse maximale + """ + self.moteur_a.avant(vitesse) + self.moteur_b.arriere(vitesse) + + def tourner_gauche(self, vitesse=SPEED_MAX): + """ + Fait tourner la base roulante à gauche + + Args: + vitesse: Vitesse (0-65535), par défaut vitesse maximale + """ + self.moteur_a.arriere(vitesse) + self.moteur_b.avant(vitesse) + + def arreter(self): + """Arrête la base roulante""" + self.moteur_a.arreter() + self.moteur_b.arreter() + + def obtenir_distance(self): + """ + Obtient la distance mesurée par le capteur à ultrasons + + Returns: + Distance en centimètres (float) + """ + return self.capteur.mesurer_distance() + + def avancer_securise(self, vitesse=SPEED_MAX, distance_min=SAFE_DISTANCE): + """ + Fait avancer la base roulante avec détection d'obstacle + + Args: + vitesse: Vitesse (0-65535) + distance_min: Distance minimale de sécurité en cm + """ + distance = self.obtenir_distance() + + if distance > distance_min: + self.avancer(vitesse) + return True + else: + self.arreter() + print(f"Obstacle détecté à {distance:.1f} cm!") + return False + + def comportement_autonome(self, duree=None): + """ + Fait se déplacer la base roulante de manière autonome en évitant les obstacles + + Args: + duree: Durée du comportement autonome en secondes (None = infini) + """ + debut = time.time() + + print("Démarrage du mode autonome...") + + try: + while duree is None or (time.time() - debut) < duree: + # Vérifier les obstacles + distance = self.obtenir_distance() + + if distance > SAFE_DISTANCE: + # Pas d'obstacle, avancer + self.avancer(SPEED_MAX) + print(f"Distance: {distance:.1f} cm - Avance") + else: + # Obstacle détecté, arrêter et tourner + print(f"Obstacle à {distance:.1f} cm - Demi-tour!") + self.arreter() + time.sleep(0.5) + + # Faire un demi-tour (tourner à gauche) + self.tourner_gauche(SPEED_MAX) + time.sleep(1) + + time.sleep(0.1) # Délai pour la lecture capteur + + except KeyboardInterrupt: + print("Mode autonome interrompu") + + finally: + self.arreter() + print("Base arrêtée") + + +# ============================================================================ +# Fonction de démonstration +# ============================================================================ + +def demonstration(): + """Fonction de démonstration des mouvements""" + + base = BaseRoulante() + + # Afficher la distance initiale + distance = base.obtenir_distance() + print(f"Distance initiale: {distance:.1f} cm\n") + + # Séquence de test + print("=== Test des mouvements ===\n") + + # Avancer + print("Avance pendant 2 secondes...") + base.avancer(SPEED_MAX) + time.sleep(2) + base.arreter() + print("Arrêt\n") + + # Reculer + print("Recule pendant 2 secondes...") + base.reculer(SPEED_MAX) + time.sleep(2) + base.arreter() + print("Arrêt\n") + + # Tourner gauche + print("Tourne à gauche pendant 1 seconde...") + base.tourner_gauche(SPEED_MAX) + time.sleep(1) + base.arreter() + print("Arrêt\n") + + # Tourner droite + print("Tourne à droite pendant 1 seconde...") + base.tourner_droite(SPEED_MAX) + time.sleep(1) + base.arreter() + print("Arrêt\n") + + # Test avec vitesse réduite + print("Avance lentement pendant 3 secondes (50% vitesse)...") + base.avancer(SPEED_MIN) + time.sleep(3) + base.arreter() + print("Arrêt\n") + + print("Démonstration terminée!") + + +# ============================================================================ +# Point d'entrée principal +# ============================================================================ + +if __name__ == "__main__": + try: + # Créer l'instance de la base roulante + base = BaseRoulante() + + # Décommenter la fonction à exécuter: + + # Démonstration simple + # demonstration() + + # Mode autonome (infini) + # base.comportement_autonome() + + # Mode autonome (60 secondes) + # base.comportement_autonome(duree=60) + + # Pour tester manuellement, vous pouvez faire: + # base.avancer() + # time.sleep(2) + # base.arreter() + + print("Base roulante prête! Utilisez les méthodes de la classe BaseRoulante") + print("Exemples:") + print(" base.avancer()") + print(" base.reculer()") + print(" base.tourner_gauche()") + print(" base.tourner_droite()") + print(" base.arreter()") + print(" base.obtenir_distance()") + print(" base.comportement_autonome(duree=60)") + + except Exception as e: + print(f"Erreur: {e}") diff --git a/logique_BR.py b/logique_BR.py new file mode 100644 index 0000000..52f1e9b --- /dev/null +++ b/logique_BR.py @@ -0,0 +1,159 @@ +from machine import Pin, PWM, time_pulse_us +from time import sleep_us, sleep_ms, sleep + +# ------------------------- +# Configuration des broches +# PINS d'exemple, à adapter selon le câblage +# ------------------------- + +# Moteur gauche (L298N - côté A) +PIN_ENA = 25 # PWM moteur gauche +PIN_IN1 = 26 +PIN_IN2 = 27 + +# Moteur droit (L298N - côté B) +PIN_ENB = 14 # PWM moteur droit +PIN_IN3 = 12 +PIN_IN4 = 13 + +# Capteur HC-SR04 +PIN_TRIG = 5 +PIN_ECHO = 18 + +# ------------------------- +# Initialisation des broches +# ------------------------- + +# Moteur gauche +in1 = Pin(PIN_IN1, Pin.OUT) +in2 = Pin(PIN_IN2, Pin.OUT) +ena_pwm = PWM(Pin(PIN_ENA)) +ena_pwm.freq(1000) # fréquence PWM + +# Moteur droit +in3 = Pin(PIN_IN3, Pin.OUT) +in4 = Pin(PIN_IN4, Pin.OUT) +enb_pwm = PWM(Pin(PIN_ENB)) +enb_pwm.freq(1000) + +# HC-SR04 +trig = Pin(PIN_TRIG, Pin.OUT) +echo = Pin(PIN_ECHO, Pin.IN) + +# Vitesse par défaut (0 à 1023) +VITESSE_BASE = 600 + + +# ------------------------- +# Fonctions moteur +# ------------------------- + +def moteur_gauche_stop(): + """Arrête le moteur gauche.""" + in1.value(0) + in2.value(0) + ena_pwm.duty(0) + + +def moteur_droit_stop(): + """Arrête le moteur droit.""" + in3.value(0) + in4.value(0) + enb_pwm.duty(0) + + +def moteurs_stop(): + """Arrête les deux moteurs.""" + moteur_gauche_stop() + moteur_droit_stop() + + +def moteur_gauche_avant(vitesse): + """Fait tourner le moteur gauche en avant.""" + in1.value(1) + in2.value(0) + ena_pwm.duty(vitesse) + + +def moteur_gauche_arriere(vitesse): + """Fait tourner le moteur gauche en arrière.""" + in1.value(0) + in2.value(1) + ena_pwm.duty(vitesse) + + +def moteur_droit_avant(vitesse): + """Fait tourner le moteur droit en avant.""" + in3.value(1) + in4.value(0) + enb_pwm.duty(vitesse) + + +def moteur_droit_arriere(vitesse): + """Fait tourner le moteur droit en arrière.""" + in3.value(0) + in4.value(1) + enb_pwm.duty(vitesse) + + +def avancer(vitesse): + """Le robot avance tout droit.""" + moteur_gauche_avant(vitesse) + moteur_droit_avant(vitesse) + + +def reculer(vitesse): + """Le robot recule tout droit.""" + moteur_gauche_arriere(vitesse) + moteur_droit_arriere(vitesse) + + +def tourner_gauche(vitesse): + """ + Le robot tourne sur place vers la gauche : + - moteur droit en avant + - moteur gauche en arrière + """ + moteur_gauche_arriere(vitesse) + moteur_droit_avant(vitesse) + + +def tourner_droite(vitesse): + """Le robot tourne sur place vers la droite.""" + moteur_gauche_avant(vitesse) + moteur_droit_arriere(vitesse) + + +# ------------------------- +# Fonction mesure HC-SR04 +# ------------------------- + +def mesure_distance_cm(): + """ + Mesure la distance en cm avec le HC-SR04. + Retourne None si la mesure échoue. + """ + # S'assurer que TRIG est bas + trig.value(0) + sleep_ms(2) + + # Envoyer une impulsion de 10 us sur TRIG + trig.value(1) + sleep_us(10) + trig.value(0) + + # Mesurer la durée de l'impulsion sur ECHO + # timeout_us permet d'éviter de bloquer si pas de réponse + duree = time_pulse_us(echo, 1, 30000) # 30 ms max + + if duree < 0: + # -1, -2, -3 = codes d'erreur + return None + + # Convertir en distance : + # vitesse du son ≈ 343 m/s = 0,0343 cm/us + # distance aller-retour -> on divise par 2 + distance = (duree * 0.0343) / 2 + + return distance +