Industriële fabricage
Industrieel internet der dingen | Industriële materialen | Onderhoud en reparatie van apparatuur | Industriële programmering |
home  MfgRobots >> Industriële fabricage >  >> Manufacturing Technology >> Productieproces

Tricks voor het besturen van gelijkstroommotoren

Componenten en benodigdheden

Arduino Due
Eigenlijk kun je elk Arduino-bord gebruiken.
× 1
Makeblock Me TFT LCD
Dit is een optioneel onderdeel. U kunt een ander type display gebruiken, of liever geen gebruiken.
× 1
Weerstand 4.75k ohm
Weerstanden kunnen variëren, afhankelijk van uw implementatie.
× 4
Draaipotentiometer (generiek)
3 daarvan zijn optioneel (alleen om de controllercoëfficiënten aan te passen).
× 4
Transistor PNP voor algemeen gebruik
De te gebruiken transistoren kunnen variëren, afhankelijk van uw implementatie.
× 4
DC-motor (generiek)
× 1
Schuifschakelaar
Dit wordt gebruikt voor het selecteren van de richting.
× 1
Foto-elektrische snelheidssensor HC-020K
× 1
Broodplank (algemeen)
× 1
Jumperdraden (algemeen)
× 1

Apps en online services

Arduino IDE

Over dit project

Besturing van snelheid en richting van gelijkstroommotoren door PID-controller en PWM-uitgangen

Inleiding

Bijna bij alle beschikbare projecten willen initiatiefnemers de motorsnelheden en -richting samen regelen, maar ze geven er de voorkeur aan om PWM rechtstreeks naar DC-motoren te sturen, zelfs via een motorbesturingscircuit. Maar zo'n methode faalt altijd als je de snelheid precies moet afstemmen op je wensen vanwege de tweelingbroers die "wrijving" en "traagheid" worden genoemd.

(Gelieve Twins nooit de schuld te geven. Wat en wanneer je ook maar iets met of zonder iets wilt ondernemen, Twins komt onmiddellijk in actie om je te helpen alles onder controle te houden. Terwijl Inertia dingen laat 'denken' voordat ze actie ondernemen, Wrijving beperkt hun acceleratie en snelheid. En "kracht" is "niets" als het niet onder volledige controle is.)

Dus als u de snelheid van een motor rechtstreeks probeert te regelen door uw invoer als PWM-signaal naar uitvoer te sturen, zal de werkelijke snelheid nooit uw instelpunt halen en zal er een aanzienlijk verschil (fout) zijn, zoals te zien is op de afbeelding hierboven. Hier hebben we een andere manier nodig, en dit wordt "PID-regeling" genoemd.

PID-controller

Wat is PID-regeling? Stelt u zich eens voor hoe u met uw auto moet rijden:om te beginnen met rijden vanuit stilstand, moet u het gaspedaal meer intrappen dan tijdens een normale cruise. Tijdens het rijden met (bijna) constante snelheid hoef je het gaspedaal niet te veel in te trappen, maar herstel je het snelheidsverlies wanneer nodig. Bovendien laat je hem een ​​beetje los als de acceleratie hoger is dan je nodig hebt. Dit is ook de manier van "efficiënt rijden".

De PID-regelaar doet dus precies hetzelfde:de regelaar leest het verschil "Foutsignaal (e)" tussen instelpunt en werkelijke uitvoer. Het heeft 3 verschillende componenten genaamd "Proportioneel", "Integraal" en "Afgeleid"; dus de naam van de controller verschijnt na de eerste letter van elke. Proportionele component definieert eenvoudig de helling (versnelling) van de controlleruitgang met betrekking tot het werkelijke foutsignaal. Integraal onderdeel somt foutsignalen in de tijd op om de uiteindelijke fout te minimaliseren. En de afgeleide component let op de versnelling van het foutsignaal en brengt een "aanpassing" aan. Ik zal hier geen verdere en langere details geven, zoek alsjeblieft op internet voor verder als je interesse hebt.

In mijn Arduino-programma is de PID-controller geschreven als een functie die hieronder wordt weergegeven:

float controllerPID(float _E, float _Eprev, float _dT, float _Kp, float _Ki, float _Kd){ float P, I, D; /* Basisformule:U =_Kp * ( _E + 0,5*(1/_Ki)*(_E+_Eprev)*_dT + _Kd*(_E-_Eprev)/_dT ); */ P =_Kp * _E; /* Proportionele component */ I =_Kp * 0,5 * _Ki * (_E+_Eprev) * _dT; /* Integrale component */ D =_Kp * _Kd * (_E-_Eprev) / _dT; /* Afgeleide component */ rendement (P+I+D);} 

Vervolgens wordt de uiteindelijke uitgangswaarde bepaald door simpelweg de huidige uitgangswaarde en de uitgang van de PID-regelaar op te tellen. Het is de volgende sectie van het hoofdprogramma samen met de berekening van het foutsignaal en de PID-regelaaruitgang:

/* Foutsignaal, PID-regelaaruitgang en einduitgang (PWM) naar motor */E =RPMset - RPM;float cPID =controllerPID(E, Eprev, dT, Kp, Ki, Kd);if ( RPMset ==0 ) Uitgang RPM =0; anders Uitgang RPM =Uitgang RPM + cPID; als (UitvoerRPM <_minRPM) UitvoerRPM =_minRPM;  

DC-motorvoedingscircuit

Het is natuurlijk nooit aan te raden om een ​​gelijkstroommotor rechtstreeks vanaf de uitgang van een Arduino of een soortgelijk bedieningspaneel aan te drijven. DC-motoren hebben een aanzienlijke hoeveelheid stroom nodig in vergelijking met die welke niet kunnen worden geleverd door uitgangen van controllerkaarten. Je moet dus relaisspoelen aansturen. Maar hier komt een ander probleem naar voren:relais hebben mechanische onderdelen en kunnen op middellange of lange termijn defect raken. We hebben hier nog een component nodig, transistors.

Eigenlijk worden gelijkstroommotoren aangedreven door stroom, niet door spanning. Dus door dit principe te gebruiken, besloot ik transistors te gebruiken. Maar je moet de juiste transistor oppikken die bestand is tegen motorstroom. Laat de motor eerst rechtstreeks draaien door hem op de voeding aan te sluiten en meet de stroom bij de maximale bedrijfsomstandigheden, of raadpleeg de specificaties van de fabrikant.

Nadat ik dit had gedaan, besloot ik om vier BC307A PNP bipolaire junctietransistoren op een "brug" te gebruiken om de stroomrichting door motorspoelen te bepalen (eigenlijk zou een NPN BC337-set beter werken vanwege het vermogen om aanzienlijk hogere collectorstromen te weerstaan, maar dat deed ik niet' heb ze op dat moment niet).

Aangezien motorstromen door het emitter-collectorpad van de transistor moeten worden gevoerd, is het noodzakelijk om transistors te gebruiken met ongeveer dezelfde DC-stroomversterkingscoëfficiënten (hfe). Om het te controleren, kunt u het volgende circuit gebruiken en transistors verzamelen die u ongeveer dezelfde stroomwaarde geven op de ampèremeter. Om die voorlopige circuits te ontwerpen, moet u het volgende overwegen:

  • Zoek "Base-emitter op spanning ” (VBEon ) van transistor. Dit is de minimale spanning die op de basis moet worden toegepast om de transistor in te schakelen.
  • Vind typische "DC-stroomversterking ” (hfe ) van de transistor rond de collectorstroom in de buurt van motorstroom. Meestal is dit de verhouding tussen Collectorstroom (IC ) en Basisstroom (IB ), hfe =IC / IB .
  • Vind "Maximale continue collectorstroom ” van transistors (ICmax ). De gelijkstroom van de motor mag deze waarde nooit overschrijden in termen van absolute waarden. Ik kan BC307 gebruiken omdat de motor die ik gebruik 70 mA nodig heeft, terwijl de transistor ICmax(abs) =100 mA heeft.

Nu kunt u de weerstandswaarde bepalen die op Base moet worden aangesloten:in eerste instantie moet u rekening houden met de beperkingen van de uitvoer van uw controllerkaart en proberen de Base-stroom zo minimaal mogelijk te houden (het wordt daarom aanbevolen om de DC-stroomversterking van de transistors als maximum te selecteren Neem de nominale spanning van de output op de controllerkaart als "Trigger Voltage ” (VT ), en zoek de vereiste basisstroom (IBreq ) door Motorstroom te delen (IM ) naar DC-stroomversterking (hfe ) van transistor:IBreq =IM / hfe .

Bepaal vervolgens de spanning die over de weerstand moet worden verlaagd (VR ), door Base-Emitter On-Voltage . af te trekken (VBEon ) van Triggerspanning :VR =VT - VBEon .

Verdeel tenslotte de te laten vallen spanning over Weerstand (VR ) tot Vereiste basisstroom (IBreq ) om Weerstandswaarde . te vinden (R ):R =VR / IBreq .

[ Gecombineerde formulering:R =( VT - VBEon ) * hfe / IM ]

In mijn geval:

  • Motorstroom:IM =70 mA
  • BC307A-parameters:ICmax =100 mA, hfe =140 (ik heb ongeveer gemeten), VBEon =0,62 V
  • Triggerspanning:VT =3,3 V (PWM-uitgang van Arduino Due)
  • R =5360 ohm (Dus heb ik besloten om 4900 ohm te gebruiken die is gemaakt door een 2K2 en 2K7, om een ​​volledige dekking van het toerentalbereik te garanderen en het circuit zuigt slechts ~0,6 mA uit de PWM-uitgang - een geschikt ontwerp.)

Richting omkeren en belangrijke opmerkingen

Om de richting van een gelijkstroommotor om te keren, volstaat het om de stroom om te keren. Om dat te doen, kunnen we eenvoudig een brugschakeling maken met vier transistorsets. Op het schema; PWM Output#2 activeert T1A en T1B terwijl PWM Output#3 T2A en T2B activeert, dus de stroom die door de motor gaat wordt veranderd.

Maar hier moeten we rekening houden met een ander probleem:wanneer ze net zijn gestart, zuigen elektromotoren tijdelijke opstartstroom aan die aanzienlijk hoger is dan de nominale stroom die u tijdens normaal/continu bedrijf afleest (fabrikanten geven alleen nominale stromen aan). De opstartstroom kan ongeveer 130% van de nominale stroom zijn voor motoren met een klein vermogen en neemt toe afhankelijk van het motorvermogen. Dus als u de motor rechtstreeks vanuit een spanningsbron voedt en tijdens bedrijf onmiddellijk de polariteit omkeert, zuigt de motor extreme stroomniveaus aan omdat deze niet volledig is gestopt. Ten slotte kan dit resulteren in het uitblazen van de stroombron of het verbranden van motorspoelen. Dit is misschien niet zo belangrijk en voelbaar voor zeer kleine krachtmotoren, maar wordt belangrijk als de vermogensniveaus waaraan u werkt toenemen. Maar als je de motor van stroom voorziet via een transistor of een set transistors (zoals Darlington Couple), heb je niet zo'n probleem, aangezien transistors de stroom al beperken.

Hoe dan ook, ik heb een kleine routine op het programma overwogen:wanneer de richtingselectie tijdens het draaien wordt gewijzigd, stuurt het programma beide commando-uitgangen eerst naar nul en wacht het op de motor tot hij volledig stopt. Dan beëindigt het zijn taak en geeft het alle controle terug aan de hoofdroutine.

if (Richting!=prevDirection) { /* Beide PWM-uitgangen naar motor worden uitgeschakeld */ analogWrite(_chMotorCmdCCW,0); analogWrite(_chMotorCmdCW,0); /* Wacht tot de motorsnelheid afneemt */ do {RPM =60*(float)readFrequency(_chSpeedRead,4)/_DiscSlots; } while (RPM> _minRPM); }  

Snel lezen

Op mijn aanvraag heb ik een goedkope HC-020K snelheidssensor gebruikt. Het stuurt pulsen op het niveau van zijn voedingsspanning, en de datasheet zegt dat de voedingsspanning 5V is. Mijn bord is echter Arduino Due en kan het niet accepteren. Dus ik voedde het rechtstreeks van Due's 3.3V-uitgang, en ja, het werkte. En de volgende functie is geschreven om de frequentie en HC-020K-uitgang te lezen.

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 _Frequentie =0; if ( (_ReadingSpeed<=0) || (_ReadingSpeed>10) ) return (-1); anders { _Time_Init =micros(); doen { _DigitalRead =digitalRead (_DI_FrequencyCounter_Pin); if ( (_DigitalRead_Previous==1) &&(_DigitalRead==0) ) _Frequency++; _DigitalRead_Previous =_DigitalRead; _Tijd =micros(); } while ( _Time <(_Time_Init + (1000000/_ReadingSpeed)) ); } terugkeer (_ReadingSpeed ​​* _Frequency); }  

Merk op dat het wiel van de HC-020K 20 slots heeft, lees de frequentie gewoon door 20 om de omwenteling per seconde als frequentie te krijgen. Vervolgens moet het resultaat worden vermenigvuldigd met 60 om RPM te krijgen.

RPM =60 * (float)readFrequency(_chSpeedRead,4) / _DiscSlots; 

Grafische retouches

Om de invoer en resultaten weer te geven, gebruikte ik een Makeblock Me TFT LCD en schreef ik de CommandToTFT()-functie om er commando's naar te sturen. De reden van deze functie is eigenlijk alleen om het seriële verbindingspunt te wijzigen op slechts een enkele rij in het programma, wanneer dat nodig is.

De functies Cartesian_Setup(), Cartesian_ClearPlotAreas() en Cartesian_Line() zijn geschreven om respectievelijk het grafische plotgebied voor te bereiden, het plotgebied te wissen wanneer het einde van de horizontale as wordt bereikt (hier is het "tijd") en grafische afbeeldingen te plotten. Raadpleeg de Makeblock Me TFT LCD-handleiding voor meer details als u geïnteresseerd bent in grafische functies hier, want ik zal ze hier niet uitleggen omdat ze eigenlijk buiten het bestek van deze blog vallen.

Einde

Hier kopieer ik het programma met en zonder grafische functies afzonderlijk, zodat je Speed ​​and Direction Control Implementation standalone of met graphics kunt bekijken. Daarnaast vindt u op de Codes verdere uitleg voor geprogrammeerde functies.

Ten slotte kunt u normaal gesproken de snelheid van een DC-motor niet verlagen tot onder 10-20% van de nominale snelheid, zelfs als er geen belasting is. Het is echter mogelijk om tot bijna 5% te verminderen door PID-regeling te gebruiken voor een onbelaste DC-motor zodra deze is gestart.

Bewerken (25.Feb.2018): Als u NPN-transistoren in plaats van PNP wilt gebruiken, moet u bij beide typen ook rekening houden met omgekeerde stroom. Wanneer geactiveerd (ingeschakeld) vloeit er stroom van emitter naar collector op PNP-transistoren, maar het is omgekeerd voor NPN-typen (van collector naar emitter). Daarom zijn PNP-transistoren gepolariseerd als E(+) C(-) en voor NPN zou het C(+) E(-) moeten zijn.

Code

  • Code met grafische aanpassingen
  • Code zonder grafische aanpassingen
Code met grafische aanpassingenArduino
/* ############################################# ## Kleurconstanten voor Makeblock Me TFT LCD######################################### ###### */#define _BLACK 0#define _RED 1#define _GREEN 2#define _BLUE 3#define _YELLOW 4#define _CYAAN 5#define _PINK 6#define _WHITE 7/* ######## ####################################### I/O-toewijzingen####### ######################################## */int _chSpeedSet =A0, // Snelheid setpoint _chKp =A1, // Proportionele coëfficiëntaflezing voor PID-regelaar _chKi =A2, // Integrale coëfficiëntaflezing voor PID-regelaar _chKd =A3, // Afgeleide coëfficiëntaflezing voor PID-regelaar _chMotorCmdCCW =3, // PWM-uitgang naar motor voor teller- rechtsom draaien _chMotorCmdCW =2, // PWM-uitgang naar motor voor rechtsom draaien _chSpeedRead =24, // Snelheidsaflezing _chDirection =25; // Lezing richtingkiezer/* ########################################### #### Andere constanten ############################################ ### */#define _minRPM 0 // Minimum RPM om richtingverandering te starten#define _maxRPM 6000 // Maximum RPM limit#define _Tmax 90 // Maximum tijdslimiet voor grafieken#define _DiscSlots 20 // Aantal slots op Index Disc/ * ############################################### Globale variabelen ############################################### */Snaar Cartesian_SetupDetails;boolean Direction, prevDirection;// Alarm Settingsfloat RALL=500,0, RAL=1000.0, RAH=4000.0, RAHH=4500.0;float Seconds=0.0, prevSeconds=0.0, prevRPM=0.0, prevRPMset=0.0, 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;/* ###### ######################################### CommandToTFT(TFTCmd) Commandofunctie voor Makeblock Me TFT LCD-ingangsparameters:(String) TFTCmd:Commandostring###################################### ######### */void CommandToTFT(String TFTCmd){ /* Seriële verbinding gebruikt voor weergave */ Serial1.println(TFTCmd); delay(5);}/* ########### Einde van CommandToTFT() ########### *//* ########### ################################## *//* ############ ################################### Cartesian_Setup(Xmin, Xmax, Ymin, Ymax, Window_X1, Window_Y1, Window_X2 , Window_Y2, MinDashQty, ColorF, ColorX, ColorY) Cartesiaanse XY-as tekenfunctie voor Makeblock Me TFT LCD-invoerparameters:(float) Xmin, Xmax, Ymin, Ymax:Asbereikwaarden (int) Window_X1, Window_Y1___:linkerbovenhoek van grafiekvenster (int) Window_X2, Window_Y2___:rechterbenedenhoek van grafiekvenster (int) MinDashQty_____________:aantal streepjes op kortste as (int) ColorB, ColorX, ColorY :Tekenkleuren voor frame, X-as en Y-as Gebruik externe functie CommandToTFT().############################################ ### */String Cartesian_Setup( float Xmin, float Xmax, float Ymin, float Ymax, int Window_X1, int Window_Y1, int Window_X2, int Window_Y2, int MinDashQty, int ColorF, int ColorX, int ColorY ){ /* Schermbeperkingen * / const int Weergave ResolutieX =319, DisplayResolutionY =239; /* Beperk titelreeksen */ Tekenreeks XminTxt; if (abs(Xmin)>=1000000000) XminTxt ="X=" + String (Xmin/1000000000) + "G"; else if (abs(Xmin)>=1000000) XminTxt ="X=" + String (Xmin/1000000) + "M"; else if (abs(Xmin)>=1000) XminTxt ="X=" + String (Xmin/1000) + "K"; anders XminTxt ="X=" + String (Xmin); Tekenreeks XmaxTxt; if (abs(Xmax)>=1000000000) XmaxTxt ="X=" + String (Xmax/1000000000) + "G"; else if (abs(Xmax)>=1000000) XmaxTxt ="X=" + String (Xmax/1000000) + "M"; anders if (abs(Xmax)>=1000) XmaxTxt ="X=" + String (Xmax/1000) + "K"; anders XmaxTxt ="X=" + String (Xmax); Tekenreeks YminTxt; if (abs(Ymin)>=1000000000) YminTxt ="Y=" + String (Ymin/1000000000) + "G"; else if (abs(Ymin)>=1000000) YminTxt ="Y=" + String (Ymin/1000000) + "M"; else if (abs(Ymin)>=1000) YminTxt ="Y=" + String (Ymin/1000) + "K"; anders YminTxt ="Y=" + String (Ymin); Tekenreeks YmaxTxt; if (abs(Ymax)>=1000000000) YmaxTxt ="Y=" + String (Ymax/1000000000) + "G"; else if (abs(Ymax)>=1000000) YmaxTxt ="Y=" + String (Ymax/1000000) + "M"; anders if (abs(Ymax)>=1000) YmaxTxt ="Y=" + String (Ymax/1000) + "K"; anders YmaxTxt ="Y=" + String (Ymax); /* Limieten */ int XminPx =Window_X1+1; int XmaxPx =Venster_X2-1; int YmaxPx =Venster_Y1+1; int YminPx =Venster_Y2-1; /* Oorsprong */ int OorsprongX =XminPx + (int)( (XmaxPx - XminPx) * abs(Xmin) / (abs(Xmax)+abs(Xmin)) ); int OorsprongY =YmaxPx + (int)( (YminPx - YmaxPx) * abs(Ymax) / (abs(Ymax)+abs(Ymin)) ); /* Frame */ CommandToTFT ("BOX(" + String(Window_X1) + "," + String(Window_Y1)+ "," + String(Window_X2) + "," + String(Window_Y2)+ "," + String( KleurF) + ");" ); /* X-as */ CommandToTFT ("PL(" + String(Window_X1+1) + "," + String(OriginY) + "," + String(Window_X2-1) + "," + String(OriginY) + " ," + String (ColorX) + ");" ); /* Y-as */ CommandToTFT ("PL(" + String(OriginX) + "," + String(Window_Y1+1) + "," + String(OriginX) + "," + String(Window_Y2-1) + " ," + String (KleurY) + ");" ); /* Streepjes:het minimum aantal streepjes wordt gegeven door "MinDashQty" en wordt gestreept op de kortste as-zijde met betrekking tot de oorsprong. Op de andere secties moeten de te markeren streepjes worden bepaald door te kijken naar de verhouding tot de kortste aszijde. */ /* Dashing */ int XlengthLeft =abs(XminPx-OriginX); int XlengthRight =abs(XmaxPx-OriginX); int YlengthLower =abs(YminPx-OriginY); int YlengthUpper =abs(YmaxPx-OriginY); int XlengthLeft_Mod, XlengthRight_Mod, YlengthLower_Mod, YlengthUpper_Mod; if (XlengthLeft<=1) XlengthLeft_Mod=32767; else XlengthLeft_Mod=XlengthLeft; if (XlengthRight<=1) XlengthRight_Mod=32767; else XlengthRight_Mod=XlengthRight; if (YlengthLower<=1) YlengthLower_Mod=32767; else YlengthLower_Mod=YlengthLower; if (YlengthUpper<=1) YlengthUpper_Mod=32767; else YlengthUpper_Mod=YlengthUpper; int MinAxisLength =min (min (XlengthLeft_Mod,XlengthRight_Mod), min (YlengthLower_Mod,YlengthUpper_Mod)); int XdashesLeft =MinDashQty * XlengthLeft / MinAxisLength; int XdashesRight =MinDashQty * XlengthRight / MinAxisLength; int YdashesLower =MinDashQty * YlengthLower / MinAxisLength; int YdashesUpper =MinDashQty * YlengthUpper / MinAxisLength; int DashingInterval=2; // Min.interval btw.dashes /* X-Dash L */ DashingInterval =(int) (XlengthLeft / XdashesLeft); if (!(DashingInterval<2)) voor (int i=OriginX; i>=XminPx; i-=DashingInterval) CommandToTFT ("PL(" + String(i) + "," + String(OriginY-2) + " ," + String(i) + "," + String(OriginY+2) + "," + String(ColorX) + ");" ); /* X-Dash R */ DashingInterval =(int) (XlengthRight / XdashesRight); if (!(DashingInterval<2)) voor (int i=OriginX; i<=XmaxPx; i+=DashingInterval) CommandToTFT ("PL(" + String(i) + "," + String(OriginY-2) + ", " + String(i) + "," + String(OriginY+2) + "," + String(ColorX) + ");" ); /* Y-Dash-L */ DashingInterval =(int) (YlengthLower / YdashesLower); if (!(DashingInterval<2)) for (int i=OriginY; i<=YminPx; i+=DashingInterval) CommandToTFT ("PL(" + String(OriginX-2) + "," + String(i) + ", " + String(OriginX+2) + "," + String(i) + "," + String(ColorY) + ");" ); /* Y-Dash-U */ DashingInterval =(int) (YlengthUpper / YdashesUpper); if (!(DashingInterval<2)) for (int i=OriginY; i>=YmaxPx; i-=DashingInterval) CommandToTFT ("PL(" + String(OriginX-2) + "," + String(i) + " ," + String(OriginX+2) + "," + String(i) + "," + String(KleurY) + ");" ); /* Coördinaten berekenen om aseindpuntwaarden weer te geven */ int XminTxtX =Window_X1 - (int)(XminTxt.length()*6) - 1, XminTxtY =OriginY, XmaxTxtX =Window_X2 + 1, XmaxTxtY =OriginY, YminTxtX =OriginX, YminTxtY =Venster_Y2 + 1, YmaxTxtX =OorsprongX, YmaxTxtY =Venster_Y1 - 12 - 1; /* Bediening:als een coördinaat -1 is, zal deze buiten de weergavelimieten vallen en de respectieve waarde wordt niet weergegeven */ als (XminTxtX<0) XminTxtX =-1; als ( (XminTxtY-12) <0 ) XminTxtY =-1; if ( (XmaxTxtX+6*XmaxTxt.length())> DisplayResolutionX ) XmaxTxtX =-1; if ( (XmaxTxtY+12)> DisplayResolutionY ) XmaxTxtY =-1; if ( (YminTxtX+6*YminTxt.length())> DisplayResolutionX ) YminTxtX =-1; if ( (YminTxtY+12)> DisplayResolutionY ) YminTxtY =-1; if ( (YmaxTxtX+6*YmaxTxt.length())> DisplayResolutionX ) YmaxTxtX =-1; als (YmaxTxtY<0) YmaxTxtY =-1; /* Titels bereiklimiet */ if ( ( XminTxtX !=-1 ) &&( XminTxtY !=-1) ) CommandToTFT( "DS12(" + String(XminTxtX) + "," + String(XminTxtY) + ",'" + String (XminTxt) + "'," + String (ColorX) + ");" ); if ( ( XmaxTxtX !=-1 ) &&( XmaxTxtY !=-1 ) ) CommandToTFT( "DS12(" + String(XmaxTxtX) + "," + String(XmaxTxtY) + ",'" + String(XmaxTxt) + " '," + String (ColorX) + ");" ); if ( ( YminTxtX !=-1 ) &&( YminTxtY !=-1 ) ) CommandToTFT( "DS12(" + String(YminTxtX) + "," + String(YminTxtY) + ",'" + String(YminTxt) + " '," + String (ColorY) + ");" ); if ( ( YmaxTxtX !=-1 ) &&( YmaxTxtY !=-1 ) ) CommandToTFT( "DS12(" + String(YmaxTxtX) + "," + String(YmaxTxtY) + ",'" + String(YmaxTxt) + " '," + String (ColorY) + ");" ); /* Retourwaarde String Cartesian_Setup() retourneert een grafische configuratie voor stringverpakking in de volgende indeling:"" String begint met '<' en eindigt met '>' . Elke waarde wordt gescheiden door ',' */ /* Initialize */ String Cartesian_SetupDetails ="<"; Cartesian_SetupDetails +=( String (Xmin) + "," ); Cartesian_SetupDetails +=( String (Xmax) + "," ); Cartesian_SetupDetails +=( String (Ymin) + "," ); Cartesian_SetupDetails +=( String (Ymax) + "," ); Cartesian_SetupDetails +=( String (Window_X1) + "," ); Cartesian_SetupDetails +=( String (Window_Y1) + "," ); Cartesian_SetupDetails +=( String (Window_X2) + "," ); Cartesian_SetupDetails +=( String (Window_Y2) + "," ); /* Sluiten */ Cartesian_SetupDetails +=">";return Cartesian_SetupDetails;}/* ########### Einde van Cartesian_Setup() ########### *// * ################################################ * //* ############################################### Cartesian_ClearPlotAreas(Descriptor, Color) Plot Area Reset/Clear Functie voor Makeblock Me TFT LCD Input Parameters:(String) Descriptor:Setup Descriptor - geretourneerd door Cartesian_Setup() (int) Color______:Kleur die moet worden gebruikt om plotgebied te vullen Gebruikt externe functie CommandToTFT ().############################################### */void Cartesian_ClearPlotAreas(String Descriptor, int Color){ int X1,Y1,X2,Y2; /* Grenscoördinaten voor plotgebieden */ /* Waarden extraheren uit Descriptor */ /* L[0] L[1] L[2] L[3] W[0] W[1] W[2] W[3 ] */ /* Xmin Xmax Ymin Ymax Venster_X1 Venster_Y1 Venster_X2 Venster_Y2 */ float L[4]; int W[4]; /* Waarden opgeslagen in Descriptor */ int j=0; /* Teller */ String D_Str =""; for (int i=1; i<=(Descriptor.length()-1); i++) if ( Descriptor[i] ==',' ) { if (j<4) L[j]=D_Str.toFloat( ); anders W[j-4]=D_Str.toInt(); D_Str=""; j++; } else D_Str +=Descriptor[i]; /* Oorsprong */ int OorsprongX =(W[0]+1) + (int)( ( (W[2]-1) - (W[0]+1) ) * abs(L[0]) / ( abs(L[1])+abs(L[0]))); int OorsprongY =(W[1]+1) + (int)( ( (W[3]-1) - (W[1]+1) ) * abs(L[3]) / (abs(L[3 ])+abs(L[2])) ); /* Plotgebieden wissen */ //Area.1 :X+ Y+ X1 =OriginX + 2; Y1 =W[1] + 1; X2 =W[2] - 1; Y2 =OorsprongY - 2; CommandToTFT ("BOXF(" + String(X1) + "," + String(Y1) + "," + String(X2) + "," + String(Y2) + "," + String(Kleur) + ");" ); //Gebied.2:X- Y+ X1 =W[0] + 1; Y1 =W[1] + 1; X2 =OorsprongX-2; Y2 =OorsprongY - 2; CommandToTFT ("BOXF(" + String(X1) + "," + String(Y1) + "," + String(X2) + "," + String(Y2) + "," + String(Kleur) + ");" ); //Gebied.3:X-Y-X1 =W[0] + 1; Y1 =OorsprongY + 2; X2 =OorsprongX-2; Y2 =W[3] - 1; CommandToTFT ("BOXF(" + String(X1) + "," + String(Y1) + "," + String(X2) + "," + String(Y2) + "," + String(Kleur) + ");" ); //Gebied.4:X+ Y- X1 =OorsprongX + 2; Y1 =OorsprongY + 2; X2 =W[2] - 1; Y2 =W[3] - 1; CommandToTFT ("BOXF(" + String(X1) + "," + String(Y1) + "," + String(X2) + "," + String(Y2) + "," + String(Kleur) + ");" );} /* ########### Einde van Cartesian_ClearPlotAreas() ########### *//* ############ ############################################# *//* # ############################################## Cartesian_Line(Xp, Yp, X, Y, descriptor, kleur) Cartesiaanse lijnfunctie voor Makeblock Me TFT LCD-invoerparameters:(int) Xp, Yp_____:vorige plotcoördinaten - y-waarde vs x (int) X, Y_______:huidige plotcoördinaten - y-waarde vs x (String) Descriptor:Setup Descriptor - geretourneerd door Cartesian_Setup() (int) Color______:Markeringskleur die moet worden gebruikt op (x,y) Gebruikt externe functie CommandToTFT().############# ################################## */void Cartesian_Line(float Xp, float Yp, float X, float Y , String Descriptor, int Color){ /* Waarden extraheren uit Descriptor */ /* L[0] L[1] L[2] L[3] W[0] W[1] W[2] W[3] */ /* Xmin Xmax Ymin Ymax Venster_X1 Venster_Y1 Venster_X2 Venster_Y2 */ zwevend L[4 ]; int W[4]; /* Waarden opgeslagen in Descriptor */ int j=0; /* Teller */ String D_Str =""; for (int i=1; i<=(Descriptor.length()-1); i++) if ( Descriptor[i] ==',' ) { if (j<4) L[j]=D_Str.toFloat( ); anders W[j-4]=D_Str.toInt(); D_Str=""; j++; } else D_Str +=Descriptor[i]; /* Oorsprong */ int OorsprongX =(W[0]+1) + (int)( ( (W[2]-1) - (W[0]+1) ) * abs(L[0]) / ( abs(L[1])+abs(L[0]))); int OorsprongY =(W[1]+1) + (int)( ( (W[3]-1) - (W[1]+1) ) * abs(L[3]) / (abs(L[3 ])+abs(L[2])) ); int XminPx =W[0] + 1; int XmaxPx =W[2] - 1; int YmaxPx =W[1] + 1; int YminPx =W[3] - 1; als (Y>L[3]) Y=L[3]; als (Y
=(OriginX-2) ) &&( DispXp <=(OriginX+2) ) ) || ( ( DispYp>
=(OriginY-2) ) &&( DispYp <=(OriginY+2) ) ) || ( ( DispX>=(OriginX-2) ) &&( DispX <=(OriginX+2) ) ) || ( ( DispY>=(OriginY-2) ) &&( DispY <=(OriginY+2) ) ) )) CommandToTFT( "PL(" + String(DispXp) + "," + String(DispYp) + "," + String(DispX) + "," + String(DispY) + "," + String(Kleur ) + ");" );}/* ########### Einde van Cartesian_Line() ########### *//* ######## ####################################### *//* ####### ######################################## readFrequency(_DI_FrequencyCounter_Pin, _ReadingSpeed) Frequentie Lezen Functie Invoer Parameters:(int) _DI_FrequencyCounter_Pin :Digitale pin die moet worden gelezen (float) _ReadingSpeed_________:Aangepaste leessnelheid tussen 0...10 (Note.1) Note.1:_ReadingSpeed ​​is een waarde om aan te geven hoe lang de wijzigingen moeten worden geteld. Het mag geen 0 (nul), negatieve waarden of een waarde groter dan 10 zijn. Als _ReadingSpeed ​​verandert, wordt 1 seconde gedeeld door deze waarde om de vereiste telduur te berekenen. Bijvoorbeeld; - _ReadingSpeed ​​=0,1 -> invoer wordt geteld gedurende 10 seconden (=1/0,1) - _ReadingSpeed ​​=0,5 -> invoer wordt geteld gedurende 2 seconden (=1/0,5) - _ReadingSpeed ​​=2,0 -> invoer wordt geteld gedurende 0,5 seconden (=1/2) - _ReadingSpeed ​​=4,0 -> invoer wordt geteld gedurende 0,25 seconden (=1/4) Merk op dat het verhogen van _ReadingSpeed ​​een nadeel is, vooral bij lagere frequenties (in het algemeen onder 100 Hz) aangezien de telfout toeneemt tot 20%~40% door de frequentie te verlagen.####################################### ######## */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 _Frequentie =0; if ( (_ReadingSpeed<=0) || (_ReadingSpeed>10) ) return (-1); anders { _Time_Init =micros(); doen { _DigitalRead =digitalRead (_DI_FrequencyCounter_Pin); if ( (_DigitalRead_Previous==1) &&(_DigitalRead==0) ) _Frequency++; _DigitalRead_Previous =_DigitalRead; _Tijd =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(){ Serial1.begin(9600); Serial1.println("CLS(0);");delay(20); 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; // The section below prepares TFT LCD // Cartesian_Setup(Xmin, Xmax, Ymin, Ymax, Window_X1, Window_Y1, Window_X2, Window_Y2, MinDashQty, ColorF, ColorX, ColorY) Cartesian_SetupDetails =Cartesian_Setup(0, _Tmax, _minRPM, _maxRPM, 20, 20, 220, 120, 10, 0, 7, 7); CommandToTFT("DS12(250,10,'Dir:CW '," + String(_WHITE) + ");"); CommandToTFT("DS12(250,25,'____ Set'," + String(_YELLOW) + ");"); CommandToTFT("DS12(250,40,'____ RPM'," + String(_GREEN) + ");"); /* Alarm Values */ CommandToTFT("DS12(250,55,'AHH:" + String(RAHH) + "'," + String(_WHITE) + ");"); CommandToTFT("DS12(250,70,'AH :" + String(RAH) + "'," + String(_WHITE) + ");"); CommandToTFT("DS12(250,85,'AL :" + String(RAL) + "'," + String(_WHITE) + ");"); CommandToTFT("DS12(250,100,'ALL:"+ String(RALL) + "'," + String(_WHITE) + ");"); /* Alarm Window */ CommandToTFT("BOX(240,55,319,115," + String(_WHITE) + ");"); /* Alarm Lamps */ CommandToTFT("BOX(240,55,248,70," + String(_WHITE) + ");"); CommandToTFT("BOX(240,70,248,85," + String(_WHITE) + ");"); CommandToTFT("BOX(240,85,248,100," + String(_WHITE) + ");"); CommandToTFT("BOX(240,100,248,115," + String(_WHITE) + ");");}/* ############################################### Loop############################################### */void loop(){ // Initialization Time:Necessary for PID controller. int InitTime =micros(); // X-Axis Auto-Reset for Graphing if ( Seconds> 90.0 ) { Seconds =0.0; Cartesian_ClearPlotAreas(Cartesian_SetupDetails,0); } // 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 Note that no any graphical indication is performed on this function. */ 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)); // Graphing /* Indicating Direction */ if (Direction==HIGH) CommandToTFT("DS12(280,10,'CCW '," + String(_WHITE) + ");"); else CommandToTFT("DS12(280,10,'CW '," + String(_WHITE) + ");"); /* Plotting Curve */ Cartesian_Line(prevSeconds, prevRPMset, Seconds, RPMset, Cartesian_SetupDetails, _YELLOW); Cartesian_Line(prevSeconds, prevRPM, Seconds, RPM, Cartesian_SetupDetails, _GREEN); /* Indicating values of RPM Setpoint, PID Controller Coefficients, Error Signal, PID Controller Output and Final RPM Output (PWM) */ CommandToTFT( "DS12(20,150,'Set:" + String(RPMset) + " rpm " + "RPM:" + String(RPM) + " rpm '," + String(_WHITE) + ");"); CommandToTFT( "DS12(20,170,'Kp=" + String(Kp) + " " + "Ki=" + String(Ki) + " " + "Kd=" + String(Kd) + " " + "dT=" + String(dT*1000) + " ms '," + String(_WHITE) + ");"); CommandToTFT( "DS12(20,190,'e=" + String(E) + " " + "cPID=" + String(cPID) + " " + "RPMout=" + String(OutputRPM) + " '," + String(_WHITE) + ");"); /* Resetting Alarm Lamps */ CommandToTFT("BOXF(241,56,247,69," + String(_BLACK) + ");"); CommandToTFT("BOXF(241,71,247,84," + String(_BLACK) + ");"); CommandToTFT("BOXF(241,86,247,99," + String(_BLACK) + ");"); CommandToTFT("BOXF(241,101,247,114," + String(_BLACK) + ");"); /* Activating Necessary Alarm Lamps */ if (RPM>=RAHH) CommandToTFT("BOXF(241,56,247,69," + String(_RED) + ");"); if ((RPM>=RAH)&&(RPMRALL)&&(RPM<=RAL)) CommandToTFT("BOXF(241,86,247,99," + String(_RED) + ");"); if (RPM<=RALL) CommandToTFT("BOXF(241,101,247,114," + String(_RED) + ");"); // Storing Values generated on previous cycle Eprev =E; prevRPMset =RPMset; prevRPM =RPM; prevSeconds =Seconds; prevDirection =Direction; // Calculating control application cycle time and passed Seconds dT =float ( micros() - InitTime ) / 1000000.0; Seconds+=dT; }
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;}

Schema's

It's a prototype to explain DC motor speed control by using PID controller, and what should be considered for reversing.

Productieproces

  1. Wolfraam-koperlegeringen voor motoren
  2. Een effect besturen met echte sensoren
  3. Arduino Nano:bedien 2 stappenmotoren met joystick
  4. Een LED-matrix besturen met Arduino Uno
  5. SMART temperatuurbewaking voor scholen
  6. 8-bit IO-poortbibliotheek voor Arduino
  7. 64-Key Prototyping Keyboard Matrix voor Arduino
  8. TFT Shield voor Arduino Nano - Start
  9. Een geïsoleerde analoge ingang voor Arduino
  10. Een bliksemdetector voor Arduino
  11. Robot voor supercoole indoornavigatie