Mesures

Besoins de mesures.

Quelques projets de mesures afin de vérifier des fonctionnements...

Le Servo-moteur SG90

Caractéristique du Servo-moteur SG90

SG90
Torque25.0 oz-in (1.80 kg.cm) at 4.8V
Speed0.1sec/60° (4.8V)
Voltage4.0V to 7.2V, 4.6V - 5.2V nominal
Running current with 5V supply (no mechanical load)220 ±50mA
Stall current with 5V supply (horn locked)650 ±80mA
Idle current with 5V supply6 ±10mA
Dimensions0.91in x 0.48in x 1.14in (23mm x 12.2mm x 29mm)
Weight0.32oz (9g)
Dead band width10µs
Operating Temperature range-22°F to 140°F (-30°C to 60°C)
Universal "S" type connector fits most receivers

Fonctionnement du servo-moteur

Contrôler un servo-moteur à l'aide d'un microcontrôleur est simple, aucun pilote externe.

Le principe de base est assez simple. Il suffit d'envoyer une impulsion et c'est le temps que durera cette impulsion qui déterminera l'angle du servo-moteur.

ce temps d'impulsion est de quelques de quelques millisecondes et doit être répété à intervalle régulier ( 50Hz soit toutes les 20 ms ).

Si le temps d'impulsion varie d'un fabricant à l'autre, les valeurs sur ce schéma sont assez standard.

Programme pour réglage des positions avec le port série

Principe de fonctionnement

//Réglage du Servo moteur par position angulaire
//(c) sammy76.free.fr
#include "Servo.h>"
 
Servo myservo;  // Créer l'objet Servo pour contrôler servomoteur
                // 9 servomoteurs maximum 
 
int pos = 0;    // variable de sauvegarde de positon servo 

static void videbuffer_att() {
  while (Serial.available() > 0) {
    char t = Serial.read();
  }
  do {} while (!Serial.available());
}

static int readnumber() {
  //lit une chaine de caractère et la converti en entier
  videbuffer_att();
  while (Serial.available() == 0);
  int newpos = Serial.parseInt(); //lit int
  return newpos;
}

void setup() 
{ 
  myservo.attach(9);  // Attache le servo à  la broche 9 de l'arduino Nano
  Serial.begin(9600);
  Serial.print("Configuration du Servo 9g\nSaisir la position initiale\n");
} 
 
 
void loop() 
{ 
  
  pos = readnumber();
  /*if (pos<10){
    pos=10;
  }*/
  myservo.write(pos);
  Serial.print("Position : ");Serial.println(pos);
  delay(1000); // wait for one seconds
    
} 

Dimensions du servo-moteur


Contrôle de servomoteurs avec Microcontrôleurs AVR type Attiny45.

Vous pouvez utiliser la fonction micro-contrôleurs PWM AVR pour contrôler des servomoteurs.
Le module Timer/Counter0 qui est une minuterie sur 8bits et dispose de deux canaux PWM (A et B). La fréquence du processeur est 8MHz
De cette façon, le PWM avec génération automatiquement des signaux pour s'occuper des servomoteurs et le micro-contrôleurs est libre de faire d'autres tâches.

Recherche de la valeur de TOP

Avec un choix entre 9 ; 64 ; 256 ; 1024, j'ai choisi le pré-diviseur par 64. Donc, la minuterie se 8MHz / 64 = 125kHz (soit des périodes de 8μS).
$F_{pwm}=\dfrac{F_{cpu}}{N(1+TOP)}$ avec $F_{pwm}$ la fréquence du PWM ; $F_{cpu}$ fréquence du CPU (ici 8MHz) ; $N$ prédiviseur (ici 64)
$TOP=\dfrac{F_{cpu}}{F_{pwm}\cdot N}-1$
$TOP=\dfrac{8~000~000~Hz}{50~Hz\times 64}-1=2499$
FREQUENCY = ((CLOCK-SPEED/PRE-SCALER)/256)

Modifier un micro-servomoteur (SG90) pour une rotation continue

Un servomoteur normal tourne à environ 180 degrés. De toute évidence, nous ne pouvons pas l'utiliser pour un véhicule roulant sur roues.
Etapes pour retirer le limiteur du servo et le faire tourner à 360 degrés, afin que vous puissiez l'utiliser comme si vous utilisiez un moteur à courant continu!
Le limiteur actuel est constitué de 2 parties: le potentiomètre et la boîte de vitesses. Ce que nous devons faire, c'est casser la connexion du potentiomètre avec la carte de circuit imprimé interne et retirer un bouton d'un pignon de la boîte de vitesses.
Retirer du couvercle et de la boîte de vitessesDessouder, puis Retirer le moteur à courant continu et le potentiomètre
Remplacement du potentiomètre
par deux résistances, soit la moitié
de la valeur du potentiomètre
Ici 2,7kΩ
Couper la patte qui dépasse de l'engrenage

Récupérer la position d'un SG90

A l'intérieur du SG90

Un servomoteur est constitué d'un moteur avec réducteurs d'une carte électronique et d'un potentiomètre linéaire qui permet à la carte électronique de vérifier la position actuelle du servo.
Je vous propose d'utiliser cette valeur envoyée par le potentiomètre afin de connaitre la position du servomoteur.
En effet le potentiomètre est mécaniquement lié directement au palonnier du servomoteur.
Donc la résistance obtenue au bornes du potentiomètre correspond à la position du palonnier.

Il suffit pour cela de souder un fil sur la patte du milieu du potentiomètre. Ensuite une fois le servomoteur en action, on peut mesurer la tension entre la masse en notre nouvelle broce du servo (patte du milieu du potentiomètre) d'environ 0.9V lorsque le palonnier est tourné tout a droite, et 1,7V lorsque le palonnier est tout a gauche.

Le programme devra évidement effectuer un genre d'étalonnage au début de l'exécution.
Positionner le servo tout a droite, et voire la valeur du capteur, puis positionner le servo à gauche et voir la valeur du capteur.
Ces deux valeurs vont nous servir de référence min et max de la variation.
En effet la variation mesurée est de 1.7V - 0.9V = 0.8V. Votre entrée analogie doit avoir une définition suffisante pour avoir un peu de précision.
En effet sur une variation de 0.8V il s'agit de représenter théoriquement les 180° de rotation. Donc l'entrée analogique devra être capable de repérer des variations de 5mV pour avoir une précision d'environ 1°.
Il va de sois qu'il est aussi envisageable d'enregistrer les valeurs minimales et maximales de la tension obtenue au bornes du potentiomètre pour ne pas avoir a effectuer l'étalonnage a chaque initialisation.
Les moteurs Nema

Les désignations

Un moteur NEMA (National Electrical Manufacturers Association)
Nous prendrons comme exemple : 17HS2408 NEMA DDMMLLLCCCIVVVSSSW
DDDimensions du moteur, son diamètre ou longueur côté du carré de la face avant, hors tout, exprimé en dixièmes de pouce ,arrondi.
Exemple pour un NEMA17 : $1,7\times25,4=43,18mm$
MMtype de montage (pouce x 10)+diamètre,
"C" trous taraudés en façade
"D" tôle arriere
LLLLLL : longueur (pouce x 10)
-séparation
CCCcourant par phase (A x 10)
Iclasse d'isolation, définit la temp max de travail en °F
  • Class A is 221°F - 105°C
  • Class B is 266°F - 130°C
  • Class F is 311°F - 155°C
  • Class H is 356°F - 180°C
VVVTension par phase (V x 10)
SSSNombre de pas/tour
Wcode bobinage
  • A : 2 fils
  • B : 3 fils
  • C : 4 fils
  • D : 5 fils
  • E : 6 fils
  • F : 8 fils
Les moteurs les plus utilisés :
Nema 8, 11, 14, 17, 23, 24, 34 et 42
La fréquence d'utilisation d'un NEMA17 est d'environ 8000Hz - 150.00 tr/min

Branchement 17HS2408


Bien que le moteur pas à pas que nous utiliserons ici 17HS2408 est soit conçu pour 0,6A, nous ne devons jamais le régler à sa capacité de courant maximale.
Il est fortement recommandé de réduire la quantité de courant vers le moteur d'au moins 10 %, ce qui dans notre cas se traduirait par environ 0,54 A.
TMC2208
$Vref=I\cdot \sqrt{2}=\dfrac{0,6}{1.1}\times \sqrt{2}=0,771V$
A4988
$Vref=I\times 0,8=\dfrac{0,6}{1.1}\times 0,8=0,436V$
DRV8825
$Vref=\dfrac{I}{2}=\dfrac{0,6}{2.2}=0,273V$
17HS2408
Angle par pas
deg
Courant
A
Résistance/phase
Ω
couple de maintien
Nmm
Nombre de fils
1.80.681204

La fixation se fait par 4 taraudage M3 positionner tous les 31mm

Drivers

 A4988DRV8825LV8729TMC2208TMC2100TMC2130
Courant par default2A1.3A0.8A0.7A0.5A0.76
Vref par default0.8V0.65V0.4V1V0.65V1V
Courant Maxi2A2.5A1.5A1.41A1.2A2A
Formule$i=\dfrac{V_REF}{0.8}$$i=V_REF\times 2$$i=\dfrac{V_REF}{\sqrt 2}$ $i=\dfrac{V_REF\times 1.9}{2.5}$
Ajustement du courantHoraire Augmente
Anti-horaire Diminue
Horaire Diminue
Anti-horaire Augmente
Horaire Augmente
Anti-horaire Diminue
Micropas1, 1/2, 1/4, 1/8, 1/161, 1/2, 1/4, 1/8, 1/16,
1/32
1, 1/2, 1/4, 1/8, 1/16,
1/32, 1/64, 1/128
1, 1/2, 1/4, 1/8, 1/161/161, 1/2, 1/4, 1/8, 1/16,
1/256
Moteur8V-35V8.2V-45V6V-36V4V-35V<1.2A4.75V-46V
SpécificationsPas cherCourant fortSilencieuxSilencieux, lissage
Il est important que le courant maximale soit prévu dans le driver, sinon le moteur chauffe, vibre voir décède, snif...
Pour cela il faut calibrer la tension $V{REF}$, celle-ci se prends entre le GND et le potentiomètre (Et oui, directement dessus). En débranchant le moteur, il y a juste à alimenter le composant.
Concernant les drivers, le DRV8825 offre une résolution micro-stepping au 1/32 de pas; le A4988 est limité à 1/16 de pas.
Les broches de sélection activée pour le mode 1/16 de pas sur le A4988 correspond à 1/32 de pas sur le DRV8825. A l'exception de ce cas, toutes les autres configurations M0-M1-M2 de résolutions micro-stepping sont identiques sur le DRV8825 et le A4988.
Les impulsions STEP HAUT et BAS doivent avoir un temps d'au moins 1.9 µs; mais peut descendre jusqu'à 1 µs avec un A4988
Le DRV8825 supporte une tension maximale plus élevée qu'un A4988 (45 V contre 35 V).

Driver A4988

$I_{MAX}=\dfrac{V_{REF}}{8\cdot R_{CS}}$
$V_{REF}=8\cdot I_{MAX} \cdot R_{CS}$

$R_{CS}$ est la résistance de détection de courant; Les versions originales de cette carte utilisaient des résistances de détection de courant de 0,050 Ω, mais le fabricant est passés à des résistances de détection de courant de 0,068 Ω en janvier 2017, ce qui rend plus utile la plage du potentiomètre de réglage.
Ainsi, par exemple, si vous souhaitez régler la limite de courant à 1 A et que vous avez une carte avec des résistances de détection de 68 mΩ, vous définirez VREF sur 540 mV. Cela garantit que même si le courant à travers chaque bobine change d'un pas à l'autre, l'amplitude du vecteur de courant dans le moteur pas à pas reste constante à 1 A:
$\sqrt{I_{COIL1}^2 + I_{COIL2}^2}= I_{MAX}= 1 A$
Si vous souhaitez au contraire que le courant traversant chaque bobine soit de 1 A en mode pas à pas complet, vous devez définir la limite de courant à 40% plus élevée, ou 1,4 A, car les bobines sont limitées à environ 70% du courant défini limite en mode pas à pas (l'équation ci-dessus montre pourquoi c'est le cas). Pour ce faire avec une carte avec des résistances de détection de 68 mΩ, vous devez régler VREF à 770 mV.

Pour le moteur 17HS2408 le courant maximum est de 0,6A
$V_{REF}=8\times 0,6 \times0,068=0,3264V$

Driver DRV8825

$V_{REF}=\dfrac{I_{MAX}}{2}$
Pour mon 17HS2408, $V_{REF}=\dfrac{0,6}{2}=0,3V$
Avec 10% de marge cela fait 0,27V

Détails

MoteurA4988 (Imax=2A)A8825 (Imax=2,5A)
ImoteurInom (71%)R050R100R200R050R100R200
Vref (mV)Vref (mV)
0,10,072856112173570
0,20,14561122243570140
0,30,218416833652105210
0,40,2811222444870140280
0,50,3614428857690180360
0,60,43172344688107215430
0,70,50200400800125250500
0,80,57228456912142285570
0,90,642565121 024160320640
1,00,712845681 136177355710
1,10,783126241 248195390780
1,20,853406801 360212425850
1,30,923687361 472230460920
1,40,993967921 584247495990
1,51,074288561 7122675351070
1,61,144569121 8242855701 140
1,71,214849681 9363026051 210
1,81,285121 0242 0483206401 280
1,91,355401 0802 1603376751 350
2,01,425681 1362 2723557101 420
2,11,49   3727451 490
2,21,56   3907801 560
2,31,63   4078151 630
2,41,70   4258501 700
2,51,78   4458901 780

Driver TMC2208



Leur courant ce règle en RMS et non pas en continu, donc pour, par exemple, un moteur qui consomme 1.5A par phase nominal, il faut convertir ce courant en RMS, ce qui ce fait en divisant par $\sqrt 2$
Exemple pour 1,5A, $RMS=\dfrac{1,5}{\sqrt 2} = 1,06A$, c'est la valeur que l'on utilise pour régler le Vref des TMC2xxx, donc on règle Vref à 1.06V.
Ne pas oublier que les Imax pour les 2100 et 2130 sont de 1.2A RMS max, donc Vref à 1.2V max (Inom de 1.7A max) et pour le 2208 de 1.4A RMS max, donc Vref à 1.4V max (Inom de 2A max).
MS2MS1PasInterpolationMode
001/8oui jusqu'à 256Stealthchop2
011/2oui jusqu'à 256Stealthchop2
101/4oui jusqu'à 256Stealthchop2
111/16oui jusqu'à 256Stealthchop2

Le mode UART

Les avantages du mode UART:
  1. Le courant du moteur peut être réglé arbitrairement par le micrologiciel.
  2. Les micro-étapes peuvent être définies arbitrairement par le micrologiciel (jusqu'à 256 micro-étapes réelles)
  3. Les micropas réels et interpolés peuvent être combinés pour obtenir
  4. Le micrologiciel peut basculer dynamiquement les moteurs pas à pas entre les modes stealthChop2 et spreadCycle via UART.>/li>
  5. Lorsque le moteur ne bouge pas, le courant de veille du moteur peut être réduit dynamiquement (via UART).
"stealthChop2™"-for quiet positioning
"spreadCycle™"-for high speed and high dynamics

Mode de transmission

chaque octet est LSB ... MSB, premier octet transmis
Mot de 64 bits pour la transmission
0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
Sync + Reservé Adresse Esclave R/W Registre 7bits 32 bits données CRC
1 0 1 0 Réservé (sans souci mais inclus dans le CRC) SLAVEADDR=0 Adresse du registre 7bits 1 octets de données 3, 2, 1, 0 (octet haut à bas CRC
Un quartet de synchronisation précède chaque transmission vers et depuis le TMC22xx et est incorporé dans le premier octet transmis, suivi d'un octet d'adressage (0 pour TMC22xx).
Chaque transmission permet une synchronisation du diviseur de débit en bauds interne avec l'horloge maître.
La vitesse de transmission réelle est adaptée et les variations de la fréquence d'horloge interne sont compensées.
Ainsi, le débit en bauds peut être librement choisi dans la plage valide.
Chaque octet transmis commence par un bit de démarrage (logique 0, niveau bas sur SWIOP) et se termine par un bit d'arrêt (logique 1, niveau haut sur SWIOP).
Le temps de bit est calculé en mesurant le temps entre le début du bit de début (transition 1 à 0) et la fin de la trame de synchronisation (transition 1 à 0 du bit 2 au bit 3). Toutes les données sont transmises par octet. Les mots de données de 32 bits sont transmis avec l'octet le plus élevé en premier.

Une vitesse de transmission minimale de 9000 bauds est autorisée, en supposant une horloge de 20 MHz (pire des cas pour une faible vitesse de transmission).
Le débit en bauds maximum est de fCLK / 16 en raison de la stabilité requise de l'horloge en bauds.
L'adresse esclave SLAVEADDR est toujours 0 pour le TMC22xx.

Documentation complète

Vitesse et accélération

source : embedded
Un nouvel algorithme d'accélération des moteurs pas à pas permet de paramétrer et de calculer des profils de vitesse en temps réel.
Cet algorithme peut fonctionner sur un microcontrôleur bas de gamme en utilisant uniquement des opérations arithmétiques à virgule fixe simples et aucune table de données.
Il développe une approximation précise de la synchronisation d'une rampe linéaire avec une accélération et une décélération constantes.

La fréquence de la minuterie du microcontroleur doit être la plus élevée que possible tout en permettant de longs délais lorsque le moteur est accéléré à partir de l'arrêt.
Une fréquence de minuterie de 1MHz a été utilisée. Une vitesse maximale du moteur de 300 tr/min équivaut alors à un compte de retard de 1 000.
Il est nécessaire d'avoir une résolution de minuterie élevée pour donner une accélération douce à grande vitesse.

Notation et formules de base

Retard (sec) programmé par le compteur de temps $c$ :
Equation 1$\delta t=\dfrac{c}{f}$$f$ = fréquence de la minuterie (Hz).
Vitesse du moteur ( rad /sec) à compteur horaire fixe $c$ :
Equation 2$\omega=\dfrac{\alpha\cdot f}{c}$$\omega$ angle de pas du moteur (radian)
$1~rad = 180 /\pi = 57,3 \deg$
$1~rad\cdot s^{-1} = 30 /\pi = 9,55~tr\cdot min^{-1}$.
Accélération $( rad\cdot sec^{-2} )$ à partir des comptes de minuterie adjacents $c_1$ et $c_2$ :
Equation 3$\omega \prime =\dfrac{2\dot \alpha\cdot f^2\cdot (c_1-c_2)}{c_1\cdot c_2 \cdot (c_1+c_2)}$$\omega$ angle de pas du moteur (radian)
$1~rad = 180 /\pi = 57,3 \deg$
$1~rad\cdot s^{-1} = 30 /\pi = 9,55~tr\cdot min^{-1}$.
L'équation 3 suppose une vitesse de comptage fixe (équation 2) au milieu de chaque intervalle de pas (équation 1), comme sur une rampe linéaire, figure 1. Notez que la résolution de la dérivée est inversement proportionnelle au cube de la vitesse.
Figure 1 : Géométrie de la rampe : déplacement de m =12 pas

Rampe de vitesse linéaire—exacte

Sur une rampe linéaire, l'accélération' est constante et la $vitesse(t) = \omega \prime \cdot t$. L'intégration donne l'angle d'arbre moteur ( t ):
Equation 4$\theta(t)=\int_{0}^{t}\omega(\tau)d\tau=\dfrac{\omega'\cdot t^2}{2}=n\cdot\alpha$$n≥ 0$ numéro de pas (réel). Lorsque l'arbre est à $\theta = n\cdot\alpha$, (entier n ) est la n ième impulsion de pas.
Equation 5$t_n=\sqrt{\dfrac{2\cdot n\cdot \alpha}{\omega'}}$Le décompte exact de la minuterie pour programmer le délai entre les n ième et (n + 1) ième impulsions ($n≥0).
Equation 6$C_n=f\cdot (t_{n+1}-t_n)$Le compte initial $c_0$ se factorise pour donner les équations 7 et 8 :.
Equation 7$C_0=f\sqrt{\dfrac{2\alpha}{\omega'}}$ 
Equation 8$C_n=C_0(\sqrt{n+1}-\sqrt{n})$ 
Notez que c 0 définit l'accélération, proportionnelle à $t_0\big(\frac{1}{c_0}\big)^2$
En temps réel, l'équation 8 nécessiterait le calcul d'une racine carrée pour chaque étape, avec le problème supplémentaire de perte de précision par soustraction.

GRBL


Changer le G54 (10,10,Z0.5)
G10L2P1X10Y10Z0.5 G54 (10,10,Z0.5)
G10L2P2X10Y10Z0.5 G55 (10,10,Z0.5)
G10L2P3X10Y10Z0.5 G56 (10,10,Z0.5)
G10L2P4X10Y10Z0.5 G57 (10,10,Z0.5)
G10L2P5X10Y10Z0.5 G58 (10,10,Z0.5)
G10L2P6X10Y10Z0.5 G59 (10,10,Z0.5)
Décallage d'origine G92
G92X0Y0Z0 à la position ou je me trouve
Longueur d'outil a 50
G43.1 Z50 pour selectionner l'outil T1
G2(sens horaire), G3 (sens anti-horaire) Interpolation circulaire en vitesse travail G2 ou G3 axes décalages (format centre) G2 ou G3 axes R- (format rayon) G2 ou G3 décalages (cercles complet)
G0 G90 X100 Y0
G1 Y100 F200
G2 X150 Y150 I50 J0
G2 X200 Y100 I0 J-50
G1 Y0
X100
    
Moteur 24BYJ-48

20BYJ-48/ 24BYJ-48 /28BYJ-48

C'est une question de diamètre Ø20mm pour le 20BYJ-48, Ø24mm pour le 24BYJ-48, et Ø28 pour le 28BYJ48.

Caractéristique des moteurs pas à pas 24BYJ-48


Câblage du 24BYJ-48

Engrenages internes 24BYJ-48

24BYJ-48 un autre pilote

Il est important que le courant maximale soit prévu dans le driver, sinon le moteur chauffe, vibre voir décède, snif... Pour cela il faut calibrer la tension $V_{ref}$, celle-ci se prends entre le GND et le potentiomètre (Et oui, directement dessus).
En débranchant le moteur, il y a juste à alimenter le composant.
$I_{moteur}=300mA$ auquel on enlève 10% pour éviter les échauffements moteur soit $0.27A$
DRV8825
$i=V_{ref}\times 2$
$V_{ref}=\dfrac{I_{moteur}}{2}=0,135V$

A4988
$i=\dfrac{V_{ref}}{0.8}$
$V_{ref}=I_{moteur}\times 0.8 =0,338V$

TMC2208
$i=\dfrac{V_{ref}}{\sqrt{2}}$
$V_{ref}=I_{moteur}\times \sqrt{2} =0,382V$
Le moteur Brushless Comme son nom l'indique un moteur Brushless ne comporte pas de balais, c'est une machine synchrone à aimants permanents.
Suivant les technologies, les aimants peuvent se trouver sur le Stator ou le Rotor.Le rapport poids/puissance est très favorable par rapport aux moteurs à courant continu.

Caractéristiques des moteurs Brushless

Il faut tout d'abord savoir que le rendement d'un moteur Brushless est de 75 à 95%.
Ce sont souvent des moteurs à 3 phases, U, V et W, la séquences comporte 6 étapes de 60°
Le rotor, alimenté en courant continu, par un système de contacts glissants (bagues), crée un champ magnétique rotorique qui suit le champ tournant statorique avec un retard angulaire θ lié à la charge (plus la charge est importante, plus θ est grand).
Étant donné que le rotor tourne à la même vitesse que le champ tournant, ce moteur ne peut pas être démarré directement sur le réseau 50 Hz.
On peut utiliser un convertisseur de fréquence dont la fréquence augmente progressivement lors de la phase de démarrage (rampe).

Le pilotage
PWM (Modulation de largeur d'impulsion)

Carte de programmation

Moteur DC

Moteur courant continu (Direct Current)
Conversion d'énergie électrique continue, en énergie mécanique.

Caractéristique de ces moteur

Ce sont des moteurs à courant continu (CC ou DC pour direct current). Imaginons une machine électrique alimentée par une source de tension U, constante. Lorsque le moteur tourne à vide (il ne fait pas d'effort) il n'y a pas besoin de fournir de couple, I est très faible et U, ≈ E.
La vitesse de rotation est proportionnelle à U.

Le pont en H

Pour faire tourner un moteur DC dans un sens, il suffit de l'alimenter + à une broche et - à l'autre, pour inverser le sens, il y a juste à inverser la polarité.
Pour réaliser cette opération automatiquement, il est nécessaire de réaliser un pont en H.

Le pont en H permet la commande de moteur dans les deux sens de rotation, sans modification du montage, seulement en modifiant l'état des commutateurs. Les commutateurs sont actionnés deux par deux pour faire tourner le moteur dans un sens ou dans l'autre. Les paires de relais peuvent avoir des puissances différentes selon que l'utilisation ne requiert pas le même couple dans un sens que dans l'autre.

Driver L298N double pont en H

Le double pont en H

Le L298N est un double pont en H, c'est à dire qu'il permet de faire tourner deux moteurs dans un sens ou dans l'autre.
Utilisation avec un arduino, ce qui donne en C:
// connectez les broches du contrôleur de moteur aux broches numériques Arduino
// moteur 1
int enA = 10;
int in1 = 9;
int in2 = 8;
// moteur 2
int enB = 5;
int in3 = 7;
int in4 = 6;
void setup()
{
  // définit toutes les broches de commande du moteur sur les sorties
  pinMode (enA, OUTPUT);
  pinMode (enB, OUTPUT);
  pinMode (in1, OUTPUT);
  pinMode (in2, OUTPUT);
  pinMode (in3, OUTPUT);
  pinMode (in4, OUTPUT);
}
void demoOne ()
{
  // cette fonction fera tourner les moteurs dans les deux sens à une vitesse fixe
  // allumer le moteur A
  digitalWrite (in1, HIGH);
  digitalWrite (in2, LOW);
  // régler la vitesse à 200 hors de la plage possible 0 ~ 255
  analogWrite (enA, 200);
  // allumer le moteur B
  digitalWrite (in3, HIGH);
  digitalWrite (in4, LOW);
  // régler la vitesse à 200 hors de la plage possible 0 ~ 255
  analogWrite (enB, 200);
  delay (2000);
  // change maintenant les directions du moteur
  digitalWrite (in1, LOW);
  digitalWrite (in2, HIGH);  
  digitalWrite (in3, LOW);
  digitalWrite (in4, HIGH); 
  delay (2000);
  // éteignez maintenant les moteurs
  digitalWrite (in1, LOW);
  digitalWrite (in2, LOW);  
  digitalWrite (in3, LOW);
  digitalWrite (in4, LOW);
}
void demoTwo ()
{
  // cette fonction fera fonctionner les moteurs sur la plage de vitesses possibles
  // notez que la vitesse maximale est déterminée par le moteur lui-même et la tension de service
  // les valeurs PWM envoyées par analogWrite () sont des fractions de la vitesse maximale possible 
  // par votre matériel
  // allumer les moteurs
  digitalWrite (in1, LOW);
  digitalWrite (in2, HIGH);  
  digitalWrite (in3, LOW);
  digitalWrite (in4, HIGH); 
  // accélère de zéro à la vitesse maximale
  for (int i = 0; i <256; i ++)
  {
    analogWrite (enA, i);
    analogWrite (enB, i);
    delay (20);
  } 
  // décélère de la vitesse maximale à zéro
  for (int i = 255; i> = 0; --i)
  {
    analogWrite (enA, i);
    analogWrite (enB, i);
    delay (20);
  } 
  // éteignez maintenant les moteurs
  digitalWrite (in1, LOW);
  digitalWrite (in2, LOW);  
  digitalWrite (in3, LOW);
  digitalWrite (in4, LOW);  
}
void loop()
{
  demoOne ();
  delay (1000);
  demoTwo ();
  delay (1000);
}

Utilisation d'un MX1508 comme double pont en H pour deux moteurs

Très bien pour du tout ou rien, avec inversion de sens de marche.

Equipé d'un MOS L298N, module d'entraînement de moteur à courant continu, double pont en H.
Tension d'alimentation du Module 2 V-10 V
Tension d'entrée du Signal 1.8-7 V
Courant de fonctionnement monocanal de 1,5A, le courant de crête jusqu'à 2,5A, faible courant de veille (moins de 0,1μA)
Et enfin ça mini taille : 24,7x21x5mm.
L'inconvénients de ce petit composant c'est qu'il necéssite deux sorties PWM par moteur, pour réguler la vitesse.
La bibliothèque arduino est bien documentée, mais elle n'existe pas en micropython sur RP2040-ZERO

# Librairie MX1508
# Auteur : Samuel Dupré
# (c) sammy76.free.fr
# Date: April 4th, 2022
# Version: 1.0

#Pour deux moteurs uniquement
#Utilisation :
#from mx1508a import MX1508A
#from time import sleep
#CarteMX=MX1508A(9,10,11,12)
#CarteMX.speed(250,-127)
#sleep(5)
#CarteMX.stop()


from machine import Pin,PWM


class MX1508A:
    def __init__(self, in1,in2,in3,in4):
        self.IN1=in1
        self.IN2=in2
        self.IN3=in3
        self.IN4=in4
        self.pwm1=PWM(Pin(self.IN1))
        self.pwm2=PWM(Pin(self.IN2))
        self.pwm3=PWM(Pin(self.IN3))
        self.pwm4=PWM(Pin(self.IN4))
                
        self.pwm1.freq(2000)
        self.pwm2.freq(2000)
        self.pwm3.freq(2000)
        self.pwm4.freq(2000)

    def stop(self):
        self.pwm1.duty_u16(0)  #stop
        self.pwm2.duty_u16(0)  #stop
        self.pwm3.duty_u16(0)  #stop
        self.pwm4.duty_u16(0)  #stop
        
    def speed(self,M1,M2):
        """Vitesse Moteur 1 et 2"""
        def map(x, in_min, in_max, out_min, out_max):
            return int((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)

        #print(Npwm1,Npwm2)
        pwmV1 = map(abs(M1), 0, 255, 5000, 50000)
        if M1>0:
            self.pwm1.duty_u16(pwmV1)
            self.pwm2.duty_u16(0)
        else:
            self.pwm2.duty_u16(pwmV1)
            self.pwm1.duty_u16(0)
            
        pwmV2 = map(abs(M2), 0, 255, 5000, 50000)
        if M2>0:
            self.pwm3.duty_u16(pwmV2)
            self.pwm4.duty_u16(0)
        else:
            self.pwm4.duty_u16(pwmV2)
            self.pwm3.duty_u16(0)
        
        #print(pwmV1,pwmV2)
    def display(self):
        print("IN1\tIN2\tIN3\tIN4")
        print("%d\t%d\t%d\t%d" % (self.IN1,self.IN2,self.IN3,self.IN4))
        print("PWM1\tPWM2\tPWM3\tPWM4")
        print("%d\t%d\t%d\t%d" % (self.pwm1.duty_u16(),self.pwm2.duty_u16(),self.pwm3.duty_u16(),self.pwm4.duty_u16()))
    
	
Maintenant elle existe....
Exemple : Utilisation de moteur DC pour Fred

Utilisation d'un A4988 comme double pont en H

Principe de fonctionnement

Le A4988 est un modules de pilote pas à pas.
Cependant, il m'est venu à l'esprit que pourrait-on utiliser ces modules très bon marché, petits et agréables pour entraîner également des moteurs à courant continu? Lorsque deux moteurs sont branchés sur le module A4988 (au lieu de bobines pas à pas), ils peuvent être entraînés en manipulant la séquence de pas.

Avantages d'utiliser A4988 comme double pont en H :

Les inconvénients

Logique

Les pas à pas sont entraînés par la broche de pas d'impulsions step et la direction est modifiée en réglant la broche dir signal haut ou bas.
Chaque fois qu'un pas d'impulsion est envoyé (HIGH, LOW), le courant dans l'une des bobines change sa direction.
Etapes en pas entier pour un moteur pas à pas bipolaire donc à deux enroulement :

Un code qui fait le bon nombre de pas dans la bonne direction en fonction de l'état actuel des moteurs.

// object avoidance robot, two DC motors driven by a A4988 module

// speed is set with value of 0-255 but 255 is the slowest speed and 0 the fastest


int dirPin = 2;         //Broche Dir du A4988
int stepperPin = 3;     //Broche Step du A4988
int en = 5;             //Broche EN du A4988
int state = 0;
int oldSpeed = 0;
int rst = 4;

const int trigPin = 7;
const int echoPin = 8;



void setup() {
  Serial.begin(9600);
 pinMode(dirPin, OUTPUT);
 pinMode(stepperPin, OUTPUT);
  pinMode(en, OUTPUT);  //enable, active low
  pinMode(rst, OUTPUT); //rst, active low
  digitalWrite(rst, HIGH);
  
     pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  
  digitalWrite(en, LOW);
   digitalWrite(dirPin, HIGH);
  delay(1000);
  randomSeed(analogRead(A0));
}


 
void loop(){ // some simple object avoidance
int view = ultra();
view = min(view,100); //Valeur la plus petit entre view et 100


if (view < 30)        //si view<30
{
  if (view <15)       //si view<15
  {
   state = motors(1, 100); //backwards recul durant 800ms
    delay(800);
  }
 int way = random(4,6);  //hasard gauche droite
 while (ultra() < 30)
  state = motors(way, 70); //tand que la distance est <30 alors avance 
  delay(110);
}

view = map(view, 0, 100, 5, 150);
view = 255-view;
state = motors(2, view); //go forward, speed depends on distance ahead
delay(10);
}




int motors(int robot_direction, int robot_speed) // this determines how many steps to what direction ;  determine le nombre d'étapes vers quelle direction
{
 
  switch(robot_direction){
    
  case 0:
  if(state != robot_direction){
    digitalWrite(en, HIGH); //Desative les moteurs
    robot_direction = state;
  }
  break;
  case 1:
    if(state != robot_direction || oldSpeed != robot_speed){
      digitalWrite(rst, LOW);
      delayMicroseconds(5);
      digitalWrite(rst, HIGH);

     analogWrite(en, robot_speed);  //
    oldSpeed = robot_speed;

  
     step(2);
  }
  break;
  case 2:
    if(state != robot_direction || oldSpeed != robot_speed){
        digitalWrite(rst, LOW);
      delayMicroseconds(5);
      digitalWrite(rst, HIGH);
        analogWrite(en, robot_speed);
    oldSpeed = robot_speed;
     //step(false, 2);
     
  }
  break;
  case 4:
    if(state != robot_direction || oldSpeed != robot_speed){
        digitalWrite(rst, LOW);
      delayMicroseconds(5);
      digitalWrite(rst, HIGH);
     digitalWrite(en, LOW);
       analogWrite(en, robot_speed);
    oldSpeed = robot_speed;
     step(1);
  }
  break;
  case 5:
    if(state != robot_direction || oldSpeed != robot_speed){
        digitalWrite(rst, LOW);
      delayMicroseconds(5);
      digitalWrite(rst, HIGH);
      digitalWrite(en, LOW);
       analogWrite(en, robot_speed);
    oldSpeed = robot_speed;
     step(3);
  }
  break;
  
  }
  
  
  return(robot_direction);
}
  
  
  //make some steps
  void step(int stepsit){
 //digitalWrite(dirPin,dir);
 //delay(50);
 for(int i=0;i<stepsit;i++){
   digitalWrite(stepperPin, HIGH);
   delayMicroseconds(800);
   digitalWrite(stepperPin, LOW);
   delayMicroseconds(800);
 }
}

//Capteur ultra-son
int ultra() {
  // just get distance: 
  
  int result = 0;
  unsigned long duree = 0;
   unsigned long dist = 0;
  for (int i = 0; i<3; i++)
{
  digitalWrite(trigPin, LOW); 
 delayMicroseconds(2); 
digitalWrite(trigPin, HIGH);

delayMicroseconds(10);

digitalWrite(trigPin, LOW);
delayMicroseconds(1);

 duree = pulseIn(echoPin, HIGH);

 dist = dist +(duree/58.2);
 delay(10);
}
dist /= 3;
delay(10);
Serial.println(dist);
result = dist;
return(result);
}
Les codeurs incrémentaux

La position relative.
Le codeur de position incrémental signal un changement de position angulaire.
Lorsqu’un codeur incrémental est mis sous tension, il ne rapportera sa position angulaire qu’une fois qu’il disposera d’un point de référence à partir duquel il pourra effectuer des mesures.

Fonctionnement

Un information de position basée sur la détection et le comptage d'une suite de 0 ou 1. Qu'elles soient données sous forme magnétique, optique, inductifs, capacitifs ou simplement avec des balais sur un rotor.
Le pas est la distance qui sépare ces 0 et 1, sur une surface réctiligne ou cylindrique, suivant le besoin.
La vitesse de mesure peut-être déterminée par la formule $Vitesse=\dfrac{pas}{periode}$

Position

En ce qui concerne la position, c'est plus délicat, il faut compter en permanence.
Pour déterminer cette position relative à une position initiale, qu'il faut initialiser (comme son nom l'indique).

Mais nous devons également connaitre le sens dans lequel nous comptons, faut-il ajouter ou soustraire les pas
Pour cela il nous faut deux capteurs déphasé d’un quart de période (B) cela ne change rien si nous ajoutons des périodes complètes (exemple : B'). L'implantation des leds n'en sera que plus simple.

Lors de la rotation, chaque capteur récéptionne un signal comme ce qui suit, suivant le sens de rotation ou d'avance:

Nous pouvons donc voir le sens de rotation, en effet, ce n'est pas si visible, alors je vais détailler avec l'utilisation d'un compteur
Observons à la loupe les signaux.
AdditionSoustraction
Lors d'un changement d'état de A
on vérifie l'état de B, si c'est identique, on ajoute 1
Si c'est différent, on enlève 1.
Rien de plus simple
Comme vous pouvez le voir sur l'animation ci-dessous:
Comme nous venons de le voir, un simple interruption sur un microcontrolleur!

Un petit exemple sur arduino

  
volatile int compteur = 0;
volatile int old_compteur=0;
void setup(){
  pinMode(2, INPUT); //A
  pinMode(3, INPUT); //B
  attachInterrupt(0, Int_Calc, CHANGE); //int0 sur P2
  Serial.begin(115200);
  Serial.println("Start");
}
 
void loop(){
  if (compteur!=old_compteur){
    Serial.println(compteur);
  }
}
 
void Int_Calc(){
  old_compteur=compteur;
  if(digitalRead(2) == digitalRead(3)){
    compteur++;
  }else{
    compteur--;
  }
}

En micropython sur ESP8266

from machine import Pin

# declare les broches
SA = Pin(13, Pin.IN)
SB= Pin(12, Pin.IN)

# variable pour l'indication de la position
compteur_position = 0
old_position=0

def fonction_interruptionSA(irq1):
  global compteur_position
  print(SA.value(),SB.value())
  if SB.value() == SA.value(): 
    compteur_position += 1
  else:
    compteur_position -= 1
# declaration de l'interruption SA (sur front tous le front, sans PULL_UP interne):
SA.irq(trigger=3, handler=fonction_interruptionSA)
#Si demi période uniquement sur front montant
#SA.irq(trigger=Pin.IRQ_RISING, handler=fonction_interruptionSA)

while "true":
    if compteur_position!=old_position:
        print(compteur_position)
        old_position=compteur_position

En assembleur sur Attiny85

;             ATtiny85 - 20MHz interne
;                         +-\/-+
;                    PB5 1|-  -|8  Vcc
;                    PB3 2|-  -|7  PB2 T0/Int0 entrée signal A
; entrée signal B    PB4 3|-  -|6  PB1
;                    GND 4|-  -|5  PB0
;                         +----+
.equ    SGA     = PB2   ;Signal A
.equ    SGB     = PB4   ;Signal B

.def    countLL =r2
.def    countLH =r3
.def    countHL =r4
.def    countHH =r5
.def    regA    =r16
.def    zero    =r15
.def    savereg =r11
.def    justone =r12

.cseg
.org 0000
;************************************************
;* Interrupt Vectors                      *
;************************************************
    rjmp    main    ; Reset Handler
    rjmp    VECT_INT0; External Interrupt 0
main:
    ldi     regA,high(RAMEND)   ; set up stack pointer
    out     SPH,regA
    ldi     regA,low(RAMEND)
    out     SPL,regA
    ldi     regA,(1<<CLKPCE)  ; enable the clock prescaler
    sts     CLKPR,regA      ; for 4 cycles
    clr     regA
    sts     CLKPR,regA      ; and div factor to 1
    clr     zero
    ldi     regA,1
    mov     justone,regA
    
    cbi     DDRB,SGA
    cbi     DDRB,SGB
    rcall   start_count ;init compteur
loop:
    BRTC    loop
    clt
    ;Traitement du compteur après modification de la valeur
    
    rjmp    loop
    

;****************************************************
;start_count: configure int0
;****************************************************
start_count:
    cli     
    ldi     regA,(0<<ISC01) | (1<<ISC00) ;Any logical change on INT0 generates an interrupt request.
    out     MCUCR,regA
    ldi     regA,(1<<INTF0) | (0<<PCIE) ; INT0: External Interrupt Request 0 Enable
    out     GIMSK,regA
    clt     ;Flag T=0
    clr     countLL
    clr     countLH
    clr     countHL
    clr     countHH
    sei     
    ret     
    
;****************************************************
; Interruption 0
;****************************************************
VECT_INT0:
    set     ;Spécifie le changement du flag T=1
    in      savereg,SREG ;Avant sauvegarde
    push    regA
    clr     regA
    
    sbic    PINB,SGB    ;skip B=0
    inc     regA
    sbic    PINB,SGA    ;skip A=0
    inc     regA
    SBRC    regA,0      ;
    rjmp    subLL
addLL:
    add     countLL,justone
    adc     countLH,zero
    adc     countHL,zero
    adc     countHH,zero
    rjmp    end_int0
subLL:
    sub     countLL,justone
    sbc     countLH,zero
    sbc     countHL,zero
    sbc     countHH,zero
end_int0:
    pop     regA
    out     SREG,savereg
    reti

La vitesse

Calcul de la vitesse La vitesse d’un mouvement est la dérivée par rapport au temps de sa position : $v(t)=\dfrac{dx(t)}{dt}$
D’un point de vue pratique, on calcule la dérivée d’un signal grâce la distance et au temps entre deux points de mesure de changement d'état.

Un petit exemple sur arduino

  
volatile int compteur = 0; //Position (en nombre de pas) du codeur
volatile bool flagT=False; //Booléen en cas de changement de position
volatile float vit = 0;        // Vitesse (en nombre de pas par seconde) du codeur
volatile unsigned long t = 0;  // temps "courant" (en microsecondes)

void setup(){
  pinMode(2, INPUT); //A
  pinMode(3, INPUT); //B
  attachInterrupt(0, Int_Calc, CHANGE); //int0 sur P2
  Serial.begin(115200);
  Serial.println("Start");
  t = micros();             // Initialisation du temps "courant"
}
 
void loop(){
  if (flagT){
    //traitement en cas de déplacement ici
    Serial.print(compteur);
    Serial.print(" ");
    Serial.println(vit);
    flagT=False;
  }
}
 
void Int_Calc(){
  unsigned long dt = micros() - t;   // Temps écoulé depuis le dernier front
  t += dt;
  flagT=True;
  if(digitalRead(2) == digitalRead(3)){
    compteur++;
  }else{
    compteur--;
  }
  if (dt > 0)   {
      vit = 1e6/dt;  // Calcul de la vitesse (ici en pas par seconde)
   }
}

Montage

Eclairage avec deux led IR:

Soit une alimentation de 3,3V et des leds de 1,2V (30mA maxi), la résistance sera donc de :
$R=\dfrac{3,3V-2\times 1,2V}{0,025}=36\Omega$
Le standard est plutôt 33Ω ce qui représente :
$I=\dfrac{3,3V-2\times 1,2V}{33}=27mA$

L'ensemble du montage avec des photorécepteurs IR

On utilise le fait que les recepteurs inversés vont bloquer la tension lorsqu’ils sont éclairés (on aura donc le maximum correspondant à la pull-up, tandis que non excitée par la lumière IR, une tension plus faible mais clairement visible.).
Régulation PID Le régulateur PID, appelé aussi correcteur Proportionnel, Intégral, Dérivé est un système de contrôle permettant d’améliorer les performances d'un asservissement, d'un système ou d'un procédé en boucle fermée.
La boucle fermée, permet de savoir l'état réel entre la consigne et la commande.

Introduction

L'erreur entre la consigne et la mesure est ici intégrée par rapport au temps et multipliée par une constante qu'il faudra aussi déterminer en fonction du système.

Fonctionnement

Je souhaite remplir un maintenir la température d'une pièce à 20°C avec un radiateur équipé un robinet manuel, plus j'ouvre, plus je chauffe, plus je ferme, moins je chauffe.

Règle 1 :

Plus ma température est basse, plus j'ouvre pour atteindre mon objectif.
L'ouverture est donc proportionnelle à la différence entre la température voulue de 20°c et la vitesse réelle.

$Erreur=T_{consigne}-T_{actuelle}$
Vous êtes à 17°C, vous ouvrez à fond !
Plus vous vous rapprochez de la température, plus vous fermé le robinet.
Une fois que la température de 20°C atteinte, c'est fini.
$Erreur=0$
  • Vous laissez le robinet dans sa position.
  • La température commence à baisser dût à la taille de la pièce et à sa déperdition thermique.
  • Vous recommencez donc à ouvrir le robinet.
  • Plus la température s'approche, plus vous baissez.
  • Vous recommencez jusqu'a atteindre une température légèrement inférieure à celle que vous avez choisi.

Règle 2 :

Si votre température reste longtemps en dessous de la consigne, vous ouvrez de plus en plus.
Vous décidez donc qu'en plus d'ouvrir proportionnellement à l'erreur commise, vous allez mémoriser cette erreur au cours du temps.
Plus l'erreur globale est importante et plus vous ouvrer.
L'erreur globale est donc la somme des erreurs dans le temps, c'est l'intégration $\int_{0}^{t} erreur(t)dt$
  • Lorsque vous stabilisez votre température à 19°C, l'erreur globale augmente et vous vous mettez à ouvrir de plus en plus jusqu’à atteindre 20°C... et le dépasser !
  • Arrivé à 20°C, l'erreur globale est encore positive, donc vous continuez à ouvrir.
  • Au-delà de 20°C, l'erreur est négative et fait donc diminuer d'erreur globale.
  • Vous fermez donc le robinet de plus en plus fortement jusqu’à retourner à 20°C.
  • A 20°C, vous recommencez, l'erreur est passé en négatif et vous continuez à fermer... ainsi de suite jusqu'à arriver à vous stabiliser à 20°C après de multiples oscillations.

Règle 3 :

Arrivé à 20°C, vous vous dites : " ça y est, j'y suis ! Mais je n'ai pas été très efficace...
Ne faudrait-il pas rajouter une troisième règle afin d'être plus performant ? ".
C'est alors que vous décidez d'anticiper bonne ouverture.
  • Plus votre ouverture se rapproche de la l'ouverture optimale, plus vous ouvrez le robinet et plus elle s'éloigne de la tempéraure optimale, plus vous ouvrez !
  • Ainsi, si vous vous rapprochez rapidement des 20°C, vous fermez rapidement, afin de ne pas dépasser les 20°C.
  • Ainsi, vous réduisez les oscillations et vous vous stabilisez rapidement la température souhaitez !

Mathématiquement

Le contrôle par PID est une méthode de régulation souvent employée pour les asservissements.
Vous ne savez pas ce qu'est un asservissement ? Et bien, c'est un système, capable d'atteindre et de maintenir une consigne grâce aux mesures qu'il effectue.

Imaginez vous, par exemple, dans une voiture sur l'autoroute. Vous souhaitez rouler à 130Km/h sans avoir à appuyer sur l'accélérateur.
La commande de vitesse de croisière de votre voiture devra par elle-même maintenir cette vitesse.
À l'approche d'une pente le système "s'aperçoit" que pour une même puissance au niveau du moteur, il n'atteint plus la consigne des 130Km/h et rajoutera un petit coup d'accélération.
Oui mais de combien ? Et combien de temps faudra t-il au système pour se stabiliser autour de la consigne ?

C'est tout le problème de l'asservissement et le contrôle par PID est un moyen de le résoudre !

Le PID est le régulateur le plus utilisé dans l'industrie.
L'idée de cet organe de contrôle est de modifier intentionnellement la valeur de l'erreur qui subsiste entre la consigne et la mesure effectuée.

Par exemple de la cas d'un asservissement en position l'erreur serait : $\epsilon = c(p) - s(p)$

Proportionnel

$Consigne(t)=K_p\cdot \epsilon(t)$
Ce qui en Laplace donne : $Consigne(p)=K_p\cdot \epsilon(p)$

Intégral

Au contrôle proportionnel, nous pouvons ajouter l'intégration de l'erreur. Dans ce cas nous obtenonsune régulation PI.
$Consigne(t)=K_p\cdot \epsilon(t)+K_i \int \epsilon(\tau) \,d\tau $
Ce qui en Laplace donne : $Consigne(p)=K_p\cdot \epsilon(p)+K_i\dfrac{\epsilon(p)}{p}$

Dérivé

Pour obtenir un contrôle en PID, il nous faut encore rajouter un terme.
Celui-ci consiste à dériver l'erreur entre la consigne et la mesure par rapport au temps et a le multiplier lui aussi par une constante. $Consigne(t)=K_p\cdot \epsilon(t)+K_i \int \epsilon(\tau) \,d\tau+K_d\dfrac{d\epsilon(\tau)}{d\tau} $
Ce qui en Laplace donne : $Consigne(p)=K_p\cdot \epsilon(p)+K_i\dfrac{\epsilon(p)}{p}+K_d\cdot p \cdot \epsilon(p)=\epsilon(p)\big[K_p+K_i\dfrac{1}{p}+K_d\cdot p\big]$

Nous avons besoin d'un terme dérivé car le contrôle PI peut amener à un dépassement de la consigne, ce qui n'est pas toujours très souhaitable (exemple d'inversion de polarité dans le cas de moteurs électriques).
Le terme dérivé permet de limiter cela. Lorsque le système s'approche de la consigne, ce terme freine le système en appliquant une action dans le sens opposé et permet ainsi une stabilisation plus rapide.

$Commande=K_p\cdot e(t)+K_i \int e(t) \,dt +K_d \dfrac{d}{dt} e(t)$
$u(t) = K_p e(t) + K_i \int_{0}^{t} e(t)dt + K_d \dfrac{de}{dt}$

Implémentation d'un PID sur un robot

Le PID, ce n'est que cela! Mémoriser l'erreur, faire la somme des erreurs et la différence de l'erreur courante avec l'erreur précédente.

Le régulateur proportionnel (P : Règle 1)

La commande de ce régulateur est proportionnelle à l'erreur.
$Commande = K_p \cdot Erreur$
$K_p$ est le coefficient de proportionnalité de l'erreur à régler de façon manuelle.

Le régulateur proportionnel intégral (PI : Règle 1 et 2)

La commande de ce régulateur est proportionnelle à l'erreur, mais aussi proportionnelle à l'intégrale de l'erreur.
On rajoute donc à la commande généré par le régulateur proportionnel, la somme des erreurs commises au cours du temps.
$Commande=K_p\cdot Erreur+K_i \cdot \sum Erreur$
$K_i$ est le coefficient de proportionnalité de la somme des erreurs. Il faut aussi le régler de façon manuelle.

Le régulateur proportionnel dérivé (PD : Règle 1 et 3)

La commande de ce régulateur est proportionnelle à l'erreur, mais aussi proportionnelle à la dérivée de l'erreur.
La dérivée de l'erreur correspond à la variation de l'erreur d'un échantillon à l'autre et se calcule simplement en faisant la différence entre l'erreur courante et l'erreur précédente (c'est une approximation linéaire et locale de la dérivée).
$Commande=K_p\cdot Erreur+K_d \cdot (Erreur - Erreur~précédente)$
$K_d$ est le coefficient de proportionnalité de la variation de l'erreur. Il faut régler ce coefficient manuellement.

Le régulateur proportionnel intégrale dérivé (PID : Règle 1,2 et 3)

Ici, la commande est à la fois proportionnelle à l'erreur, proportionnelle à la somme des erreurs et proportionnelle à la variation de l'erreur.
$Commande=K_p\cdot Erreur+K_i \cdot \sum Erreur+K_d \cdot (Erreur - Erreur~précédente)$
Vous devez donc faire une mesure sur votre système pour pouvoir calculer l'erreur et ainsi appliquer le PID.
Cette mesure est à faire régulièrement à une certaine fréquence d'échantillonnage.
Tous les x millisecondes, faire :
    erreur = consigne - mesure;
    somme_erreurs += erreur;
    variation_erreur = erreur - erreur_précédente;
    commande = Kp * erreur + Ki * somme_erreurs + Kd * variation_erreur;
    erreur_précédente = erreur

Comment régler les coefficients d'un PID ?

Le réglage des coefficients $K_p, K_i$ et $K_d$ d'un PID peut se faire "à la main" par essais/erreurs.
Tout d'abord, sachez qu'il ne sert à rien de vouloir régler les trois coefficients en même temps !
Il y a trop combinaisons possibles et trouver un triplet performant relèverait de l'exploit. Il vaut mieux y aller par étape.
En général, pour régler ces coefficients, on donne au système une consigne fixe (exemple : pour un moteur : tourne à 3 tours par seconde) et on observe la réponse du système (exemple : l'évolution du nombre de tours par seconde du moteur au cours du temps).

Le PID parfait n'existe pas, tout est une question de compromis.
Certaines applications autoriseront un dépassement afin d'améliorer le temps de stabilisation, alors que d'autres ne l'autoriseront pas (exemple, contrôler le déplacement d'un outil. S'il y a dépassement dans le PID, l'outil dépassera dimension).
Tout dépend donc du cahier des charges. Chacun des coefficients à un rôle à jouer sur la réponse à une consigne :
Attention, les coefficients $K_i$ et $K_d$ dépendent de la fréquence d'échantillonnage du système !
En effet, l'intégrateur fera la somme des erreurs au cours du temps!
Si on échantillonne deux fois plus vite, on sommera deux fois plus d'échantillons. Du coup, le coefficient $K_i$ devra être divisé par 2.
A l'inverse, pour le dérivateur, si on double la fréquence d'échantillonnage, il faudra doubler le coefficient $K_d$ afin de garder les mêmes performances du PID.
Plus la fréquence d'échantillonnage est élevé et plus le PID sera performant. (En effet, plus on échantillonne souvent et plus l'intégration et la dérivée seront précises). Quelques remarques sur les coefficients
Quand on augmente coefficient de proportionnalité $K_p$
Quand on augmente coefficient de intégral $K_i$
Quand on augmente coefficient de dérivé $K_d$
Règles générales

Exemple en python

Soit un moteur avec un commande entre -1000 et 1000 pour sa vitesse
Petit exemple de calcul de PID.
Kp=4.
Ki=0.
Kd=0.
encoderval=0
consigne=384*4
commande_max=1000.
commande=1.
erreur_precedente = 0
somme_erreurs=0
while encoderval<>consigne:
    encoderval-=(commande/abs(commande))*1 #commande pour la variation
    erreur=encoderval-consigne
    somme_erreurs += erreur
    variation_erreur = erreur - erreur_precedente
    commande = (Kp * erreur) + (Ki * somme_erreurs) + (Kd * variation_erreur)
    if abs(commande)>1000:
        commande=(commande/abs(commande))*1000
        
    print (("commande "+str(commande)+" encoderval "+str(encoderval)))
    erreur_precedente = erreur

Méthode Ziegler–Nichols

Dans un premier temps $K_I=0$ et $K_D=0$.
On augmente $K_P$ jusqu'à ce que la sortie oscille de manière importante.
Alors on prends $K_U=K_P$ gain maximal (ou gain critique).
On note $T_U$ la période d'oscillation du signal.
Ziegler–Nichols en boucle fermée
Performances rapides (1/4 du rapport d'amortissement)
Type de contrôle$K_P$$T_I$$T_D$$K_I$$K_D$
P$0,5\cdot K_U$----
PI$0,45\cdot K_U$$0,83\cdot T_U$-$0,54\dfrac{K_U}{T_U}$-
PD$0,8\cdot K_U$-$0,125\cdot T_U$-$0,1\cdot K_U\cdot T_U$
PID$0,6\cdot K_U$$0,5\cdot T_U$$0,125\cdot T_U$$1,2\dfrac{K_U}{T_U}$$0,075\cdot K_U\cdot T_U$
Performances normales (du dépassement)
P$0,2\cdot K_U$----
PI$0,18\cdot K_U$$0,83\cdot T_U$---
PID$0,25\cdot K_U$$0,5\cdot T_U$$0,125\cdot T_U$--
lentes (peu de dépassement)
P$0,13\cdot K_U$----
PI$0,13\cdot K_U$$0,83\cdot T_U$---
PID$0,15\cdot K_U$$0,5\cdot T_U$$0,125\cdot T_U$--
lentes (aucun de dépassement)
PID$0,2\cdot K_U$$0,5\cdot T_U$$0,33\cdot T_U$$0,4\dfrac{K_U}{T_U}$$0,066\cdot K_U\cdot T_U$
Ces paramètres sont bien endendu, à ajuster manuellement, mais permettent une approche rapide vers une solution fiable.

Méthode du Régleur

Le réglage du régulateur se fait par petit pas. Le système fonctionnant en boucle fermée, autour du point de consigne, on observe la réponse de la mesure à un échelon de consigne.

En régulation proportionnelle

On cherche la bande proportionnelle correcte en observant la réponse du système à un échelon de consigne

En régulation proportionnelle dérivée

On cherche le temps dérivé correct en observant la réponse du système à un échelon de consigne

En régulation proportionnelle intégrale dérivée

On cherche le temps intégral correct en observant la réponse du système à un échelon de consigne
Remarques