====== TP Arduino – Commande d’un moteur CC par pont en H (hacheur) ======
{{https://www.ile-reunion.org/louispayen/cours/hacheur.gif}}
{{https://www.ile-reunion.org/louispayen/cours/hacheur_quadrant1.gif}}
⭐ À retenir
Le hacheur permet de contrôler la vitesse du moteur. Il utlise la modulation de largeur d'impulsion (MLI ou PWM).
Le rapport cyclique \( \alpha \)(%) vaut \( \alpha = \frac{t_{ON}}{T} \).
{{avr8js>ssi_mot?layout=horizontal}}
https://mistert.freeboxos.fr/circuit/proxy.php?id=mistert2_hacheur1&mode=open
https://mistert.freeboxos.fr/circuit/proxy.php?id=mistert2_hacheur2&mode=open
===== A) Câblage =====
Sur le matériel fourni :
* Connecter le moteur CC à la sortie du **pont en H**.
* Relier les broches de commande du pont en H aux sorties numériques de l’Arduino (ici D3 et D6).
* Vérifier l’alimentation.
* Objectif : faire tourner le moteur dans les deux sens.
===== B) Premier programme Arduino =====
Entrer le code suivant et observer le comportement du moteur :
**Programme de commande de moteur sur Arduino**
// Commande d’un moteur CC avec PWM et pont en H
// Tout ou rien (100 %), mais map() est déjà en place pour préparer la suite
const int sortie_commande_AV_pont_H = 3;
const int sortie_commande_AR_pont_H = 6;
void setup() {
pinMode(sortie_commande_AV_pont_H, OUTPUT);
pinMode(sortie_commande_AR_pont_H, OUTPUT);
}
// Marche avant (par défaut 100 %)
void moteurAvant(int alpha_percent = 100) {
int alpha_pwm = map(alpha_percent, 0, 100, 0, 255);
digitalWrite(sortie_commande_AR_pont_H, LOW);
analogWrite(sortie_commande_AV_pont_H, alpha_pwm);
}
// Marche arrière (par défaut 100 %)
void moteurArriere(int alpha_percent = 100) {
int alpha_pwm = map(alpha_percent, 0, 100, 0, 255);
digitalWrite(sortie_commande_AV_pont_H, LOW);
analogWrite(sortie_commande_AR_pont_H, alpha_pwm);
}
void loop() {
moteurAvant(); // avant 100 %
delay(2000);
}
===== C) Marche avant à 50 % =====
* Modifier l’appel dans ''loop()'' en utilisant ''moteurAvant(50);''
* Observer le moteur : vitesse plus faible.
* Avec un multimère mesurer la tension moyenne sur le moteur.
* Avec l’oscilloscope :
- Capturer la tension aux bornes du moteur.
- Relever la largeur d’impulsion et le rapport cyclique (~50 %).
- Calculer la tension moyenne et vérifier que U_moy = 0,5 × U_alim.
===== D) Marche arrière à 70 % =====
* Modifier l’appel dans ''loop()'' en utilisant ''moteurArriere(70);''
* Observer le moteur : marche arrière avec une vitesse plus élevée que pour 50 %.
* Avec un multimère mesurer la tension moyenne sur le moteur.
* Avec l’oscilloscope :
- Capturer la tension aux bornes du moteur.
- Relever le rapport cyclique (~70 %).
- Calculer la tension moyenne et vérifier que U_moy = -0,7 × U_alim.
===== E) Observation du courant haché =====
* Modifier l’appel dans ''loop()'' en utilisant ''moteurAvant(50);''
* Avec l’oscilloscope et la sonde de courant :
- Capturer la forme du courant moteur.
- Comparer avec le cas où le moteur est alimenté en continu.
- Expliquer pourquoi le courant est ici de forme quasi triangulaire : lien avec la PWM et l’inductance du moteur.
- Exercer un couple résistant sur le moteur (freinage à la main par exemple).
Que se passe-t-il sur l’amplitude moyenne et l’ondulation du courant ? Conclure sur l'intérêt du hacheur.
===== F) Programmation d’une rampe de vitesse =====
Lors d’un démarrage direct en PWM, le moteur reçoit immédiatement sa consigne (exemple : 100 %).
Cela provoque :
* un appel de courant important,
* une accélération brutale,
* un risque de perte d’adhérence (patinage d’un robot, glissement d’un convoyeur),
* une usure plus rapide du système mécanique.
Pour éviter cela, on programme une **rampe de vitesse** : la consigne de PWM augmente progressivement.
Exemple de code :
void loop() {
// Rampe de vitesse en marche avant
for (int alpha = 0; alpha <= 100; alpha += 5) {
moteurAvant(alpha); // vitesse croissante
delay(200); // attendre 200 ms entre chaque pas
}
delay(2000); // maintenir pleine vitesse 2 s
// Rampe de vitesse en marche arrière
for (int alpha = 0; alpha <= 100; alpha += 5) {
moteurArriere(alpha);
delay(200);
}
delay(2000); // maintenir pleine vitesse 2 s
}
Travail demandé :
* Observer la différence de comportement avec et sans rampe.
* Mesurer le courant moteur avec l’oscilloscope.
* Expliquer en quoi la rampe améliore l’adhérence et limite les efforts mécaniques.
===== G) Marche avant arrière commandée par un potentiomètre =====
Règle:
* à position médiane du potentiomètre, le moteur est l'arrêt
* sur la partie gauche, le moteur tourne à gauche (vitesse max sur position max gauche)
* sur la partie droite, le moteur tourne à droite (vitesse max sur position max droite)
===== H) Commande par clavier =====
**Objectif :**
Programmer la carte Arduino pour commander un moteur à courant continu via un **pont en H**, en saisissant les consignes directement depuis le **moniteur série**.
---
### Principe de fonctionnement
1. L’utilisateur entre au clavier :
- le **sens de rotation** du moteur :
`F` pour *Forward* (horaire) ou `R` pour *Reverse* (antihoraire),
- le **rapport cyclique** (en %),
- la **position à atteindre** (en nombre d’impulsions du codeur).
2. Le programme :
- commande le moteur à l’aide du **pont en H**,
- fait varier la vitesse par **PWM**,
- compte les impulsions du **codeur incrémental**,
- **arrête automatiquement** le moteur lorsque la position demandée est atteinte.
---
graph TD
A([Début]) --> B[Initialisation Arduino : broches, interruption, série]
B --> C[/Lecture du rapport cyclique et position finale/]
C --> D[Compteur = 0 / alphafinal = 50]
D --> E{compt < position finale ?}
E -->|Oui| F[/Sortie moteur AV : PWM augmente/]
F --> G[alphafinal = alphafinal + 1]
G --> E
E -->|Non| H[/Arrêt moteur PWM = 0/]
H --> I[/Afficher position finale et "Cycle terminé"/]
I --> J([Fin])
### Travail demandé
1. **Analyser** le programme fourni.
2. **Tester** son fonctionnement à l’aide du **moniteur série**.
3. **Observer** le comportement du moteur selon :
- le sens choisi,
- le rapport cyclique,
- la position demandée.
4. **Améliorer** le programme en ajoutant une rampe de vitesse pour la marche arrière.
// === Commande moteur CC avec pont en H et codeur incrémental ===
// Version optimisée avec fonction lireTexteSerie() pour lecture clavier
// --- Sorties PWM vers le pont en H ---
const int sortie_commande_AV_pont_H = 3;
const int sortie_commande_AR_pont_H = 6;
// --- Entrées codeur incrémental ---
const int canalA = 2; // interruption 0
const int canalB = 11;
// --- Variables globales ---
volatile long compt = 0; // compteur codeur (volatile car modifié par interruption)
String sens = "";
String consigne = "";
String position = "";
byte alpha = 0; // consigne en PWM
byte alphafinal = 0; // rampe progressive
int positionfinale = 0;
// --- Prototypes ---
void moteurAvant(int alpha_percent = 100);
void moteurArriere(int alpha_percent = 100);
void Reagir();
String lireTexteSerie(String messageInvite);
void choix_sens();
void choix_consigne();
void choix_position();
// === SETUP ===
void setup() {
Serial.begin(9600);
pinMode(sortie_commande_AV_pont_H, OUTPUT);
pinMode(sortie_commande_AR_pont_H, OUTPUT);
pinMode(canalB, INPUT);
attachInterrupt(digitalPinToInterrupt(canalA), Reagir, RISING);
Serial.println("Système prêt !");
}
// === BOUCLE PRINCIPALE ===
void loop() {
choix_sens();
if (sens == "F" || sens == "R") {
choix_consigne();
choix_position();
alphafinal = 50; // démarrage progressif
if (sens == "F") {
Serial.println("→ Moteur sens horaire (avant)");
digitalWrite(sortie_commande_AR_pont_H, LOW);
compt = 0;
while (compt < positionfinale) {
moteurAvant(map(alphafinal, 0, 255, 0, 100));
if (alphafinal < alpha) alphafinal++;
delay(4);
}
moteurAvant(0);
Serial.print("Position finale atteinte : ");
Serial.println(compt);
}
else if (sens == "R") {
Serial.println("← Moteur sens antihoraire (arrière)");
digitalWrite(sortie_commande_AV_pont_H, LOW);
compt = 0;
while (-compt < positionfinale) {
moteurArriere(map(alphafinal, 0, 255, 0, 100));
if (alphafinal < alpha) alphafinal++;
delay(4);
}
moteurArriere(0);
Serial.print("Position finale atteinte : ");
Serial.println(-compt);
}
Serial.println("Cycle terminé !");
}
}
// === Fonction générique pour lecture série ===
String lireTexteSerie(String messageInvite) {
Serial.println(messageInvite);
String texte = "";
char car = 0;
while (car != '\n') {
if (Serial.available() > 0) {
car = Serial.read();
if (car != '\n' && car != '\r') {
texte += car;
}
}
}
texte.trim(); // Supprime espaces et retours éventuels
return texte;
}
// === INTERRUPTIONS CODEUR ===
void Reagir() {
if (digitalRead(canalB) == HIGH)
compt++;
else
compt--;
}
// === Commandes moteur ===
void moteurAvant(int alpha_percent) {
int alpha_pwm = map(alpha_percent, 0, 100, 0, 255);
digitalWrite(sortie_commande_AR_pont_H, LOW);
analogWrite(sortie_commande_AV_pont_H, alpha_pwm);
}
void moteurArriere(int alpha_percent) {
int alpha_pwm = map(alpha_percent, 0, 100, 0, 255);
digitalWrite(sortie_commande_AV_pont_H, LOW);
analogWrite(sortie_commande_AR_pont_H, alpha_pwm);
}
// === Fonctions d’interaction ===
void choix_sens() {
sens = lireTexteSerie("Quel fonctionnement souhaitez-vous ? (F = Forward, R = Reverse)");
}
void choix_consigne() {
consigne = lireTexteSerie("Quel rapport cyclique souhaitez-vous (20 à 100 %) ?");
alpha = map(consigne.toInt(), 0, 100, 0, 255);
Serial.print("→ PWM = ");
Serial.println(alpha);
}
void choix_position() {
position = lireTexteSerie("Quelle position souhaitez-vous atteindre (20 à 2000) ?");
positionfinale = position.toInt();
Serial.print("→ Position finale = ");
Serial.println(positionfinale);
}