Kontrol PID: Apakah menambahkan penundaan sebelum loop berikutnya merupakan ide yang bagus?

Jan 11 2021

Saya menerapkan kontrol PID di c ++ untuk membuat robot penggerak diferensial memutar jumlah derajat yang akurat, tetapi saya mengalami banyak masalah.

Keluar dari loop kontrol lebih awal karena runtime loop cepat

Jika robot mengukur kesalahannya kurang dari 0,5 derajat, ia keluar dari loop kontrol dan menganggap belokan "selesai" (.5 adalah nilai acak yang mungkin saya ubah di beberapa titik). Tampak bahwa loop kontrol berjalan sangat cepat sehingga robot dapat berbelok pada kecepatan yang sangat tinggi, melewati setpoint, dan keluar dari loop / cut daya motor, karena berada pada setpoint sesaat. Saya tahu bahwa ini adalah tujuan keseluruhan dari kontrol PID, untuk mencapai setpoint secara akurat tanpa overshooting, tetapi masalah ini membuatnya sangat sulit untuk menyetel konstanta PID. Misalnya, saya mencoba mencari nilai kp sehingga ada osilasi yang stabil, tetapi tidak pernah ada osilasi karena robot mengira telah "selesai" setelah melewati setpoint. Untuk mengatasinya,Saya telah menerapkan sistem di mana robot harus berada pada setpoint untuk jangka waktu tertentu sebelum keluar, dan ini telah efektif, memungkinkan terjadinya osilasi, tetapi masalah keluar dari loop lebih awal tampak seperti masalah yang tidak biasa dan solusi saya mungkin salah.

Istilah D tidak berpengaruh karena runtime yang cepat

Setelah robot saya berosilasi secara terkontrol hanya dengan menggunakan P, saya mencoba menambahkan D untuk mencegah overshoot. Namun, ini tidak berpengaruh untuk sebagian besar waktu, karena loop kontrol berjalan sangat cepat sehingga 19 loop dari 20, tingkat perubahan kesalahan adalah 0: robot tidak bergerak atau tidak cukup bergerak untuk itu untuk diukur pada saat itu. Saya mencetak perubahan dalam kesalahan dan istilah turunan setiap loop untuk mengonfirmasi ini dan saya dapat melihat bahwa keduanya akan menjadi 0 untuk sekitar 20 siklus loop sebelum mengambil nilai yang wajar dan kemudian kembali ke 0 untuk 20 siklus lainnya. Seperti yang saya katakan, saya pikir ini karena siklus loop sangat cepat sehingga robot benar-benar tidak bergerak cukup untuk segala jenis perubahan kesalahan yang nyata.Ini adalah masalah besar karena itu berarti bahwa istilah D pada dasarnya tidak berpengaruh pada gerakan robot karena hampir selalu 0. Untuk memperbaiki masalah ini, saya mencoba menggunakan nilai bukan-nol terakhir dari turunan sebagai ganti nilai 0, tetapi ini tidak bekerja dengan baik, dan robot akan berosilasi secara acak jika turunan terakhir tidak mewakili tingkat perubahan kesalahan saat ini.

Catatan: Saya juga menggunakan feedforward kecil untuk koefisien gesekan statis, dan saya menyebutnya feedforward "f"

Haruskah saya menambahkan penundaan?

Saya menyadari bahwa menurut saya sumber dari kedua masalah ini adalah loop yang berjalan sangat cepat, jadi sesuatu yang saya pikirkan adalah menambahkan pernyataan tunggu di akhir loop. Namun, sepertinya solusi yang buruk secara keseluruhan untuk sengaja memperlambat loop. Apakah ini ide yang bagus?

turnHeading(double finalAngle, double kp, double ki, double kd, double f){
    std::clock_t timer;
    timer = std::clock();

    double pastTime = 0;
    double currentTime = ((std::clock() - timer) / (double)CLOCKS_PER_SEC);

    const double initialHeading = getHeading();
    finalAngle = angleWrapDeg(finalAngle);

    const double initialAngleDiff = initialHeading - finalAngle;
    double error = angleDiff(getHeading(), finalAngle);
    double pastError = error;

    double firstTimeAtSetpoint = 0;
    double timeAtSetPoint = 0;
    bool atSetpoint = false;

    double integral = 0;
    double derivative = 0;
    double lastNonZeroD = 0;

    while (timeAtSetPoint < .05)
    {
        updatePos(encoderL.read(), encoderR.read());
        error = angleDiff(getHeading(), finalAngle);

        currentTime = ((std::clock() - timer) / (double)CLOCKS_PER_SEC);
        double dt = currentTime - pastTime;

        double proportional = error / fabs(initialAngleDiff);
        integral += dt * ((error + pastError) / 2.0);
        double derivative = (error - pastError) / dt;
        
        //FAILED METHOD OF USING LAST NON-0 VALUE OF DERIVATIVE
        // if(epsilonEquals(derivative, 0))
        // {
        //     derivative = lastNonZeroD;
        // }
        // else
        // {
        //     lastNonZeroD = derivative;
        // }

        double power = kp * proportional + ki * integral + kd * derivative;

        if (power > 0)
        {
            setMotorPowers(-power - f, power + f);
        }
        else
        {
            setMotorPowers(-power + f, power - f);
        }

        if (fabs(error) < 2)
        {
            if (!atSetpoint)
            {
                atSetpoint = true;
                firstTimeAtSetpoint = currentTime;
            }
            else //at setpoint
            {
                timeAtSetPoint = currentTime - firstTimeAtSetpoint;
            }
        }
        else //no longer at setpoint
        {
            atSetpoint = false;
            timeAtSetPoint = 0;
        }
        pastTime = currentTime;
        pastError = error;
    }
    setMotorPowers(0, 0);
}

turnHeading(90, .37, 0, .00004, .12);

Jawaban

3 UgoPattacini Jan 12 2021 at 05:14

Jangan lepaskan pengontrol Anda.

Tujuan pengontrol tidak hanya untuk mengarahkan sistem Anda ke setpoint yang diinginkan sesuai dengan respons dinamis yang telah ditentukan, tetapi juga untuk melawan faktor eksternal potensial yang dapat menghalangi tugas ini. Pikirkan gangguan yang akan mendorong sistem jauh dari setpoint setelah tercapai. Jadi, pengontrol harus selalu beroperasi (kecuali jika Anda perlu mengubah tugas itu sendiri dan akibatnya juga mengubah pengontrol).

Untuk tujuan ini, Anda tentu perlu menjumlahkan bagian integral, yang bertanggung jawab untuk mencapai kesalahan kondisi-mapan nol di hadapan kuantitas yang tidak dimodelkan dan gangguan eksternal.

Ini adalah sumber daya yang cukup terkait: https://robotics.stackexchange.com/a/19198/6941.

Tinggalkan turunannya.

95% pengontrol PID di industri adalah pengontrol PI (lihat "Sistem Umpan Balik" Astrom, Murray) karena bagian D dapat memainkan peran penting hanya dengan proses yang lambat (seperti yang berkaitan dengan pengaturan suhu dan ketinggian tangki). Ini jelas bukan kasus Anda. Entah bagaimana sumber terkait tentang kesulitan istilah turunannya adalah:

  • https://robotics.stackexchange.com/a/21556/6941.
  • https://robotics.stackexchange.com/a/21555/6941.

Setelah robot saya berosilasi secara terkontrol hanya dengan menggunakan P, saya mencoba menambahkan D untuk mencegah overshoot.

Nah, tampaknya Anda mengikuti resep Ziegler-Nichols untuk menyetel pengontrol Anda. Ada tabel yang harus Anda ikuti yang memberi Anda perkiraan untuk keuntungan. Namun, ini adalah heuristik dan kemungkinan itu tidak akan berhasil dalam kasus Anda.

Cukup jatuhkan bagian D dan fokus pada pengontrol PI. Ada juga varian ZN lain yang tidak bergantung pada osilasi yang dibuat dengan sengaja:https://robotics.stackexchange.com/a/21616/6941.

Jangan pernah menambahkan penundaan ke sistem.

Penundaan adalah hal buruk yang harus dihadapi dalam lingkaran kontrol, dan insinyur binatang terburuk harus melawan ( lihat ini 🎥 ) karena mereka mengurangi secara signifikan margin fase yang mendorong keseluruhan sistem menuju ketidakstabilan.

Jika Anda menganggap loop Anda terlalu cepat, maka terapkan input-shaping ke setpoint (dengan misalnya minimum-jerk trajectory ) untuk memperhalus onset langkah yang tajam. Sistem loop tertutup akan bereaksi dengan lebih anggun. Kemungkinan lain adalah bahwa gain P Anda terlalu tinggi: lepaskan saja pengontrolnya.

Dalam hal ini, jika Anda memasukkan suku integral I, maka Anda perlu mempertimbangkan waktu sampel $T_s$ demikian juga.

1 TimWescott Jan 12 2021 at 08:36

Akan jauh lebih baik untuk menentukan laju sampel tetap untuk pengontrol Anda. Aturan praktis yang sangat kasar adalah bahwa berapa pun waktu penyelesaian yang Anda butuhkan dari loop setelah beroperasi dalam rezim linier , interval pengambilan sampel Anda harus antara 10 kali dan 100 kali lebih kecil dari waktu penyelesaian. Dengan kata lain, laju sampel harus 10 hingga 100 kali lebih cepat daripada bandwidth loop yang diinginkan.

Sebagai bonus tambahan kecil, ini berarti Anda dapat menghitung dt di luar loop

Berdasarkan preferensi, laju sampel harus ditentukan oleh perangkat keras, misalnya oleh pengatur waktu perangkat keras, dan bahkan pengambilan sampel posisi yang lebih baik harus dilakukan oleh perangkat keras, yang dipicu oleh pengatur waktu. Ini secara signifikan mengurangi kendala waktu nyata pada perangkat lunak.

Jika Anda menggunakan frekuensi sampel yang wajar dan sebagian besar tidak mendaftarkan perubahan di pembuat enkode, maka pembuat enkode Anda tidak memiliki langkah yang cukup.

Saya akan tidak setuju dengan @Ugo_Pattachini, karena jika loop melibatkan motor dan pengontrol Anda cukup bersih, beberapa tindakan diferensial dapat bermanfaat. Tetapi ada kemungkinan bagus bahwa itu perlu bandlimited (yaitu, Anda memerlukan pengontrol lead-lag), dan jika Anda melakukan penyetelan PID di kursi, ada kemungkinan besar Anda tidak memiliki alat untuk itu. atur batasan pita dengan benar.

Namun, jika Anda tidak dapat menerapkan pengaturan waktu yang sangat ketat pada pengambilan sampel pembuat enkode, jangan coba-coba menggunakan kontrol turunan. Waktu pengambilan sampel yang tidak teratur ditambah beberapa kecepatan pada motor Anda akan menimbulkan kebisingan ekstra, dan pengontrol turunan cenderung memperkuat kebisingan. Jika Anda tidak dapat mengatur sinyal yang bersih untuk dikontrol, kontrol turunan tidak akan cocok.