Anda perlu menangani dua masalah:
- melimpah aritmatika
- windup integrator
Overflow aritmatika cukup mudah - setiap kali Anda melakukan bilangan bulat matematika, pastikan Anda menggunakan nilai menengah selebar lebih besar: misalnya, jika a
dan b
16-bit, dan Anda menambahkan / mengurangi, gunakan perantara 32-bit nilai, dan batasi pada kisaran nilai 16-bit (0 hingga 65535 untuk yang tidak ditandatangani, -32768 hingga 32767 untuk ditandatangani) sebelum memasukkan kembali ke 16 bit. Jika Anda benar - benar yakin bahwa Anda tidak akan pernah bisa overflow, karena Anda benar - benar yakin tentang rentang variabel input, maka Anda dapat melewati langkah ini, tetapi waspadalah.
Masalah windup integrator lebih halus. Jika Anda memiliki kesalahan besar untuk periode waktu yang lama, sehingga Anda mencapai batas saturasi dari output controller Anda, tetapi kesalahan itu masih nol, maka integrator akan terus mengakumulasi kesalahan, mungkin menjadi jauh lebih besar dari yang seharusnya dicapai stabil. Setelah pengontrol keluar dari saturasi, integrator harus turun kembali, menyebabkan penundaan yang tidak perlu dan mungkin ketidakstabilan dalam respons pengontrol Anda.
Pada catatan lain:
Saya akan sangat menyarankan (ya, saya tahu pertanyaan ini berusia 18 bulan, jadi Anda mungkin sudah selesai dengan tugas Anda, tetapi untuk kepentingan pembaca mari kita berpura-pura tidak) bahwa Anda menghitung istilah integral berbeda: Alih-alih Ki * (kesalahan terintegrasi), hitung integral (kesalahan Ki *).
Ada beberapa alasan untuk melakukannya; Anda dapat membacanya di posting blog yang saya tulis tentang cara menerapkan pengendali PI dengan benar .
<stdint.h>
untukuint8_t
danuint16_t
, bukanunsigned int
danunsigned char
.unsigned
variabel untuk pengontrol PI? Ini menambah banyak kerumitan pada kode Anda;if/else
case terpisah tidak perlu (kecuali jika Anda menggunakan keuntungan yang berbeda tergantung pada tanda kesalahan) Anda juga menggunakan nilai absolut dari derivatif, yang tidak benar.PID_derivative
tugas; Anda mendapatkan nilai yang sama jika Anda beralihPID_error
danPID_lastError
. Dan dalam hal ini Anda sudah kehilanganPID_error
tanda: jika terakhir kalisetMotorSpeed =8
dancurrentMotorSpeed = 15
, kali inisetMotorSpeed = 15
dancurrentMotorSpeed = 8
, maka Anda akan mendapatkanPID_derivative
nilai 0, yang salah.unsigned char
adalah tipe 8-bit dan tipeunsigned int
16-bit: jikaPID_kd = 8
danPID_derivative = 32
, maka produk mereka akan(unsigned char)256 == 0
, karena di C, produk dua bilangan bulat dari tipe T yang sama juga dari itu tipe yang sama T. Jika Anda ingin melakukan 8x8 -> 16 kali, Anda perlu melemparkan salah satu syarat ke angka 16-bit yang tidak ditandatangani sebelum kali, atau menggunakan kompiler intrinsik (MCHP menyebutnya "bawaan") yang dirancang untuk memberi Anda 8x8 -> 16 kali lipat.