SE3Binome2023-4

De projets-se.plil.fr
Aller à la navigation Aller à la recherche

Sujet : voiture commandée par USB

Cahier des Charges

Le projet consiste à concevoir une voiture autonome en terme d'énergie, avec l'utilisation d'une batterie au lithium ainsi que la mise en place d'une alimentation intégrée sur la carte.

Cette voiture sera équipée de deux moteurs continus de petite taille (2 cm) situés au niveau des roues avant. Cette configuration présente plusieurs avantages, notamment la réduction des contraintes mécaniques grâce à l'utilisation d'un pont en H et l'inversion des sens de rotation des roues. De plus, cela permet de diminuer le poids de la voiture, ce qui se traduit par moins de contraintes sur la taille de la carte électronique.

Le système comprendra également quatre leds de surface, dont deux seront placées à l'avant et deux à l'arrière de la voiture. Ces leds serviront à indiquer le sens de déplacement de la voiture, par exemple en allumant la led gauche avant pour tourner à gauche en avançant.

L'utilisateur pourra choisir le programme de déplacement de la voiture grâce à un logiciel implanté sur un ordinateur, qui sera ensuite transféré sur la carte via le port USB.

Concernant la carte électronique, elle sera équipée d'un microcontrôleur ATmega16u4 ou ATmega32u4 (différence en fonction de l'espace dédié à la flash).

Une optimisation possible serait l'ajout d'un détecteur d'obstacles.

Lien GIT

https://archives.plil.fr/sdeparis/projet_voiture_sd_ah.git

Partie électronique

Schématique Kicad

On ajoute divers composants :

  • Le microcontrôleur ATMega16u4.
  • 4 leds (pour les feux) et leurs résistances associées.
  • Un connecteur ISP afin que la carte puisse effacer le programme précédent et charger le suivant.
  • Un capteur infrarouge afin de détecter les obstacles.
  • Un contrôleur moteurs pour les moteurs gauche (placé sur le connecteur J5) et droit (placé sur le connecteur J6).
  • Un chargeur pour la batterie et un connecteur qui à l'aide d'un cavalier permet de choisir le mode d'utilisation de la batterie.
  • Deux ports USB : un pour transférer le programme à la carte, et un pour l'alimentation.
Schématique voiture
Schématique voiture
ATMEGA16U4
NOM PIN
LED1 PF7
LED2 PB7
LED3 PD2
LED4 PC7
LED5 PE6
PWMA PD1
PWMB PD0
AIN1 PF4
AIN2 PF5
BIN1 PF1
BIN2 PF0
capteur PF6
SENS DU MOTEUR
NOM PIN
M1+ A01
M1- A02
M2+ B01
M2- B02

Routage

Routage du PCB
Routage du PCB

On place des vias afin d'uniformiser le plan de masse.

Vue 3D

Visualisation carte PCB
Visualisation carte PCB


Brasure

PCB sans composants

PCB sans composants
PCB  sans composants

Brasure des principaux composants

Carte brasée avec quelques composants
Carte brasée avec quelques composants


On doit ensuite braser les moteurs à l'avant de la voiture. Problème : après avoir brasé le premier moteur, la carte n'est plus reconnue en mode DFU. Par conséquent, il est inutile de braser le deuxième moteur et le détecteur d'obstacle. Il faudrait recommencer une carte mais nous manquons maintenant de temps pour braser à nouveau tous les composants sur une nouvelle carte.

premier moteur brasé
premier moteur brasé
Vue du dessus : premier moteur brasé
Vue du dessus : premier moteur brasé

Détecteur d'obstacles

Le OPB733TR est un interrupteur photoélectrique réflectif. Il intègre un émetteur infrarouge et un phototransistor dans un boîtier plastique compact.

L'émetteur infrarouge (une LED) émet de la lumière infrarouge lorsqu'un courant passe à travers lui. Cette lumière est ensuite réfléchie par un objet situé à proximité du capteur.

Le récepteur infrarouge (phototransistor) détecte alors la lumière réfléchie. Lorsqu'il la capte, il se met en conduction, permettant ainsi de détecter la présence de l'objet.

Detecteur.jpg Detecteur .png PIN

Attention, d'après la datasheet de OPB33TR, il ne doit pas être exposé à une température de plus de 260°C pendant plus de 10 secondes,

donc lors du brasage de ce détecteur il faut être rapide et précis.

Partie programmation

Détection du mode DFU sur notre carte

Utilisation de la carte Arduino Uno afin de détecter le mode DFU

Le mode DFU de notre carte n'étant pas détecté, comme pour notre manette : https://projets-se.plil.fr/mediawiki/index.php/SE3_PSE_Binome2023-2#D%C3%A9tection_du_mode_DFU_sur_notre_carte, nous avons dû utiliser un Arduino en lançant les commandes suivantes :

  • avrdude -c stk500v1 -p atmega16u4 -P /dev/ttyACM0 -b 19200 -U lfuse:w:0xFF:m -U efuse:w:0xF7:m
  • avrdude -c stk500v1 -p atmega16u4 -P /dev/ttyACM0 -b 19200 -U flash:w:ATMega16U4-usbdevice_dfu-1_0_0.hex

Programmation des leds

Le programme ci-dessous nous permet de faire clignoter les leds de la voiture.

Code leds

Programmation des moteurs

Nous avons ensuite implémenté un programme afin de faire tourner les moteurs mais celui-ci n'a pas pu être testé à cause de l’absence de détection du mode DFU.

code voiture Programmation des moteurs

Les lignes 61-63 permettent de contrôler la vitesse des moteurs.

Registres de comparaison des sorties

Voici la datasheet de l'ATMega16u4 : https://ww1.microchip.com/downloads/en/devicedoc/atmel-7766-8-bit-avr-atmega16u4-32u4_datasheet.pdf

A partir de la page 94, on obtient des explications sur le mode de fonctionnement des registres de comparaisons, qui nous sont utiles afin de moduler la vitesse du moteur grâce à la méthode de contrôle du moteur PWM : "Pulse Width Modulation", qui permet une commande plus fine du moteur, comparée à la méthode du tout ou rien.

La datasheet nous informe qu'il existe des registres de comparaison de sortie à double tampon nommés OCR0A et OCR0B (Output Compare Registers) qui sont comparés au compteur. Le résultat de cette comparaison est utilisé pour générer un PWM, à savoir une sortie à fréquence variable sur les broches de comparaison de sortie OC0A et OC0B (Output Compare).

Les formes d'onde générées sur les sorties Output Compare OC0A et OC0B sont déterminées par le compteur. Et le compteur dépend lui-même du réglage des bits WGM01 et WGM00 (Waveform Generator) situés dans le registre de contrôle du compteur TCCR0A (Timer/Counter Control Register) et le bit WGM02 situé dans le registre de contrôle du compteur TCCR0B.

Chaque fois que TCNT0 (counter value) est égal à OCR0A ou OCR0B (Output Compare Registers), le comparateur signale une correspondance. Un match définira l'indicateur de comparaison de sortie OCF0A ou OCF0B au prochain cycle d'horloge de la minuterie. Si il y a une correspondance, le comparateur de sortie s'interrompra.

Le générateur de forme d'onde utilise le signal de correspondance pour générer une sortie selon le mode de fonctionnement défini par les bits WGM02:0 (Waveform generator) et les bits Compare Output mode (COM0x1:0).

Output Compare Unit

Programmation du détecteur d'obstacle

#define DETECTEUR       PF6

void detecteur_init(){
  DDRF &= ~(1<<DETECTEUR);
}

Quelques explications :

1<<DETECTEUR : Déplace le bit 1 à la position de la broche PF6 (bit 6 du registre DDRF).

~ : Inverse tous les bits (complément à un).

&= : effectue une opération AND avec l'inverse pour s'assurer que le bit de PF6 dans DDRF est mis à 0, configurant ainsi PF6 comme une entrée.

int detecter_obstacle(){
    return PINF & (1<<DETECTEUR); // Retourne l'état du détecteur d'obstacle
}

Quelques explications :

PINF & (1<<DETECTEUR) : Effectue une opération AND bit à bit entre le registre PINF et le bit correspondant à PF6.

Si la broche PF6 est à un niveau logique haut (1), l'opération AND retournera une valeur non nulle.

Si la broche PF6 est à un niveau logique bas (0), l'opération AND retournera 0.

Cette fonction renvoie donc une valeur non nulle si un obstacle est détecté (c'est-à-dire si la broche PF6 est à un niveau logique haut) et 0 sinon.

Programmation d'un trajet prédéfini pour la voiture

Le but est de programmer les leds et les moteurs de manière à ce que la voiture effectue un chemin prédéfini à l'avance. Pour cela, on va d'abord charger le programme sur la carte, puis laisser la voiture exécuter le programme en totale autonomie à l'aide de la batterie.

Voici le trajet prédéfini :

Liste des déplacements et états des LEDs

  1. Marche avant (5 secondes) :
    • Déplacement : Avance tout droit.
    • LEDs : Aucune LED allumée.
  2. Tourner à droite (1 seconde) :
    • Déplacement :
      • Moteur droit : Rotation arrière.
      • Moteur gauche : Rotation avant.
    • LEDs :
      • LED D4 (Droite) : Clignote.
  3. Marche arrière (2 secondes) :
    • Déplacement : Recule tout droit.
    • LEDs :
      • LED D2 (Feux de recul) : Allumée.
      • LED D3 (Feux de recul) : Allumée.
  4. Marche avant (2 secondes) :
    • Déplacement : Avance tout droit.
    • LEDs : Aucune LED allumée.
  5. Tourner à gauche (1 seconde) :
    • Déplacement :
      • Moteur droit : Rotation avant.
      • Moteur gauche : Rotation arrière.
    • LEDs :
      • LED D5 (Gauche) : Clignote.

Cas spécial : Détection d'obstacle

  • Déplacement : Arrêt immédiat des moteurs.
  • LEDs :
    • LED D1, D2, D3, D4 (Feux d'arrêt) : Allumées.

Programme :

#include <avr/io.h>
#include <stdio.h>
#include <stdlib.h>
#include <util/delay.h>

// Déclaration des ports et des entrées/sorties
#define PWM_DDRD        DDRD
#define PWM_PORD        PORTD
#define DETECTEUR       PF6   
#define PWM1            1
#define PWM2            0
#define AIN1            4 // Moteur droit +
#define AIN2            5 // Moteur droit -
#define BIN1            1 // Moteur gauche +
#define BIN2            0 // Moteur gauche -

#define JTD 7 // cf ds p82 atmega8u2

void horloge_init(){
    CLKSEL0 = 0b00010101;   // Sélection de l'horloge externe
    CLKSEL1 = 0b00001111;   // Minimum de 8Mhz
    CLKPR = 0b10000000;     // Modification du diviseur d'horloge (CLKPCE=1)
    CLKPR = 0;              // 0 pour pas de diviseur (diviseur de 1)
}

void configuration(){
    MCUCR |= (1<<JTD); // Pour désactiver la fonctionnalité JTAG
    MCUCR |= (1<<JTD);
    // LEDs et moteurs
    DDRB |= 0x80; // D2 sur PB7
    DDRC |= 0x80; // D4 sur PC7
    DDRD |= 0x04; // D3 sur PD2
    DDRE |= 0x40; // D5 sur PE6
    DDRF |= 0x80; // D1 sur PF7
}

void moteur_init(void){               // Initialisation de la PWM
    PWM_DDRD |= (1<<PWM1)|(1<<PWM2);      // Les ports PWM sont des sorties
    TCCR0A |= (1<<COM0A1)|(1<<COM0B1);   // Les ports PWM se comportent normalement
    TCCR0A |= (1<<WGM01)|(1<<WGM00);     // Minuteur mis en mode PWM
    TCCR0B |= (1<<CS00);                 // Pas de pré-diviseur, démarre le compteur
}

void detecteur_init(){
  DDRF &= ~(1<<DETECTEUR);
}

void clignotant_droit(){ // D4
  PORTC ^= 0x80;
  _delay_ms(1000); 
}

void clignotant_gauche(){ // D5
   PORTE ^= 0x40;
  _delay_ms(1000);
}

void feux_recul(){ // D3 et D2
  PORTB |= 0x80; // D2
  PORTD |= 0x04; // D3
}

void feux_arret_moteur(){ // D1-2-3-4
  PORTC |= 0x80;
  PORTE |= 0x40;
  feux_recul();
}

void moteur_droit_rotation_avant(){ // J6 (moteur A)
  PWM_PORD |= (1<<AIN1);
  PWM_PORD &= ~(1<<AIN2);
}
void moteur_droit_rotation_arriere(){ // J6 (moteur A)
  PWM_PORD &= ~(1<<AIN1);
  PWM_PORD |= (1<<AIN2);
}
void moteur_gauche_rotation_avant(){ // J5 (moteur B)
  PWM_PORD |= (1<<BIN1);
  PWM_PORD &= ~(1<<BIN2);
}
void moteur_gauche_rotation_arriere(){ // J5 (moteur B)
  PWM_PORD &= ~(1<<BIN1);
  PWM_PORD |= (1<<BIN2);
}

void marche_avant(){
  moteur_gauche_rotation_avant();
  moteur_droit_rotation_avant();
}

void marche_arriere(){
  moteur_gauche_rotation_arriere();
  moteur_droit_rotation_arriere();
  feux_recul();
}

void arret_moteur(){
    OCR0A = 0;
    OCR0B = 0;
    feux_arret_moteur();
}

int detecter_obstacle(){
    return PINF & (1<<DETECTEUR); // Retourne l'état du détecteur d'obstacle
}

void trajet(){
        // Avancer pendant 5 secondes
        marche_avant();
        _delay_ms(5000);

        // Tourner à droite
        clignotant_droit();
        moteur_droit_rotation_arriere();
        moteur_gauche_rotation_avant();
        _delay_ms(1000);

        // Reculer pendant 2 secondes
        marche_arriere();
        _delay_ms(2000);

        // Avancer pendant 2 secondes
        marche_avant();
        _delay_ms(2000);

        // Tourner à gauche
        clignotant_gauche();
        moteur_droit_rotation_avant();
        moteur_gauche_rotation_arriere();
        _delay_ms(1000);

        // Vérification d'obstacles et arrêt en cas de détection
        if(detecter_obstacle()){
            arret_moteur();
            while(detecter_obstacle()); // Attend que l'obstacle soit enlevé
        }
}


int main(){
    configuration();
    horloge_init();
    moteur_init();
    detecteur_init();
    int pwm = 0;

    while(1){
        trajet();

        // Réinitialisation des PWM pour la boucle suivante
        pwm++;
        if(pwm > 100)
            pwm = 0;
        OCR0A = pwm * 255 / 100;                 // PWM pour le port 5 (ramenée à 255)
        OCR0B = (100 - pwm) * 255 / 100;         // PWM pour le port 6 (ramenée à 255)
        _delay_ms(10);
    }

    return 0;
}


Cependant n'ayant plus le mode DFU nous n'avons pas pu tester notre programme.

Partie Énergie

Batterie

On utilise une batterie au Lithium afin que la voiture fonctionne de manière autonome, et puisse exécuter son programme indépendamment du port USB.

La batterie au Lithium se placera sur le connecteur J10 Batterie

Charge de la batterie

On place le cavalier entre les pins 1 et 2 du connecteur J10.

Utilisation de la batterie

On place le cavalier entre les pins 2 et 3 du connecteur J10.

Chargeur LIPO et alimentation USB

Le chargeur recharge la batterie Lithium Polymère par le biais d'un port USB qui sert d'alimentation au chargeur. Grâce au chargeur LiPo, la tension et le courant d'entrée sont adaptés à la batterie.

chargeur lipo


USB

La batterie au Lithium sera placée sur le connecteur J7.

Partie mécanique

Cette partie pourra être utile pour fixer les moteurs, les roues et la batterie sur la carte.

Moteurs et roues

L'objectif est de coller les moteurs sous la carte à l'aide d'un pistolet à colle. On ajoute ensuite les roues sur les arbres des moteurs.

Nous avons également placé un lego à la place du deuxième moteur à l'aide de pâte à fixe. L'ajout de ce lego est temporaire, et sera ensuite remplacé par un moteur fixé à la carte par de la glue.

Roue intallée sur le 1er moteur Mise en place des roues (Vue du dessus) Mise en place des roues ( roue sur le 1er moteur )


Support de batterie

Nous avons créé un support de batterie en lego. Celui-ci est maintenu sous la carte grâce à une liaison lego avec le support des roues arrières. La batterie est ainsi maintenue sur le support de manière fiable.

support de batterie Support batterie.jpg