astuces pour contrôler les moteurs à courant continu
Composants et fournitures
![]() |
| × | 1 | |||
| × | 1 | ||||
![]() |
| × | 4 | |||
![]() |
| × | 4 | |||
![]() |
| × | 4 | |||
| × | 1 | ||||
![]() |
| × | 1 | |||
| × | 1 | ||||
![]() |
| × | 1 | |||
![]() |
| × | 1 |
Applications et services en ligne
![]() |
|
À propos de ce projet
Contrôle de la vitesse et de la direction des moteurs à courant continu par contrôleur PID et sorties PWM
Introduction
Presque sur tous les projets disponibles, les créateurs aimeraient contrôler les vitesses et la direction des moteurs ensemble, mais ils préfèrent principalement envoyer directement le PWM aux moteurs à courant continu, même via un circuit de commande de moteur. Mais une telle méthode échoue toujours si vous devez faire correspondre la vitesse exactement comme vous le souhaitez à cause des frères jumeaux appelés « friction » et « inertie ».
(Veuillez ne jamais blâmer les jumeaux. Peu importe et quand vous souhaitez prendre des mesures avec ou sans quelque chose, les jumeaux viennent immédiatement et agissent juste pour vous aider à tout garder sous contrôle. Tandis que l'inertie laisse les choses « penser » avant d'agir, La friction limite leur accélération et leur vitesse. Et la "puissance" n'est "rien" si elle n'est pas sous contrôle total.)

Ainsi, si vous essayez de contrôler la vitesse d'un moteur directement en envoyant votre entrée en tant que signal PWM à la sortie, la vitesse réelle ne correspondra jamais à votre point de consigne et il y aura une différence significative (erreur), comme on peut le voir sur l'image juste au-dessus. Ici, nous avons besoin d'un autre moyen, appelé "contrôle PID".
Contrôleur PID
Qu'est-ce que le contrôle PID ? Imaginez comment conduire votre voiture :pour démarrer à partir de l'arrêt complet, vous devez appuyer sur la pédale d'accélérateur plus que pendant la croisière normale. Lors d'un déplacement à vitesse (presque) constante, vous n'avez pas besoin d'appuyer trop sur la pédale d'accélérateur, mais vous récupérez simplement la perte de vitesse lorsque cela est nécessaire. D'ailleurs, vous le relâchez légèrement si l'accélération est supérieure à vos besoins. C'est aussi la voie de la "conduite efficace".
Ainsi, le contrôleur PID fait exactement la même chose :le contrôleur lit la différence « Signal d'erreur (e) » entre le point de consigne et la sortie réelle. Il a 3 composants différents appelés "Proportionnel", "Intégral" et "Dérivé" ; le nom du contrôleur apparaît donc après la première lettre de chacun. La composante proportionnelle définit simplement la pente (accélération) de la sortie du contrôleur par rapport au signal d'erreur réel. La partie intégrale additionne les signaux d'erreur dans le temps afin de minimiser l'erreur finale. Et le composant dérivé surveille l'accélération du signal d'erreur et effectue un « ajustement ». Je ne donnerai pas ici de détails plus longs et plus longs, veuillez rechercher sur Internet si vous êtes intéressé.
Sur mon programme Arduino, le contrôleur PID est écrit sous la forme d'une fonction illustrée ci-dessous :
float controllerPID(float _E, float _Eprev, float _dT, float _Kp, float _Ki, float _Kd){ float P, I, D ; /* Formule de base :U =_Kp * ( _E + 0.5*(1/_Ki)*(_E+_Eprev)*_dT + _Kd*(_E-_Eprev)/_dT ); */ P =_Kp * _E; /* Composante proportionnelle */ I =_Kp * 0.5 * _Ki * (_E+_Eprev) * _dT; /* Composant Intégral */ D =_Kp * _Kd * (_E-_Eprev) / _dT; /* Composant Dérivé */ return (P+I+D);}
Ensuite, la valeur de sortie finale est déterminée simplement en ajoutant la valeur de sortie actuelle et la sortie du contrôleur PID. C'est la section suivante du programme principal avec le calcul du signal d'erreur et de la sortie du contrôleur PID :
/* Signal d'erreur, sortie du contrôleur PID et sortie finale (PWM) vers le moteur */E =RPMset - RPM;float cPID =controllerPID(E, Eprev, dT, Kp, Ki, Kd);if ( RPMset ==0 ) OutputRPM =0 ; sinon OutputRPM =OutputRPM + cPID ; if ( OutputRPM <_minRPM ) OutputRPM =_minRPM;


Circuit d'alimentation du moteur à courant continu
Bien sûr, il n'est jamais recommandé de piloter un moteur à courant continu directement à partir de la sortie d'Arduino ou d'une carte de commande similaire. Les moteurs à courant continu ont besoin d'une quantité de courant importante par rapport à ceux qui ne peuvent pas être fournis par les sorties des cartes de contrôleur. Vous devez donc piloter des bobines de relais. Mais ici un autre problème se pose :les relais ont des pièces mécaniques et ils peuvent tomber en panne à moyen ou long terme. Nous avons besoin d'un autre composant ici, des transistors.
En fait, les moteurs à courant continu sont entraînés par le courant, pas par la tension. Donc en utilisant ce principe, j'ai décidé d'utiliser des transistors. Mais vous devez choisir le bon transistor capable de supporter le courant du moteur. Au début, faites fonctionner le moteur directement en le connectant à l'alimentation et mesurez le courant dans les conditions de fonctionnement maximales, ou reportez-vous aux spécifications du fabricant.
Après avoir fait cela, j'ai décidé d'utiliser quatre transistors à jonction bipolaire BC307A PNP sur un "pont" pour déterminer la direction du courant à travers les bobines du moteur (en fait, un ensemble NPN BC337 fonctionnerait mieux en raison de sa capacité à résister à des courants de collecteur nettement plus élevés, mais je ne l'ai pas fait je ne les ai pas à l'époque).
Étant donné que les courants du moteur doivent passer par le chemin émetteur-collecteur du transistor, il est nécessaire d'utiliser des transistors avec approximativement les mêmes coefficients de gain de courant continu (hfe). Pour le vérifier, vous pouvez utiliser le circuit suivant et collecter des transistors vous donnant approximativement la même lecture de courant sur l'ampèremètre. Pour concevoir ces circuits préliminaires, vous devez tenir compte des éléments suivants :
- Recherchez « Base-Emetteur sous tension ” (VBEon ) de transistor. C'est la tension minimale à appliquer à la base pour allumer le transistor.
- Trouvez le « gain de courant continu typique » ” (hfe ) du transistor aux alentours du courant du collecteur proche du courant du moteur. Typiquement, c'est le rapport entre Collector Current (IC ) et Courant de base (IB ), hfe =IC / IB .
- Rechercher « Courant continu maximal du collecteur ” de transistors (ICmax ). Le courant continu du moteur ne doit jamais dépasser cette valeur en termes de valeurs absolues. Je peux utiliser BC307 car le moteur que j'utilise a besoin de 70 mA tandis que le transistor a ICmax(abs) =100 mA.



Vous pouvez maintenant déterminer la valeur de la résistance à connecter à la base :au début, vous devez prendre en compte les limites de la sortie de votre carte contrôleur et essayer de maintenir le courant de base aussi bas que possible (il est donc recommandé de sélectionner le gain de courant continu des transistors au maximum Prenez la tension nominale de la sortie sur la carte contrôleur comme « Trigger Voltage ” (VT ), et recherchez le courant de base requis (IBreq ) en divisant le courant du moteur (IM ) au gain de courant continu (hfe ) de transistor :IBreq =IM/hfe .
Déterminez ensuite la tension à chuter à travers la résistance (VR ), en soustrayant la Base-Emitter On-Voltage (VBEon ) à partir de la Tension de déclenchement :VR =VT - VBEon .
Enfin, divisez la tension à supprimer sur la Résistance (VR ) à Courant de base requis (IBreq ) pour trouver la Valeur de résistance (R ):R =VR / IBreq .
[ Formulation combinée :R =( VT - VBEon ) * hfe / IM ]
Sur mon cas :
- Courant du moteur :IM =70 mA
- Paramètres BC307A :ICmax =100 mA, hfe =140 (j'ai mesuré approximativement), VBEon =0,62 V
- Tension de déclenchement : VT = 3,3 V (sortie PWM d'Arduino Due)
- R =5360 ohms (j'ai donc décidé d'utiliser 4900 ohms fabriqués par un 2K2 et un 2K7, pour assurer une couverture complète de la plage de RPM et le circuit aspire juste ~ 0,6 mA de la sortie PWM - une conception appropriée.)
Inverser la direction et notes importantes
Pour inverser la direction d'un moteur à courant continu, il suffit d'inverser le flux de courant. Pour ce faire, nous pouvons simplement faire un circuit en pont avec quatre ensembles de transistors. Sur le schéma; La sortie PWM n°2 active T1A et T1B tandis que la sortie PWM n°3 active T2A et T2B, de sorte que le courant traversant le moteur est modifié.
Mais ici, nous devons considérer un autre problème :lorsqu'ils viennent juste de démarrer, les moteurs électriques aspirent un courant de démarrage transitoire nettement supérieur au courant nominal que vous lisez pendant le fonctionnement normal/continu (les fabricants ne donnent que des courants nominaux). Le courant de démarrage peut être d'environ 130 % de la valeur nominale pour les moteurs de petite puissance et augmente en fonction de la puissance du moteur. Ainsi, si vous alimentez le moteur directement à partir d'une source de tension et inversez immédiatement la polarité pendant le fonctionnement, le moteur aspire des niveaux de courant extrêmes car il n'est pas complètement arrêté. Enfin, cela peut entraîner l'explosion de la source d'alimentation ou la combustion des bobines du moteur. Cela peut ne pas être si important et ressenti pour les moteurs de très petite puissance, mais devient important si les niveaux de puissance sur lesquels vous travaillez augmentent. Mais si vous alimentez le moteur via un transistor ou un ensemble de transistors (comme le couple Darlington), vous n'avez pas ce problème puisque les transistors limitent déjà le courant.
Quoi qu'il en soit, j'ai envisagé une petite routine sur le programme :lorsque la sélection de direction est modifiée pendant l'exécution, le programme met d'abord les deux sorties de commande à zéro et attend le moteur jusqu'à l'arrêt complet. Ensuite, il termine son devoir et redonne tout le contrôle à la routine principale.
if ( Direction !=prevDirection ) { /* Tuer les deux sorties PWM vers le moteur */ analogWrite(_chMotorCmdCCW,0) ; analogWrite(_chMotorCmdCW,0) ; /* Attendre que la vitesse du moteur diminue */ do { RPM =60*(float)readFrequency(_chSpeedRead,4)/_DiscSlots; } while ( RPM> _minRPM ); }

Lecture rapide
Sur mon application, j'ai utilisé un capteur de vitesse HC-020K bon marché. Il envoie des impulsions au niveau de sa tension d'alimentation, et la fiche technique indique que la tension d'alimentation est de 5V. Cependant, ma carte est Arduino Due et elle ne peut pas l'accepter. Je l'ai donc alimenté directement à partir de la sortie 3,3 V de Due, et oui, cela a fonctionné. Et la fonction suivante est écrite pour lire la fréquence et la sortie HC-020K.
int readFrequency(int _DI_FrequencyCounter_Pin, float _ReadingSpeed) { pinMode(_DI_FrequencyCounter_Pin, INPUT); octet _DigitalRead, _DigitalRead_Previous =0 ; _Time long non signé =0, _Time_Init ; float _Frequency =0; if ( (_ReadingSpeed<=0) || (_ReadingSpeed>10) ) return (-1); else { _Time_Init =micros(); do { _DigitalRead =digitalRead(_DI_FrequencyCounter_Pin); if ( (_DigitalRead_Previous==1) &&(_DigitalRead==0) ) _Frequency++; _DigitalRead_Previous =_DigitalRead; _Temps =micros(); } while ( _Time <(_Time_Init + (1000000/_ReadingSpeed)) ); } return (_ReadingSpeed * _Frequency); }
Notez que la roue du HC-020K a 20 emplacements, la fréquence de lecture doit simplement être divisée par 20 afin d'obtenir un tour par seconde comme fréquence. Ensuite, le résultat doit être multiplié par 60 pour obtenir RPM.
RPM =60 * (float)readFrequency(_chSpeedRead,4) / _DiscSlots ;
Retouches graphiques
Afin d'afficher les entrées et les résultats, j'ai utilisé un écran LCD TFT Makeblock Me et écrit la fonction CommandToTFT() pour lui envoyer des commandes. En fait, la raison de cette fonction est simplement de changer le point de connexion série sur une seule ligne du programme, si nécessaire.
Les fonctions Cartesian_Setup(), Cartesian_ClearPlotAreas() et Cartesian_Line() sont écrites pour préparer la zone de traçage graphique, effacer la zone de traçage lorsqu'elle est atteinte à la fin de l'axe horizontal (ici c'est le « temps ») et tracer des graphiques respectivement. Référez-vous au manuel Makeblock Me TFT LCD pour plus de détails si vous vous intéressez aux fonctions graphiques ici, car je ne les expliquerai pas ici car elles sont en fait hors de la portée de ce blog.
Fin
Ici, je copie le programme avec et sans fonctions graphiques séparément, afin que vous puissiez revoir la mise en œuvre du contrôle de vitesse et de direction de manière autonome ou avec des graphiques. En outre, sur les codes, vous pouvez trouver d'autres explications sur les fonctions programmées.
Enfin, normalement, vous ne pouvez pas réduire la vitesse d'un moteur à courant continu en dessous de 10 à 20 % de sa vitesse nominale, même s'il n'y a pas de charge. Cependant, il est possible de diminuer jusqu'à près de 5 % en utilisant le contrôle PID pour un moteur à courant continu non chargé une fois qu'il a démarré.
Modifier (25 février 2018) : Si vous souhaitez utiliser des transistors NPN au lieu de PNP, vous devez également considérer le flux de courant inverse sur les deux types. Lorsqu'il est déclenché (allumé), le courant passe de l'émetteur au collecteur sur les transistors PNP, mais c'est l'inverse pour les types NPN (du collecteur à l'émetteur). Par conséquent, les transistors PNP sont polarisés comme E(+) C(-) et pour NPN, il devrait être C(+) E(-).

Code
- Code avec retouches graphiques
- Code sans retouches graphiques
Code avec retouches graphiquesArduino
Code without Graphical Touch-UpsArduino
/* ############################################### I/O Assignments############################################### */int _chSpeedSet =A0, // Speed setpoint _chKp =A1, // Proportional coefficient reading for PID controller _chKi =A2, // Integral coefficient reading for PID controller _chKd =A3, // Derivative coefficient reading for PID controller _chMotorCmdCCW =3, // PWM output to motor for counter-clockwise turn _chMotorCmdCW =2, // PWM output to motor for clockwise turn _chSpeedRead =24, // Speed reading _chDirection =25; // Direction selector reading/* ############################################### Other Constants ############################################### */#define _minRPM 0 // Minimum RPM to initiate direction changing#define _maxRPM 6000 // Maximum RPM limit#define _DiscSlots 20 // Qty of slots on Index Disc/* ############################################### Global Variables############################################### */boolean Direction, prevDirection;float RPM=0.0, RPMset=0.0, OutputRPM=0.0, Kp=0.0, Ki=0.0, Kd=0.0, Kpmax=2.0, Kimax=1.0, Kdmax=1.0, E=0.0, Eprev=0.0, dT=1.0;/* ############################################### readFrequency(_DI_FrequencyCounter_Pin, _ReadingSpeed) Frequency Reading Function Input Parameters:(int) _DI_FrequencyCounter_Pin :Digital pin to be read (float) _ReadingSpeed____________:Custom reading speed between 0...10 (Note.1) Note.1:_ReadingSpeed is a value to specify how long shall the changes be counted. It cannot be 0(zero), negative values or a value greater than 10. When _ReadingSpeed changed, 1 second shall be divided by this value to calculate required counting duration. For example; - _ReadingSpeed =0.1 -> input shall be counted during 10 seconds (=1/0.1) - _ReadingSpeed =0.5 -> input shall be counted during 2 seconds (=1/0.5) - _ReadingSpeed =2.0 -> input shall be counted during 0.5 seconds (=1/2) - _ReadingSpeed =4.0 -> input shall be counted during 0.25 seconds (=1/4) Importantly note that, increasing of _ReadingSpeed is a disadvantage especially on lower frequencies (generally below 100 Hz) since counting error increases up to 20%~40% by decreasing frequency.############################################### */int readFrequency(int _DI_FrequencyCounter_Pin, float _ReadingSpeed){ pinMode(_DI_FrequencyCounter_Pin,INPUT); byte _DigitalRead, _DigitalRead_Previous =0; unsigned long _Time =0, _Time_Init; float _Frequency =0; if ( (_ReadingSpeed<=0) || (_ReadingSpeed>10) ) return (-1); else { _Time_Init =micros(); do { _DigitalRead =digitalRead(_DI_FrequencyCounter_Pin); if ( (_DigitalRead_Previous==1) &&(_DigitalRead==0) ) _Frequency++; _DigitalRead_Previous =_DigitalRead; _Time =micros(); } while ( _Time <(_Time_Init + (1000000/_ReadingSpeed)) ); } return (_ReadingSpeed * _Frequency);}/* ########### End of readFrequency() ########### *//* ############################################## *//* ############################################### controllerPID(RangeMin, RangeMax, _E, _Eprev, _dT, _Kp, _Ki, _Kd) PID Controller Function Input Parameters:(float) RangeMin:Minimum limit for output (float) RangeMax:Maximum limit for output (float) _E_____:Current error signal (float) _Eprev :Previous error signal (float) _dT____:Time difference as seconds (float) _Kp____:Proportional coefficient (float) _Ki____:Integral coefficient (float) _Kp____:Derivative coefficient Adjustment procedure:1. Set Kp=0, Ki=0, Kd=0. 2. Start to increase Kp until the system oscillates at fixed period (Pc) and note critical gain Kc =Kp. 3. Adjust final coefficients as follows. for P-control only :Kp =0.50*Kc for PI-control only :Kp =0.45*Kc, Ki =1.2/Pc for PID-control :Kp =0.60*Kc, Ki =2.0/Pc, Kd=Pc/8 4. Fine tuning could be done by slightly changing each coefficient.############################################### */ float controllerPID(float _E, float _Eprev, float _dT, float _Kp, float _Ki, float _Kd){ float P, I, D; /* Base Formula:U =_Kp * ( _E + 0.5*(1/_Ki)*(_E+_Eprev)*_dT + _Kd*(_E-_Eprev)/_dT ); */ P =_Kp * _E; /* Proportional Component */ I =_Kp * 0.5 * _Ki * (_E+_Eprev) * _dT; /* Integral Component */ D =_Kp * _Kd * (_E-_Eprev) / _dT; /* Derivative Component */ return (P+I+D);}/* ########### End of controllerPID() ########### *//* ############################################## *//* ############################################### Setup############################################### */void setup(){ analogReadResolution(12); pinMode(_chDirection,INPUT); // Direction selector reading pinMode(_chMotorCmdCCW,OUTPUT); // PWM output to motor for counter-clockwise turn pinMode(_chMotorCmdCW,OUTPUT); // PWM output to motor for clockwise turn // Initial killing the PWM outputs to motor analogWrite(_chMotorCmdCCW,0); analogWrite(_chMotorCmdCW,0); // Initial reading for direction selection Direction=digitalRead(_chDirection); // HIGH=CCW, LOW=CW prevDirection=Direction;}/* ############################################### Loop############################################### */void loop(){ // Initialization Time:Necessary for PID controller. int InitTime =micros(); // Reading Inputs /* Controller Coefficients */ Kp =Kpmax * (float)analogRead(_chKp) / 4095; Ki =Kimax * (float)analogRead(_chKi) / 4095; Kd =Kdmax * (float)analogRead(_chKd) / 4095; /* Direction Selector */ Direction =digitalRead(_chDirection); /* HIGH=CCW, LOW=CW */ /* Actual RPM and RPM Setpoint Note that maximum selectable RPM is 5000. */ RPM =60 * (float)readFrequency(_chSpeedRead,4) / _DiscSlots; RPMset =5000 * (float)analogRead(_chSpeedSet) / 4095; // Calculations and Actions /* Error Signal, PID Controller Output and Final Output (PWM) to Motor */ E =RPMset - RPM; float cPID =controllerPID(E, Eprev, dT, Kp, Ki, Kd); if ( RPMset ==0 ) OutputRPM =0; else OutputRPM =OutputRPM + cPID; if ( OutputRPM <_minRPM ) OutputRPM =_minRPM; if ( OutputRPM> _maxRPM ) OutputRPM =_maxRPM; /* Changing Direction when inverted */ if ( Direction !=prevDirection ) { /* Killing both of the PWM outputs to motor */ analogWrite(_chMotorCmdCCW,0); analogWrite(_chMotorCmdCW,0); /* Wait until motor speed decreases */ do { RPM =60 * (float)readFrequency(_chSpeedRead,4) / _DiscSlots; } while ( RPM> _minRPM ); } // Writing Outputs if (Direction==HIGH) analogWrite(_chMotorCmdCCW,(int)(255*OutputRPM/_maxRPM)); else analogWrite(_chMotorCmdCW, (int)(255*OutputRPM/_maxRPM)); // Storing Values generated on previous cycle Eprev =E; prevDirection =Direction; // Calculating control application cycle time and passed Seconds dT =float ( micros() - InitTime ) / 1000000.0;}
Schémas
It's a prototype to explain DC motor speed control by using PID controller, and what should be considered for reversing.
Processus de fabrication
- Alliages de cuivre tungstène pour moteurs
- Contrôler un effet avec de vrais capteurs
- Arduino Nano :contrôler 2 moteurs pas à pas avec joystick
- Contrôler une matrice LED avec Arduino Uno
- Surveillance SMART de la température pour les écoles
- Bibliothèque de ports E/S 8 bits pour Arduino
- Matrice de clavier de prototypage à 64 touches pour Arduino
- Une entrée analogique isolée pour Arduino
- Robot pour une navigation intérieure super cool