Manufaktur industri
Industri Internet of Things | bahan industri | Pemeliharaan dan Perbaikan Peralatan | Pemrograman industri |
home  MfgRobots >> Manufaktur industri >  >> Manufacturing Technology >> Proses manufaktur

Trik Mengontrol Motor DC

Komponen dan persediaan

Arduino Due
Sebenarnya Anda dapat menggunakan Papan Arduino apa pun.
× 1
Makeblock Me TFT LCD
Ini adalah komponen opsional. Anda dapat menggunakan jenis tampilan lain, atau memilih untuk tidak menggunakannya.
× 1
Resistor 4.75k ohm
Resistor dapat bervariasi tergantung pada penerapan Anda.
× 4
Potensiometer putar (generik)
3 di antaranya opsional (hanya untuk menyesuaikan koefisien pengontrol).
× 4
PNP Transistor Serbaguna
Transistor yang akan digunakan dapat bervariasi tergantung pada implementasi Anda.
× 4
Motor DC (generik)
× 1
Sakelar Geser
Ini digunakan untuk pemilihan arah.
× 1
Sensor Kecepatan Fotolistrik HC-020K
× 1
Breadboard (generik)
× 1
Kabel jumper (generik)
× 1

Aplikasi dan layanan online

Arduino IDE

Tentang proyek ini

Kontrol Kecepatan dan Arah Motor DC dengan Kontroler PID dan Output PWM

Pengantar

Hampir di semua proyek yang tersedia, pencetus ingin mengontrol kecepatan dan arah motor bersama-sama, tetapi mereka lebih suka mengirim PWM langsung ke motor DC, bahkan melalui rangkaian kontrol motor. Tetapi metode seperti itu selalu gagal jika Anda harus mencocokkan kecepatan persis seperti yang Anda inginkan karena saudara kembar disebut sebagai "gesekan" dan "kelembaman".

(Tolong jangan pernah menyalahkan si Kembar. Apa pun dan kapan pun Anda ingin mengambil tindakan dengan atau tanpa sesuatu, Si Kembar segera datang dan mengambil tindakan hanya untuk membantu Anda mengendalikan semuanya. Sementara Inersia membiarkan segala sesuatunya "berpikir" sebelum bertindak, Gesekan membatasi akselerasi dan kecepatannya. Dan "kekuatan" tidak berarti apa-apa jika tidak dikendalikan sepenuhnya.)

Jadi, jika Anda mencoba mengontrol kecepatan motor secara langsung dengan mengirimkan input Anda sebagai sinyal PWM ke output, kecepatan sebenarnya tidak akan pernah bertemu dengan set point Anda dan akan ada perbedaan (kesalahan) yang signifikan, seperti yang terlihat pada gambar di atas. Di sini kita membutuhkan cara lain, dan itu disebut sebagai “kontrol PID”.

Pengontrol PID

Apa itu kontrol PID? Bayangkan bagaimana cara mengemudikan mobil Anda:Untuk mulai bergerak dari titik penuh, Anda harus menekan pedal gas lebih dari saat jelajah normal. Selama bergerak dengan kecepatan (hampir) konstan, Anda tidak perlu menekan pedal gas terlalu banyak, tetapi Anda cukup memulihkan kecepatan yang hilang saat dibutuhkan. Selain itu, Anda melepaskannya sedikit jika akselerasi lebih tinggi dari kebutuhan Anda. Ini juga merupakan cara "mengemudi secara efisien".

Jadi, pengontrol PID melakukan hal yang persis sama:Kontroler membaca perbedaan “Sinyal Kesalahan (e)” antara titik setel dan keluaran aktual. Ini memiliki 3 komponen berbeda yang disebut sebagai "Proporsional", "Integral" dan "Derivatif"; jadi nama pengontrol muncul setelah huruf pertama masing-masing. Komponen proporsional hanya mendefinisikan kemiringan (percepatan) output pengontrol sehubungan dengan sinyal kesalahan aktual. Bagian integral menjumlahkan sinyal kesalahan dalam waktu untuk meminimalkan kesalahan akhir. Dan komponen Derivatif mengawasi percepatan sinyal kesalahan, dan menempatkan "penyesuaian". Saya tidak akan memberikan detail lebih lanjut dan lebih panjang di sini, silakan cari di internet untuk lebih lanjut jika Anda tertarik.

Pada program Arduino saya, pengontrol PID ditulis sebagai fungsi yang ditunjukkan di bawah ini:

float controllerPID(float _E, float _Eprev, float _dT, float _Kp, float _Ki, float _Kd){ float P, I, D; /* Rumus Dasar:U =_Kp * ( _E + 0.5*(1/_Ki)*(_E+_Eprev)*_dT + _Kd*(_E-_Eprev)/_dT ); */ P =_Kp * _E; /* Komponen Proporsional */ I =_Kp * 0.5 * _Ki * (_E+_Eprev) * _dT; /* Komponen Integral */ D =_Kp * _Kd * (_E-_Eprev) / _dT; /* Komponen Derivatif */ return (P+I+D);} 

Kemudian nilai keluaran akhir ditentukan hanya dengan menjumlahkan nilai keluaran arus dan keluaran pengontrol PID. Berikut bagian program utama beserta perhitungan Error Signal dan PID Controller Output:

/* Sinyal Error, Output Kontroler PID dan Output Akhir (PWM) ke Motor */E =RPMset - RPM;float cPID =controllerPID(E, Eprev, dT, Kp, Ki, Kd);if ( RPMset ==0 ) OutputRPM =0;else OutputRPM =OutputRPM + cPID; jika ( OutputRPM <_minRPM ) OutputRPM =_minRPM;  

Sirkuit Suplai Motor DC

Tentu saja tidak pernah disarankan untuk menggerakkan motor DC langsung dari output Arduino atau papan kontrol serupa. Motor DC membutuhkan jumlah arus yang signifikan dibandingkan dengan yang tidak dapat disuplai oleh output kartu pengontrol. Jadi, Anda perlu menggerakkan kumparan relai. Tapi di sini masalah lain muncul:Relay memiliki bagian mekanis dan bisa gagal dalam jangka menengah atau panjang. Kami membutuhkan komponen lain di sini, transistor.

Sebenarnya motor DC digerakkan oleh arus, bukan tegangan. Jadi dengan menggunakan prinsip ini, saya memutuskan untuk menggunakan transistor. Tetapi Anda harus memilih transistor yang benar yang mampu menahan arus motor. Pertama, jalankan motor secara langsung dengan menghubungkan ke catu daya, dan ukur arus pada kondisi pengoperasian maksimum, atau lihat spesifikasi pabrik.

Setelah melakukan ini, saya memutuskan untuk menggunakan empat transistor persimpangan bipolar BC307A PNP pada "jembatan" untuk menentukan arah arus melalui kumparan motor (sebenarnya satu set NPN BC337 akan bekerja lebih baik karena kemampuan menahan arus kolektor yang jauh lebih tinggi, tetapi saya tidak melakukannya) tidak memilikinya pada saat itu).

Karena arus motor harus melewati jalur transistor Emitter-Collector, maka perlu menggunakan transistor dengan koefisien Penguatan Arus DC (hfe) yang kurang lebih sama. Untuk memeriksanya, Anda dapat menggunakan rangkaian berikut, dan mengumpulkan transistor yang memberi Anda pembacaan arus yang kira-kira sama pada ampermeter. Untuk merancang sirkuit pendahuluan tersebut, Anda harus mempertimbangkan hal berikut:

  • Temukan “Base-Emitter On-Voltage ” (VBEon ) dari transistor. Ini adalah tegangan minimum yang harus diterapkan ke Basis untuk mengaktifkan transistor.
  • Temukan “Penguatan Arus DC typical yang khas ” (hfe ) transistor di sekitar Arus Kolektor dekat dengan Arus Motor. Biasanya rasio antara Arus Kolektor (IC ) dan Arus Dasar (IB ), hfe =IC / IB .
  • Temukan “Arus Kolektor Kontinu Maksimum ” transistor (ICmax ). Arus DC motor tidak boleh melebihi nilai ini dalam hal nilai absolut. Saya dapat menggunakan BC307 karena motor yang saya gunakan membutuhkan 70 mA sedangkan transistor memiliki ICmax(abs) =100 mA.

Sekarang Anda dapat menentukan nilai resistor yang akan dihubungkan ke Basis:Pada awalnya, Anda harus mempertimbangkan batasan keluaran kartu pengontrol Anda, dan mencoba untuk menjaga arus Basis seminimal mungkin (jadi disarankan untuk memilih Penguatan Arus DC transistor sebagai maksimum mungkin. Ambil peringkat tegangan keluaran pada papan pengontrol sebagai “Tegangan Pemicu ” (VT ), dan temukan Arus Basis yang Diperlukan (IBreq ) dengan membagi Arus Motor (IM ) ke Penguatan Arus DC (hfe ) transistor:IBreq =IM / hfe .

Kemudian tentukan Tegangan yang akan dijatuhkan pada Resistor (VR ), dengan mengurangkan Base-Emitter On-Voltage (VBEon ) dari Tegangan Pemicu :VR =VT - VBEon .

Akhirnya membagi Tegangan untuk dijatuhkan di Resistor (VR ) ke Arus Basis yang Diperlukan (IBreq ) untuk menemukan Nilai Resistor (R ):R =VR / IBreq .

[ Formulasi gabungan:R =( VT - VBEon ) * hfe / IM ]

Dalam kasus saya:

  • Arus Motor:IM =70 mA
  • BC307A Parameter:ICmax =100 mA, hfe =140 (saya mengukur kira-kira), VBEon =0,62 V
  • Tegangan Pemicu:VT =3,3 V (keluaran PWM Arduino Due)
  • R =5360 ohm (Jadi saya memutuskan untuk menggunakan 4900 ohm yang dibuat oleh 2K2 dan 2K7, untuk memastikan cakupan rentang RPM penuh dan rangkaian hanya menyedot ~0,6 mA dari output PWM - desain yang sesuai.)

Mengembalikan Arah dan Catatan Penting

Untuk mengembalikan arah motor DC, cukup dengan mengembalikan aliran arus. Untuk melakukan itu, kita cukup membuat rangkaian jembatan dengan empat set transistor. Pada skema; PWM Output#2 mengaktifkan T1A dan T1B sedangkan PWM Output#3 mengaktifkan T2A dan T2B, sehingga arus yang melewati motor berubah.

Tapi di sini kita harus mempertimbangkan masalah lain:Ketika baru mulai, motor listrik menyedot arus start-up transien secara signifikan lebih tinggi dari arus nominal yang Anda baca selama operasi normal/terus menerus (produsen hanya memberikan arus nominal). Arus start-up mungkin sekitar 130% dari satu nominal untuk motor daya kecil, dan meningkat tergantung pada daya motor. Jadi, jika Anda memberi makan motor langsung dari sumber tegangan dan segera membalikkan polaritas selama operasi, motor menyedot tingkat arus yang ekstrem karena tidak sepenuhnya berhenti. Akhirnya ini dapat mengakibatkan sumber listrik meledak atau kumparan motor terbakar. Ini mungkin tidak begitu penting dan terasa untuk motor dengan daya yang sangat kecil, tetapi menjadi penting jika tingkat daya yang Anda kerjakan meningkat. Tetapi jika Anda menyalakan motor melalui transistor atau satu set transistor (seperti Darlington Couple), Anda tidak memiliki masalah seperti itu karena transistor sudah membatasi arus.

Bagaimanapun, saya menganggap rutinitas kecil pada program:Ketika pemilihan arah diubah selama berjalan, program menggerakkan kedua output perintah ke nol pada awalnya, dan menunggu motor sampai berhenti total. Kemudian ia menyelesaikan tugasnya, dan mengembalikan semua kendali ke rutinitas utama.

if ( Direction !=prevDirection ) { /* Mematikan kedua output PWM ke motor */ analogWrite(_chMotorCmdCCW,0); analogWrite(_chMotorCmdCW,0); /* Tunggu sampai kecepatan motor berkurang */ lakukan { RPM =60*(float)readFrequency(_chSpeedRead,4)/_DiscSlots; } while ( RPM> _minRPM ); }  

Membaca Cepat

Pada aplikasi saya, saya menggunakan sensor kecepatan HC-020K yang murah. Ini mengirim pulsa pada tingkat tegangan suplainya, dan lembar data mengatakan bahwa tegangan suplai adalah 5V. Namun papan saya adalah Arduino Due dan tidak dapat menerimanya. Jadi saya langsung menyalakannya dari output 3.3V Due, dan ya, itu berhasil. Dan fungsi berikut ditulis untuk membaca frekuensi dan output HC-020K.

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 _Frekuensi =0; if ( (_ReadingSpeed<=0) || (_ReadingSpeed>10) ) return (-1); else { _Waktu_Init =mikro(); lakukan { _DigitalRead =digitalRead(_DI_FrequencyCounter_Pin); if ( (_DigitalRead_Previous==1) &&(_DigitalRead==0) ) _Frequency++; _DigitalRead_Previous =_DigitalRead; _Waktu =mikro(); } while ( _Time <(_Time_Init + (1000000/_ReadingSpeed)) ); } kembali (_ReadingSpeed ​​* _Frequency); }  

Perhatikan bahwa roda HC-020K memiliki 20 slot, cukup baca frekuensi untuk dibagi 20 untuk mendapatkan putaran per detik sebagai frekuensi. Kemudian hasilnya harus dikalikan dengan 60 untuk mendapatkan RPM.

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

Sentuhan Grafis

Untuk menampilkan input dan hasil, saya menggunakan LCD TFT Makeblock Me dan menulis fungsi CommandToTFT() untuk mengirim perintah ke sana. Sebenarnya alasan dari fungsi ini hanyalah untuk mengubah titik koneksi serial hanya pada satu baris dalam program, bila diperlukan.

Fungsi Cartesian_Setup(), Cartesian_ClearPlotAreas() dan Cartesian_Line() ditulis untuk menyiapkan area plot grafik, membersihkan area plot ketika mencapai ujung sumbu horizontal (inilah "waktu") dan plot grafik masing-masing. Lihat manual TFT LCD Makeblock Me untuk detail lebih lanjut jika Anda tertarik dengan fungsi grafis di sini, karena saya tidak akan menjelaskannya di sini karena sebenarnya di luar cakupan blog ini.

Akhir

Di sini saya menyalin program dengan dan tanpa fungsi grafis secara terpisah, sehingga Anda dapat meninjau Implementasi Kontrol Kecepatan dan Arah secara mandiri, atau dengan grafis. Selain itu, pada Kode Anda dapat menemukan penjelasan lebih lanjut untuk fungsi yang diprogram.

Akhirnya, biasanya Anda tidak dapat menurunkan kecepatan motor DC di bawah 10-20% dari kecepatan pengenalnya, bahkan tanpa beban apa pun. Namun, dimungkinkan untuk mengurangi hingga hampir 5% dengan menggunakan kontrol PID untuk motor DC yang tidak dibebani setelah dihidupkan.

Sunting (25 Februari 2018): Jika Anda ingin menggunakan transistor NPN sebagai pengganti PNP, Anda harus mempertimbangkan juga aliran arus balik pada kedua jenis tersebut. Saat dipicu (dihidupkan) arus mengalir dari Emitter ke Collector pada transistor PNP, tetapi sebaliknya untuk tipe NPN (dari Collector ke Emitter). Oleh karena itu transistor PNP terpolarisasi sebagai E(+) C(-) dan untuk NPN seharusnya C(+) E(-).

Kode

  • Kode dengan Sentuhan Grafis
  • Kode tanpa Sentuhan Grafis
Kode dengan Sentuhan GrafisArduino
/* ######################################################### ## Konstanta warna untuk Makeblock Me TFT LCD#################################################### ###### */#define _BLACK 0#define _RED 1#define _GREEN 2#define _BLUE 3#define _YELLOW 4#define _CYAN 5#define _PINK 6#define _WHITE 7/* ######## ##################################### Tugas I/O############# ###################################### */int _chSpeedSet =A0, // Kecepatan setpoint _chKp =A1, // Pembacaan koefisien proporsional untuk pengontrol PID _chKi =A2, // Pembacaan koefisien integral untuk pengontrol PID _chKd =A3, // Pembacaan koefisien turunan untuk pengontrol PID _chMotorCmdCCW =3, // Output PWM ke motor untuk counter- putaran searah jarum jam _chMotorCmdCW =2, // Output PWM ke motor untuk putaran searah jarum jam _chSpeedRead =24, // Kecepatan membaca _chDirection =25; // Pembacaan pemilih arah/* ###################################################### #### Konstanta Lainnya ########################################################## ### */#define _minRPM 0 // RPM minimum untuk memulai perubahan arah#define _maxRPM 6000 // Batas RPM maksimum#define _Tmax 90 // Batas waktu maksimum untuk pembuatan grafik#define _DiscSlots 20 // Jumlah slot pada Disk Indeks/ * ##################################################################### Variabel Global ############################################### */Rangkaian Cartesian_SetupDetails;Arah boolean, prevDirection;// Pengaturan Alarmfloat 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) Fungsi Perintah untuk Makeblock Me Parameter Input LCD TFT:(String) TFTCmd :Command string################################################## ######### */void CommandToTFT(String TFTCmd){ /* Koneksi Serial digunakan untuk tampilan */ Serial1.println(TFTCmd); delay(5);}/* ########### Akhir CommandToTFT() ########### *//* ########### ###################################### *//* ########### ################################# Cartesian_Setup(Xmin, Xmax, Ymin, Ymax, Window_X1, Window_Y1, Window_X2 , Window_Y2, MinDashQty, ColorF, ColorX, ColorY) Fungsi Menggambar Sumbu Cartesian XY untuk Makeblock Me TFT LCD Parameter Input:(float) Xmin, Xmax, Ymin, Ymax :Nilai rentang sumbu (int) Window_X1, Window_Y1___:Sudut kiri atas dari jendela grafik (int) Jendela_X2, Jendela_Y2___:Sudut kanan bawah jendela grafik (int) MinDashQty_______________:Jumlah garis putus-putus pada sumbu terpendek (int) ColorB, ColorX, ColorY :Menggambar warna untuk Penggunaan Bingkai, sumbu X dan sumbu Y fungsi eksternal 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 ){ /* Batasan Layar * / const int Tampilan ResolutionX =319, DisplayResolutionY =239; /* Batas String Judul */ String 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"; else XminTxt ="X=" + String (Xmin); String XmaxTxt; if (abs(Xmax)>=1000000000) XmaxTxt ="X=" + String (Xmax/1000000000) + "G"; else if (abs(Xmax)>=1000000) XmaxTxt ="X=" + String (Xmax/1000000) + "M"; else if (abs(Xmax)>=1000) XmaxTxt ="X=" + String (Xmax/1000) + "K"; else XmaxTxt ="X=" + String (Xmax); String 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"; else YminTxt ="Y=" + String (Ymin); String YmaxTxt; if (abs(Ymax)>=1000000000) YmaxTxt ="Y=" + String (Ymax/1000000000) + "G"; else if (abs(Ymax)>=1000000) YmaxTxt ="Y=" + String (Ymax/1000000) + "M"; else if (abs(Ymax)>=1000) YmaxTxt ="Y=" + String (Ymax/1000) + "K"; else YmaxTxt ="Y=" + String (Ymax); /* Batas */ int XminPx =Window_X1+1; int XmaxPx =Jendela_X2-1; int YmaxPx =Jendela_Y1+1; int YminPx =Jendela_Y2-1; /* Asal */ int AsalX =XminPx + (int)( (XmaxPx - XminPx) * abs(Xmin) / (abs(Xmax)+abs(Xmin)) ); int AsalY =YmaxPx + (int)( (YminPx - YmaxPx) * abs(Ymax) / (abs(Ymax)+abs(Ymin)) ); /* Bingkai */ CommandToTFT ( "BOX(" + String(Window_X1) + "," + String(Window_Y1)+ "," + String(Window_X2) + "," + String(Window_Y2)+ "," + String( WarnaF) + ");" ); /* Sumbu X */ CommandToTFT ( "PL(" + String(Window_X1+1) + "," + String(OriginY) + "," + String(Window_X2-1) + "," + String(OriginY) + " ," + String(WarnaX) + ");" ); /* Sumbu Y */ CommandToTFT ( "PL(" + String(OriginX) + "," + String(Window_Y1+1) + "," + String(OriginX) + "," + String(Window_Y2-1) + " ," + String(WarnaY) + ");" ); /* Garis putus-putus:Jumlah minimum tanda hubung diberikan oleh "MinDashQty" dan akan dibuat garis putus-putus pada sisi sumbu terpendek sehubungan dengan titik asal. Pada bagian lain, garis putus-putus yang akan ditandai harus ditentukan dengan mempertimbangkan rasio terhadap sisi sumbu terpendek. */ /* 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; jika (XlengthLeft<=1) XlengthLeft_Mod=32767; lain XlengthLeft_Mod=XlengthLeft; jika (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; // Interval min. btw.tanda hubung /* X-Dash L */ DashingInterval =(int) (XlengthLeft / XdashesLeft); if (!(DashingInterval<2)) for (int i=OriginX; i>=XminPx; i-=DashingInterval) CommandToTFT ( "PL(" + String(i) + "," + String(OriginY-2) + " ," + String(i) + "," + String(Asal+2) + "," + String(WarnaX) + ");" ); /* X-Dash R */ DashingInterval =(int) (XlengthRight / XdashesRight); if (!(DashingInterval<2)) for (int i=OriginX; i<=XmaxPx; i+=DashingInterval) CommandToTFT ( "PL(" + String(i) + "," + String(OriginY-2) + ", " + String(i) + "," + String(Asal+2) + "," + String(WarnaX) + ");" ); /* 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(ColorY) + ");" ); /* Menghitung koordinat untuk menampilkan nilai titik akhir sumbu */ int XminTxtX =Window_X1 - (int)(XminTxt.length()*6) - 1, XminTxtY =OriginY, XmaxTxtX =Window_X2 + 1, XmaxTxtY =OriginY, YminTxtX =OriginX, YminTxtY =Window_Y2 + 1, YmaxTxtX =OriginX, YmaxTxtY =Window_Y1 - 12 - 1; /* Kontrol:Jika koordinat apa pun adalah -1, itu akan melampaui batas tampilan dan nilai masing-masing tidak akan ditampilkan */ if (XminTxtX<0) XminTxtX =-1; jika ( (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; jika (YmaxTxtY<0) YmaxTxtY =-1; /* Judul Batas Rentang */ 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(WarnaX) + ");" ); if ( ( YminTxtX !=-1 ) &&( YminTxtY !=-1 ) ) CommandToTFT( "DS12(" + String(YminTxtX) + "," + String(YminTxtY) + ",'" + String(YminTxt) + " '," + String(Warna) + ");" ); if ( ( YmaxTxtX !=-1 ) &&( YmaxTxtY !=-1 ) ) CommandToTFT( "DS12(" + String(YmaxTxtX) + "," + String(YmaxTxtY) + ",'" + String(YmaxTxt) + " '," + String(Warna) + ");" ); /* Nilai Pengembalian String Cartesian_Setup() akan mengembalikan konfigurasi grafis pengepakan string dalam format berikut:"" String dimulai dengan '<' dan diakhiri dengan '>' . Setiap nilai dibatasi oleh ',' */ /* Inisialisasi */ 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) + "," ); /* Close-Out */ Cartesian_SetupDetails +=">";return Cartesian_SetupDetails;}/* ########## Akhir dari Cartesian_Setup() ########### *// * ################################################################ * //* ########################################################## Cartesian_ClearPlotAreas(Descriptor, Color) Plot Area Reset/Hapus Fungsi untuk Makeblock Me TFT LCD Parameter Input:(String) Descriptor :Setup Descriptor - dikembalikan oleh Cartesian_Setup() (int) Color______:Warna yang akan digunakan untuk mengisi area plot Menggunakan fungsi eksternal CommandToTFT ().################################################################ */void Cartesian_ClearPlotAreas(Descriptor String, int Warna){ int X1,Y1,X2,Y2; /* Koordinat batas untuk area plot */ /* Mengekstrak nilai dari Deskriptor */ /* L[0] L[1] L[2] L[3] W[0] W[1] W[2] W[3 ] */ /* Xmin Xmax Ymin Ymax Window_X1 Window_Y1 Window_X2 Window_Y2 */ float L[4]; int W[4]; /* Nilai yang disimpan dalam Deskriptor */ int j=0; /* Penghitung */ String D_Str =""; for (int i=1; i<=(Descriptor.length()-1); i++) if ( Descriptor[i] ==',' ) { if (j<4) L[j]=D_Str.toFloat( ); lain W[j-4]=D_Str.toInt(); D_Str=""; j++; } else D_Str +=Deskriptor[i]; /* Asal */ int AsalX =(W[0]+1) + (int)( ( (W[2]-1) - (W[0]+1) ) * abs(L[0]) / ( abs(L[1])+abs(L[0])) ); int AsalY =(W[1]+1) + (int)( ( (W[3]-1) - (W[1]+1) ) * abs(L[3]) / (abs(L[3 ])+abs(L[2])) ); /* Membersihkan Area Plot */ //Area.1 :X+ Y+ X1 =OriginX + 2; Y1 =W[1] + 1; X2 =W[2] - 1; Y2 =AsalY - 2; CommandToTFT ( "BOXF(" + String(X1) + "," + String(Y1) + "," + String(X2) + "," + String(Y2) + "," + String(Warna) + ");"); //Area.2 :X- Y+ X1 =W[0] + 1; Y1 =W[1] + 1; X2 =AsalX - 2; Y2 =AsalY - 2; CommandToTFT ( "BOXF(" + String(X1) + "," + String(Y1) + "," + String(X2) + "," + String(Y2) + "," + String(Warna) + ");"); //Area.3 :X- Y- X1 =W[0] + 1; Y1 =AsalY + 2; X2 =AsalX - 2; Y2 =W[3] - 1; CommandToTFT ( "BOXF(" + String(X1) + "," + String(Y1) + "," + String(X2) + "," + String(Y2) + "," + String(Warna) + ");"); //Area.4 :X+ Y- X1 =OriginX + 2; Y1 =AsalY + 2; X2 =W[2] - 1; Y2 =W[3] - 1; CommandToTFT ( "BOXF(" + String(X1) + "," + String(Y1) + "," + String(X2) + "," + String(Y2) + "," + String(Warna) + ");" );} /* ########### Akhir dari Cartesian_ClearPlotAreas() ########### *//* ############ ############################################################## *//* # ################################################################### Cartesian_Line(Xp, Yp, X, Y, Deskriptor, Warna) Fungsi Garis Kartesius untuk Makeblock Me TFT LCD Parameter Input:(int) Xp, Yp___:Koordinat plot sebelumnya - nilai y vs x (int) X, Y_____:Koordinat plot saat ini - nilai y vs x (String) Descriptor :Setup Descriptor - dikembalikan oleh Cartesian_Setup() (int) Color______:Menandai warna yang akan digunakan pada (x,y) Menggunakan fungsi eksternal CommandToTFT().############# ################################ */void Cartesian_Line(float Xp, float Yp, float X, float Y , String Descriptor, int Color){ /* Mengekstrak nilai dari Descriptor */ /* L[0] L[1] L[2] L[3] W[0] W[1] W[2] W[3] */ /* Xmin Xmax Ymin Ymax Window_X1 Window_Y1 Window_X2 Window_Y2 */ float L[4 ]; int W[4]; /* Nilai yang disimpan dalam Deskriptor */ int j=0; /* Penghitung */ String D_Str =""; for (int i=1; i<=(Descriptor.length()-1); i++) if ( Descriptor[i] ==',' ) { if (j<4) L[j]=D_Str.toFloat( ); lain W[j-4]=D_Str.toInt(); D_Str=""; j++; } else D_Str +=Deskriptor[i]; /* Asal */ int AsalX =(W[0]+1) + (int)( ( (W[2]-1) - (W[0]+1) ) * abs(L[0]) / ( abs(L[1])+abs(L[0])) ); int AsalY =(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; jika (Y>L[3]) Y=L[3]; jika (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(Warna ) + ");" );}/* ########### Akhir Cartesian_Line() ########### *//* ######## ############################################################### *//* ###### ###################################### readFrequency(_DI_FrequencyCounter_Pin, _ReadingSpeed) Input Fungsi Pembacaan Frekuensi Parameter:(int) _DI_FrequencyCounter_Pin :Pin digital untuk dibaca (float) _ReadingSpeed____________:Kecepatan membaca khusus antara 0...10 (Catatan.1) Catatan.1:_ReadingSpeed ​​adalah nilai untuk menentukan berapa lama perubahan akan dihitung. Itu tidak boleh 0(nol), nilai negatif atau nilai yang lebih besar dari 10. Ketika _ReadingSpeed ​​berubah, 1 detik harus dibagi dengan nilai ini untuk menghitung durasi penghitungan yang diperlukan. Sebagai contoh; - _ReadingSpeed ​​=0.1 -> input dihitung selama 10 detik (=1/0.1) - _ReadingSpeed ​​=0.5 -> input dihitung selama 2 detik (=1/0.5) - _ReadingSpeed ​​=2.0 -> input dihitung selama 0.5 detik (=1/2) - _ReadingSpeed ​​=4.0 -> input akan dihitung selama 0,25 detik (=1/4) Penting dicatat bahwa, peningkatan _ReadingSpeed ​​adalah kerugian terutama pada frekuensi yang lebih rendah (umumnya di bawah 100 Hz) karena kesalahan penghitungan meningkat hingga 20%~40% dengan mengurangi frekuensi.################################################# ######## */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 _Frekuensi =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(){ 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;}

Skema

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

Proses manufaktur

  1. Paduan Tembaga Tungsten untuk Motor
  2. Mengontrol Efek dengan Sensor Nyata
  3. Arduino Nano:Kontrol 2 Motor Stepper Dengan Joystick
  4. Mengontrol Matriks LED dengan Arduino Uno
  5. Pemantauan Suhu CERDAS untuk Sekolah
  6. Perpustakaan Port IO 8-Bit untuk Arduino
  7. Matriks Keyboard Prototipe 64-Key untuk Arduino
  8. TFT Shield untuk Arduino Nano - Mulai
  9. Input Analog Terisolasi untuk Arduino
  10. Robot untuk navigasi dalam ruangan yang sangat keren