Thông báo

Collapse
No announcement yet.

Cần các bác giúp giải quyết lỗi PID này!

Collapse
X
 
  • Lọc
  • Giờ
  • Show
Clear All
new posts

  • Cần các bác giúp giải quyết lỗi PID này!

    Hi,
    Lần đầu tiên tui làm thử điều khiển PID, mới làm để demo nên chương trình chỉ việc tính các số hạng PID và điều khiển PWM theo các thông số tính được. Mạch de mo và video demo bên dưới. Tui không hiểu một lỗi xảy ra (tìm không ra nguyên nhân) khi giá trị đặt lớn gần bằng giá trị max thì PWM luôn bằng 0 cho dù sau đó giá trị setpoint được giảm nhỏ lại.
    Chú thích phần video: Đường màu vàng là đường giá trị đặt, thay đổi bằng biến trở để theo dõi quá trình điều khiển. Đường màu xanh là đường biểu diễn giá trị điều khiển. Khi giá trị đặt được đưa lên cao thì giá trị điều khiển bị rớt xuống 0 và giữ nguyên giá trị 0 luôn cho dù giá trị đặt thay đổi thế nào đi chăng nữa.
    Mạch test:

    Video:
    http://www.youtube.com/watch?v=JyIw7_3UBV0
    Bác nào có kinh nghiệm trong vụ điều khiển PID xin giúp giùm.
    Sau đây là code CCS chương trình test
    Code:
    #include <16F886.h>
    #device * = 16 ADC = 10
    #fuses HS, NOPROTECT, NODEBUG, NOWDT
    #use delay(clock = 20M)
    #priority INT_TIMER0
    
    // control loop constants
    float Kp; // proportional gain
    float Ki; // integral gain
    float Kd; // differential gain
    
    // terms
    float Tp; // proportional term
    float Ti; // integral term
    float Td; // differential term
    
    // circular queue vars
    signed long ErrorHistory[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
    int InputIdx = 0;
    int GetIdx;
    signed long PrevError;
    signed long RecError;
    
    signed long Error;
    signed long DeDt; // change in Error over time
    signed int32 ErrorSum = 0; // sum of Errors, for integral term
    signed long DesiredValue;
    
    int8 Power;
    volatile long Setpoint;
    float temp_float;
    
    void main(void)
    {
        setup_adc(ADC_CLOCK_DIV_32);
        setup_adc_ports(sAN0 | sAN1);
        setup_timer_0(T0_INTERNAL | T0_DIV_1);
        set_timer0(55536);    // interrupt interval 2ms
        setup_timer_1(T1_INTERNAL | T1_DIV_BY_8);
        set_timer1(3036);    // interrupt interval 100ms
        setup_timer_2(T2_DIV_BY_1, 249, 16);    // PWM frequency 20KHz
        setup_ccp1(CCP_PWM);
        set_pwm1_duty(0);
        enable_interrupts(GLOBAL);
        Kp = 2;
        Ki = 2.4;
        Kd = 0.6;
        set_adc_channel(1);
        read_adc(ADC_START_ONLY);
        while(!adc_done());
        Setpoint = read_adc(ADC_READ_ONLY);
        enable_interrupts(INT_TIMER0);
        enable_interrupts(INT_TIMER1);
        while(1)
        {
            //sleep();
        }
    }
    
    #int_timer0
    void PID_calculate(void)
    {
        int i = 0;
        
        set_timer0(55536);
        set_adc_channel(0);
        read_adc(ADC_START_ONLY);
        while(!adc_done());
        
        // calculate the raw Error
        Error = Setpoint - (signed long)read_adc(ADC_READ_ONLY);
        
        // calculate the proportional term
        Tp = Kp * Error;
        
        // calculate the integral term
        ErrorSum += (signed int32)Error;
        temp_float = ErrorSum;
        Ti = Ki * temp_float;
        
        // use a circular queue to save a history of the last 8 samples
        // this will be used to calculate the differential term
        ErrorHistory[InputIdx] = Error;
        InputIdx++;
        InputIdx &= 0x07;    // keep in 0..7 range
        GetIdx = InputIdx;    // Go to the oldest values in past
        
        // calculate the average for the 4 oldest samples
        for (i = 0, PrevError = 0; i < 4; i++)
        {
            PrevError += ErrorHistory[GetIdx];
            GetIdx++;
            GetIdx &= 0x07;
        }
        
        // calculate the average for the 4 most recent samples
        for (i = 0, RecError = 0; i < 4; i++)
        {
            RecError += ErrorHistory[GetIdx];
            GetIdx++;
            GetIdx &= 0x07;
        }
        
        // calculate the differential term
        DeDt = RecError - PrevError;
        Td = Kd * DeDt;
        
        // calculate the desired Power
        DesiredValue = (signed long)(Tp + Td + Ti);
        
        // set the correct Power
        if(DesiredValue < 0)
            DesiredValue = 0;
        else if(DesiredValue > 1023)
            DesiredValue = 1023;
        
        temp_float = (DesiredValue*250/1023);
        Power = (int)temp_float;
        //Power = (int)(DesiredValue*250/1023);
        set_pwm1_duty(Power); // this could be pwm duty, etc
        clear_interrupt(INT_TIMER0);
    } 
    #int_TIMER1
    void update_setpoint(void)
    {
        set_timer1(3036);
        set_adc_channel(1);
        read_adc(ADC_START_ONLY);
        while(!adc_done());
        Setpoint = read_adc(ADC_READ_ONLY);
        output_toggle(PIN_C3);
        clear_interrupt(INT_TIMER1);
    }
    Thân ái.
    Last edited by Hard; 01-07-2011, 23:28.
    Đường đến những ngày vinh quang không còn xa
    Con đường chúng ta... chúng ta đã chọn.

  • #2
    Hi,
    Tui suy nghĩ từ đêm hôm qua đến giờ và đã tìm ra lỗi, lỗi rất đơn giản mà rất hiểm, nó nằm ở chỗ biến ErrorSum. Khi khai báo kiểu signed long thì nó bị lỗi tràn và làm lỗi luôn các thứ khác, chỉ cần đổi thành kiểu signed int32 thì mọi thứ đẹp như mơ. Bây giờ giá trị điều khiển nó bám theo giá trị đặt cứ như là tên lửa dí theo máy bay vậy.
    Mặc dù vậy về lý thuyết PID tui cũng không hiểu lắm, phương châm là cứ áp dụng trước cho được việc đã rồi một ngày nào đó ngộ ra vấn đề vì vậy các bác có kinh nghiệm về PID xin tiếp tục chỉ giáo.
    Thân ái.
    Đường đến những ngày vinh quang không còn xa
    Con đường chúng ta... chúng ta đã chọn.

    Comment


    • #3
      Theo mình thấy thì :
      - Bạn đang điều khiển độ sáng của LED ah?
      - Setpoint lấy từ AN0?
      - Phản hồi đọc về AN1, phản hồi này có phải là điện áp qua bóng đèn ko bạn?

      Umh, theo mình thì nếu như thế thì đối tượng của bạn có mô hình toán gần như tuyến tính, nên chỉ cần điều khiển vòng hở cũng được. Còn nếu muốn dùng PID và tìm hiểu nó bạn nên thay bóng đèn bằng 1 con động cơ chẳng hạn, cũng dùng PWM để thấy rõ tác dụng của PID hơn.

      Comment


      • #4
        Nguyên văn bởi achut Xem bài viết
        Theo mình thấy thì :
        - Bạn đang điều khiển độ sáng của LED ah?
        - Setpoint lấy từ AN0?
        - Phản hồi đọc về AN1, phản hồi này có phải là điện áp qua bóng đèn ko bạn?

        Umh, theo mình thì nếu như thế thì đối tượng của bạn có mô hình toán gần như tuyến tính, nên chỉ cần điều khiển vòng hở cũng được. Còn nếu muốn dùng PID và tìm hiểu nó bạn nên thay bóng đèn bằng 1 con động cơ chẳng hạn, cũng dùng PWM để thấy rõ tác dụng của PID hơn.
        Cảm ơn bác đã góp ý, thực ra bóng đèn đó để test xem PIC có bị treo hay không mà thôi. Điện áp ra đang cho cấp cho một điện trở và có tụ lọc để ổn định. Setpoint lấy từ AN1 bác à, bác xem code đính kèm theo đó. Bài toán của tui thì cũng dạng tuyến tính cả thôi, tui cũng chưa thử điều khiển động cơ xem sao vì đang bận, khi nào có thời gian chắc cũng phải thử xem thế nào.
        Thân ái.
        Đường đến những ngày vinh quang không còn xa
        Con đường chúng ta... chúng ta đã chọn.

        Comment


        • #5
          Hi, Hard
          uh, Setpoint của bạn là trong hàm ngắt T1(Setpoint có thể để trong chương trình chính cũng được vì chu kỳ trích mẫu của bạn là 2ms update giá trị điều khiển 1 lần=>tính ra tín hiệu điều khiển) . Nếu chưa có điều kiện làm động cơ thì mình làm thử thế này nhé :
          - Thay con điện trở đó bằng 1 con điện trở công suất.
          - Ốp 1 con cảm biến nhiệt vào đó rồi điều khiển nhiệt độ. Giống như lò nhiệt ấy.
          Sau đó bạn lấy tín hiệu phản hồi từ con cảm biến nhiệt về, sử dụng PID điều khiển nhiệt độ tỏa ra của con điện trở.
          Đặt vài giá trị đặt xem phản ứng thế nào.(chú ý chu kỳ trích mẫu, với thứ nguyên).

          Còn bài toán trên của bạn cứ tưởng tượng, đối tượng tuyến tính thì bạn đặt 1 giá trị bất kỳ PWM nào thì bạn có thể tìm được điện áp theo 1 tỷ lệ nào đó trên con trở. PID khi đó còn ý nghĩa gì ko? Hay chỉ cần thằng P thôi.

          Chúc thành công!

          Comment


          • #6
            Mình cũng làm mới xong cái PID cho động cơ, dùng 89V51RB2. Xài họ 8051 tốc độ không cao nên chu kì tính toán PID của mình là 100ms! Làm PID cho động cơ thì có nhiều vấn đề cần chú ý như thiết lập các ngắt, độ ưu tiên ngắt, giải thuật PID,... Như bạn ở trên nói khi làm với PID thì nên có đối tượng điều khiển là động cơ, tải nhiệt,... Trong quá trình PID bằng động cơ ta sẽ tăng giảm tải (mình thì lấy tay bóp trục motor) để thấy PID điều chỉnh.
            Sau đây là post cái hình mạch cho xôm tụ. Động cơ 24V, giao diện bằng VB. Thông số Kp,Ki,Kd chưa tối ưu cho lắm, bác nào biết ngoài zigler-nichol còn cách nào xác định 3 thông số trên tốt hơn không?
            Attached Files

            Comment


            • #7
              Nguyên văn bởi Hard Xem bài viết
              Hi,
              Tui suy nghĩ từ đêm hôm qua đến giờ và đã tìm ra lỗi, lỗi rất đơn giản mà rất hiểm, nó nằm ở chỗ biến ErrorSum. Khi khai báo kiểu signed long thì nó bị lỗi tràn và làm lỗi luôn các thứ khác, chỉ cần đổi thành kiểu signed int32 thì mọi thứ đẹp như mơ. Bây giờ giá trị điều khiển nó bám theo giá trị đặt cứ như là tên lửa dí theo máy bay vậy.
              Mặc dù vậy về lý thuyết PID tui cũng không hiểu lắm, phương châm là cứ áp dụng trước cho được việc đã rồi một ngày nào đó ngộ ra vấn đề vì vậy các bác có kinh nghiệm về PID xin tiếp tục chỉ giáo.
              Thân ái.
              PID là phương pháp điều khiển rất hay. Tuy nhiên ta phải chọn kp,ki sao cho có thời gian xác lập độ vọt lố theo đúng yêu cầu mới là khó

              Comment


              • #8
                Nguyên văn bởi achut Xem bài viết
                Hi, Hard
                uh, Setpoint của bạn là trong hàm ngắt T1(Setpoint có thể để trong chương trình chính cũng được vì chu kỳ trích mẫu của bạn là 2ms update giá trị điều khiển 1 lần=>tính ra tín hiệu điều khiển) . Nếu chưa có điều kiện làm động cơ thì mình làm thử thế này nhé :
                - Thay con điện trở đó bằng 1 con điện trở công suất.
                - Ốp 1 con cảm biến nhiệt vào đó rồi điều khiển nhiệt độ. Giống như lò nhiệt ấy.
                Sau đó bạn lấy tín hiệu phản hồi từ con cảm biến nhiệt về, sử dụng PID điều khiển nhiệt độ tỏa ra của con điện trở.
                Đặt vài giá trị đặt xem phản ứng thế nào.(chú ý chu kỳ trích mẫu, với thứ nguyên).

                Còn bài toán trên của bạn cứ tưởng tượng, đối tượng tuyến tính thì bạn đặt 1 giá trị bất kỳ PWM nào thì bạn có thể tìm được điện áp theo 1 tỷ lệ nào đó trên con trở. PID khi đó còn ý nghĩa gì ko? Hay chỉ cần thằng P thôi.

                Chúc thành công!
                Hi,
                Setpoint tui để trong hàm ngắt với ý định cho CPU đi ngủ nhưng rất tiếc là với chip này thì khi CPU ngủ thì Timer 2 cũng dừng nên không PWM được.
                Các hệ số Kp, Ki, Kd là tui áp đại vô thôi, từ công thức Kp = 1.2T/L, Ki = 2L, Kd = 0.5L. Trong đó L là độ trễ, T là khoảng thời gian đạt đến Setpoint, nếu biểu diễn dạng đồ thị thì kẻ một đường tiếp tuyến với giá trị output, đường này sẽ cắt trục thời gian tại điểm A (chẳng hạn) thì khoảng từ 0-A là L, A-B (điểm đạt setpoint) là T. Để cho chẵn tui chọn L = 1.2, T > L --> T = 2 ==> Kp = 2, Ki = 2.4, Kd = 0.6. Thay đổi kiểu gì, tối ưu như thế nào thì tui không biết, các bác biết thì cứ chỉ giáo thêm.
                Về vấn đề điều khiển PWM thì tui cứ áp như thế này khi Setpoint đạt đến giá trị MAX thì duty của PWM cũng ở DUTY_MAX còn ở khoảng giữa thì nó tỉ lệ thế nào không quan tâm. Khi giá trị ra đạt đến MAX hay Setpoint đặt ở MAX thì giá trị ADC sẽ phải là 1023 (10 bit ADC) cái này được thiết lập bằng mạch phần cứng.
                Cách làm của tui là như thế, mong các bác tiếp tục chỉ giáo.
                Thân ái.
                Đường đến những ngày vinh quang không còn xa
                Con đường chúng ta... chúng ta đã chọn.

                Comment


                • #9
                  Theo mình biết thì bạn đang dùng PP Ziegler-Nichols 1. PP này được nói trong tài liệu LTDK tuyến tính của thầy Nguyễn Doãn Phước. Phương pháp này trên lý thuyết thì áp dụng cho đối tượng có mô hình toán học xấp xỉ quán tính bậc 1 dạng:
                  S(s) = k*e^(-Ls)/(1+Ts) (1)
                  - Còn đối tượng con trở của bạn thì khác. Nên chắc ko áp dụng được mấy công thức đó.
                  - Còn nữa là theo mình thì sử dụng PID với vi điều kiển thì người ta sử dụng bộ PID rời rạc (số).
                  Tức là cái mô hình 1 phải chuyển sang miền rời rạc qua mấy khâu chuyển đổi (tham khảo SGK hi). Rồi từ đấy tìm ra các thông số theo công thức như bạn nói. Nhưng đấy là lý thuyết. Còn thực tế tôi hay thấy người ta dùng PID trên vi điều khiển như sau :
                  - Mô hình đối tượng : Ko biếtcác thông số(Mặc dù biết động cơ dạng gì, lò nhiệt dạng gì… hic, cái này thì phải đo đạc các thông số - mà mình thì biết lấy cái gì đo đây).
                  - Các thông số quan tâm :
                  Error : sai lệch giữa giá trị đặt (SP) và giá trị thực phản hồi về (PV) – thường lấy 3 giá trị (1 hiện tại, 2 quá khứ trước đó).
                  VD : er[3];
                  Kp, Ki, Kd : 3 thông số PID. (Do đối tượng ko biết mô hình nó như thế nào nên tạm thời mò 3 cái này – Quên, phải nói là PP thực nghiệm).
                  Ts : chu kỳ trích mẫu – khoảng thời gian bạn đọc PV về và tính ra tín hiệu điều khiển. (Của bạn là 2ms ở trên đó). Cho 1 con Timer làm việc này.
                  Upid : đầu ra tín hiệu điều khiển (cái mà bạn đưa vào thanh ghi giá trị PWM ấy).
                  Thế tính toán bằng phần mềm ntn ?
                  simple code C - PID :
                  Hàm ngắt Timer với thời gian ngắt là Ts :
                  Void interrupt_Timer()
                  {
                  Er[0] = SP-PV; //tính sai lệch hiện tại
                  Upid = Upid+ Kp*Er[0]-Ki*Er[1]+Kd*Er[2]; /*đầu ra tín hiệu điều khiển, hic, thực ra thì Kp, Ki, Kd bản chất ko pải theo công thức này, nhưng mà mình hay chọn các hằng số để thay vào chúng nên để thê*/
                  Er[2] = Er[1]; //lưu sai lệch
                  Er[1] = Er[0];
                  /*giới hạn giá trị trên dưới của Upid*/

                  PWM = Upid; //gán tín hiệu điều khiển vào thanh ghi PWM.
                  }
                  Kiến thức có hạn, có bạn nào góp ý tiếp giúp bạn Hard. (Hoàn thành bộ inveter có phản hồi - đoán mò nhé, Hi).

                  Comment


                  • #10
                    Ngày xưa khi làm module băm xung pure sine dùng cho inverter mình cũng có dùng PID để ổn áp. Vấn đề thuật toán PID thì rõ rồi. Nhưng chọn hệ số Kp, Ki, Kd đôi khi tưởng ổn rồi thì lại phải thử lại. Chẳng hạn như khi chạy quạt và bóng đèn thì ổn áp rất tốt, nhưng khi bật TV thì đầu ra lại dao động xung quanh setpoint. Bạn nên test kĩ với từng loại tải, mức tải khác nhau trong giới hạn cho phép.

                    Chúc bạn thành công!

                    Về module sine cho inverter, bạn có thể tham khảo mô hình module của mình đã từng nói qua ở luông sau đây. Mình không open vì lí do uy tín, nhưng bạn sẽ tìm được ở đó ý tưởng để hoàn thiện sản phẩm của bạn.
                    http://www.dientuvietnam.net/forums/...7&d=1286980040
                    http://www.dientuvietnam.net/forums/...Inverter/page2
                    Last edited by bvhoang; 05-07-2011, 01:40.

                    Comment


                    • #11
                      Hi,
                      Cảm ơn các bác đã góp ý, đúng là tui cũng đang định áp dụng nó vào việc ổn định dạng sóng sine đầu ra không phụ thuộc vào tải. Tuy nhiên do tốc độ VXL quá chậm (ADC chậm) nên kết quả là 'mẹ của thành công' chứ không phải là thành công. Vì hạn chế này nên tui định quay lại phương pháp Sine PWM bằng kiểu tra bảng nhưng tra nhiều bảng tùy theo mức tải chứ không tra 1 bảng cố định.
                      @bvhoang: bác có thể bật mí sơ sơ về module của bác như dùng VXL gì, phương pháp gì và điều kiện gì để thực hiện được được không?
                      Thân ái.
                      Đường đến những ngày vinh quang không còn xa
                      Con đường chúng ta... chúng ta đã chọn.

                      Comment

                      Về tác giả

                      Collapse

                      Hard Nothing to say Tìm hiểu thêm về Hard

                      Bài viết mới nhất

                      Collapse

                      Đang tải...
                      X