Thông báo

Collapse
No announcement yet.

Giải thích đoạn code điều khiển động cơ DC sử dụng PID giúp mình với !

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

  • Giải thích đoạn code điều khiển động cơ DC sử dụng PID giúp mình với !

    Đây là một đoạn code mình viết theo một tài liệu hướng dẫn trên mạng :
    PHP Code:
    void PID_tocdo(void)
    {
        
    Err_now V_set V_real ;   
        
    Err_sum Err_now Err_last 
        
    Err_del Err_now Err_last ;
        
    Err_last Err_now ;
        
        
    Duty Duty Kp*Err_now Ki*Err_sum Kd*Err_del ;
        
        if      ( 
    Duty 255 Duty 255 ;
        else if ( 
    Duty Duty ;    
        

    Cho mình hỏi tại sao ở đây sử dụng Duty ( chữ màu đỏ )

    Duty = Duty + Kp*Err_now + Ki*Err_sum + Kd*Err_del ;
    Theo mình nghĩ thì nó phải là giá trị Duty_set ( được thiết đặt để có V_set mong muốn nào đó .) chứ không phải là Duty .
    Như vậy ở đoạn code trên thì Duty thì liên tiếp được cộng dồn có phải không ?

    Ai biết thì chỉ dùm mình với !

    Còn đây là một hướng dẫn trên mạng :
    PHP Code:
    Các biến được sử dụng:    
    SpeedTốc độ động cơtính bằng số xung đếm được trong một thời gian lấy mẫu Ts(đã định sẵn)
    Set_PointGiá trị cài đặtdùng tham chiếu với Speed nên có cùng đơn vị và công thức chuyển đổi
    int Error_AccTích lũy sai số.
    int This_ErrorSai số hiện hành
    int Last_ErrorSai số lần trước (ngôn ngữ hơi phi kỹ thuật chút)
    int P_GainI_GainD_GainĐộ lợi khâu PID
    int P_TermI_TermD_Termgiá trị khâu PID
    int Outputgiá trị ngõ ra điều khiểntỷ lệ với giá trị set cho PWM.



    Các bước tính

    Cập nhật sai số:
    This_Error Set_Point Speed;

    Tích lũy sai số:
    Error_Acc Error_Acc This_Error

    Lưu sai số:
    Last_Error  This_Error

    Tính khâu tỷ lệ:
    P_Term This_error P_Gain;
    if (
    P_Term>32767P_Term=32767//Correct out of range
    if (P_Term<-32768P_Term=-32768//Correct out of range

    Tính khâu tích phân:
    I_Term Error_Acc I_Gain;
    if (
    I_Term>32767I_Term=32767//Correct out of range
    if (I_Term<-32768I_Term=-32768//Correct out of range

    Tính khâu vi phân:
    D_Term = (This_Error Last_Error) * D_Gain;
    if (
    D_Term>32767D_Term=32767//Correct out of range
    if (D_Term<-32768D_Term=-32768//Correct out of range

    Tính ngõ ra:
    Output P_Term I_Term D_Term;
    if (
    Output >32767Output =32767//Correct out of range
    if (Output 0Output 0//Correct out of range

    Scale Output để hợp với 10bitPWM:
    PWM Output 1023/32767
    Mình đọc đoạn này thì thấy khó hiều quá . ko biết đây là PID theo tốc độ hay theo vị trí . Nếu là PID theo tốc độ thì tại sao lại có đoạn :

    PHP Code:

    PWM 
    Output 1023/32767
    Nếu vậy thì khi sai lệch Output = 0 thì PWM = 0 à . Đoạn này mình không hiểu .

    Hàm truyền PID :

    PHP Code:
    Phương trình hành động là 
            
    Ira Kp Ki ∫ edt Kd de/dt ) + I0
    Với 
    :
        
    Ira     :  tín hiệu ra khỏi bộ điều khiển khi có một sai lệch e thay đổi với thời gian t .
        
    I0     :  tín hiệu ra tại thời điểm thiết lập khi không có sai lệch .
        
    Kp     :  hằng số tỉ lệ 
        Ki     
    hằng số tích phân 
    Kd     
    hằng số đạo hàm  vi phân 
    Vậy đối với PID theo tốc độ và PID theo vị trí thì giá trị I0 khác nhau thế nào .
    Mình đã đọc một số bài lập trình trên mạng và có đọc qua cơ sở lý thuyết điều khiển PID nhưng không hiểu được phải viết hàm PID theo vị trí và PID theo tốc độ thế nào cả .... Các bài ví dụ trên mạng mỗi người làm một kiểu . mình càng đọc càng băn khoăn khó hiểu .
    Nay viết lên đây mong anh chị em cô bác nào hiểu thấu có thể chỉ giáo cho em chút ít để mở rộng tầm hiểu biết của mình được không ? Cảm ơn mọi người trước nhé ....

  • #2
    Và đây cũng là một bài mà em tham khảo được trên mạng nhưng em có chỗ không hiểu . Mong ai biết chỉ dùm . he he .....

    PHP Code:
    void pid()


    // sp : co gia tri duoc truyen tu may tinh xuong chip Driver
    err=sp-pv// (err = setpoint - process variable) pv : là số xung Encoder đo được ;
    eht err elast
    sum_err+=eht// tinh tong sai so theo cong thuc hinh thang 
    d_err=err-elast// vi phan sai so = sai so hien tai - sai so truoc do 
    cv=((kp*err)+(ki*sum_err)+(kd*d_err)); // Cong thuc tinh toan PID 
    cv=cv/1000// Cho nay / 1000 vi tren giao dien ki,kp,kd da * 1000 

    // VD: ki=0.01*1000=10 gui xuong VDK - tranh gui dau "." 

    if(cv>511
    cv=511; }                 // gioi han PWM 9Bit 
    if(cv<0)
    cv=0; }                    // chay maximum 

    pwm1=(int)cv;            // ep kieu sang so nguyen 
    pwm=511-pwm1;         // xuat pwm(OCR1A) 

    //PORTA.4=0; 
    elast=err// sai so hien tai se tro thanh sai so truoc no khi co sai so moi 
    ok=1;      // bit danh dau da xuat dc pwm 


    Có đoạn code mình không hiểu là :
    PHP Code:
    sum_err+=eht;  // tinh tong sai so theo cong thuc hinh thang 
    theo đoạn code trên thì được : sum_err = sum_err + err + elast ;
    tác giả bài lập trình có ghi chú là :// tinh tong sai so theo cong thuc hinh thang cái này có nghĩa là gì ?
    và :
    PHP Code:
    pwm=511-pwm1;   // xuat pwm(OCR1A) 
    tại sao lại như thế này nhỉ . he he . bạn nào từng làm về cái này chỉ dùm với . tớ thấy băn khoăn , khó hiểu quá ....

    Comment


    • #3
      Hi, mình thấy các thuật toán mô phỏng PID của bạn không chuẩn lắm.
      Bạn biết đấy, khối P và khối D là 2 khối tính toán phụ thuộc vào thời gian xử lý dt. Nhưng trong code của bạn không hề đề cập đến vấn đề này.

      Bạn hãy xem thuật toán này chuẩn hơn:

      previous_error = 0
      integral = 0
      start:
      error = setpoint - actual_position
      integral = integral + (error*dt)
      derivative = (error - previous_error)/dt
      output = (Kp*error) + (Ki*integral) + (Kd*derivative)
      previous_error = error
      wait(dt)
      goto start

      Muốn hiểu rõ rơn xin vào link sau:
      http://en.wikipedia.org/wiki/PID_controller
      Chúc vui

      Comment


      • #4
        Sorry mình nhầm, chỉ có khối I và D phụ thuộc thời gian dt

        Comment


        • #5
          Cảm ơn bạn nhiều nhé . Để mình xem xem ....

          Comment


          • #6
            Mình có đoạn code này không hiểu . mong các bạn giải thích giúp mình ?

            PHP Code:
            typedef struct
            {

            double dState// Last position input
            double iState// Integrator state
            double iMaxiMin
            // Maximum and minimum allowable integrator state
            double    iGain// integral gain
                         
            pGain// proportional gain
                         
            dGain// derivative gain
            SPid;

            double UpdatePID(SPid piddouble errordouble position)
            {
            double pTerm,dTermiTerm;  
            pTerm pid->pGain error;   
                                  
            // calculate the proportional term
                                  // calculate the integral state with appropriate limiting   
            pid->iState += error;
                      if        (
            pid->iState pid->iMaxpid->iState pid->iMax;
                      else if (
            pid->iState pid-> iMinpid->iState pid->iMin;

            iTerm pid->iGain iState// calculate the integral term
            dTerm pid->dGain * (position pid->dState);
            pid->dState position;

            return 
            pTerm iTerm dTerm;


            PHP Code:
            if        (pid->iState pid->iMaxpid->iState pid->iMax
            Viết thế này là thế nào ? mình đọc không hiểu .

            Comment


            • #7
              cai phương trình điều khiển pid nguyên thủy phải tính đến thời gian liên tục tuy nhiên để có thể điều khiển bằng cách lập trình theo dạng số dĩ nhiên người ta đã số hóa nó và coi một số đk là gần đúng nên không thấy trong chương trình có cái lượng dt, và cái khoảng thời gian này được hiểu là xủ lý trong chương trình ngắt lấy mẫu chẳng hạn.

              Comment


              • #8
                Nguyên văn bởi embsys Xem bài viết
                PHP Code:
                if        (pid->iState pid->iMaxpid->iState pid->iMax
                Viết thế này là thế nào ? mình đọc không hiểu .
                Chưa đọc kỹ bài của bạn, nhưng lướt qua tôi thấy PID như thế có vấn đề gì đâu, bạn chú ý số công thức tính PID ra , rồi rút gọn các phép tính cho VĐK tối giản các phép tính nhất.
                Còn riêng dòng lệnh trên chẳng qua giới hạn tín hiệu điều khiển của bạn thôi.
                Last edited by LamHong; 13-08-2009, 14:28.

                Comment


                • #9
                  Bạn thông cảm nhé . Nói thật là đoạn code đó mình không hiểu lắm . vì mình lập trình cũng tương đối mà chưa nhìn thấy kiểu viết như thế . Bạn hiểu ý mình không nhỉ .
                  PHP Code:
                  if        (pid->iState pid->iMaxpid->iState pid->iMax
                  Mấy cái dấu " -> " có nghĩa là gì . có thể viết kiểu nào cho dễ hiểu hơn được không ?

                  Comment


                  • #10
                    Toán tử -> dùng để truy xuất thành viên dạng con trỏ (pointer)trong kiểu dữ liệu có cấu trúc như struct, class.
                    Vi du
                    typedef struct _Vivi
                    {
                    int thanhvien_a;
                    nit thanhvien_b;
                    } VIVI,*LPVIVI;
                    //khai báo
                    VIVI a,*pa;
                    //Khởi tạo thành viên
                    a.thanhvien_a=1;
                    a.thanhvien_b=2;
                    //Như vậy việc truy xuất gián tiếp đối tượng a như sau
                    pa=&a;//Con trỏ pa trỏ tới đối tượng a
                    //Truy cập thành viên của đối tượng a
                    int GiaTriThanhVien_a=pa->thanhvien_a;
                    int GiaTriThanhVien_b=pa->thanhvien_b;
                    //Như vậy
                    //GiaTriThanhVien_a=1;
                    //GiaTriThanhVien_b=2;

                    Comment


                    • #11
                      pid là một con trỏ trỏ tới đối tượng SPid nên phải truy xuất bằng toán tử ->
                      Có cách viết dễ hiểu hơn là dùng MACRO nhưng nó làm mất đi tính trực quan của C/C++
                      Chúc vui

                      Comment


                      • #12
                        Cảm ơn bạn Viet Vinh nhiều lắm ! Mình hiểu rồi ! Đây là lần đâu tiên mình gặp ký tự là này nên rất khó hiểu . Rất vui vì được bạn giúp đỡ ... Cảm ơn nhiều nha

                        Comment


                        • #13
                          viet j ma kho hieu the.dieu khien dong co dcservo bang pid ne .nhap toc do can dieu khien bang ban phim luon

                          /************************************************** ***
                          This program was produced by the
                          CodeWizardAVR V2.03.4 Standard
                          Automatic Program Generator
                          © Copyright 1998-2008 Pavel Haiduc, HP InfoTech s.r.l.
                          http://www.hpinfotech.com

                          Project :
                          Version :
                          Date : 5/13/2011
                          Author :
                          Company :
                          Comments:


                          Chip type : ATmega32
                          Program type : Application
                          Clock frequency : 8.000000 MHz
                          Memory model : Small
                          External RAM size : 0
                          Data Stack size : 512
                          ************************************************** ***/

                          #include <mega32.h>
                          #include <math.h>
                          #include <delay.h>
                          #include <stdio.h>
                          #include <stdlib.h>
                          #define KEYPAD_PORT PORTA
                          #define KEYPAD_PIN PINA
                          // Alphanumeric LCD Module functions
                          #asm
                          .equ __lcd_port=0x15 ;PORTC
                          #endasm
                          #include <lcd.h>
                          #define MOTOR_DDR DDRD
                          #define MOTOR_PORT PORTD
                          #define MOTOR_DIR 6
                          #define MOTOR_EN 7
                          #define sampling_time 25
                          #define inv_Sampling_time 40
                          #define PWM_Period 8000
                          void motorspeed_pid(long int des_speed);
                          volatile long int pulse,prepulse;

                          volatile long int rspeed,err,pre_err,kp=8,kd=10,ki=1;

                          volatile long int ppart=0 ;

                          volatile long int dpart=0;

                          volatile long int ipart=0;

                          volatile long int ctrl_speed=0;;

                          volatile long int output,pulse;
                          volatile unsigned char sample_count=0;
                          int so(char a) ;
                          volatile long int n,t,c,dv,v1,v2;
                          int nhap();
                          int l=0,j,A1[2],A2[2],chophep=0,k,m,v=0,x,y;
                          char checkpad();
                          int scan_code[4]={0x0E,0x0D,0x0B,0x07};
                          int ASCII_code[4][4]=
                          {'7','8','9','/',
                          '4','5','6','*',
                          '1','2','3','-',
                          'A','0','=','+'
                          };
                          // External Interrupt 2 service routine
                          interrupt [EXT_INT2] void ext_int2_isr(void)
                          {
                          if(PINB.0==1)
                          {
                          pulse=pulse+1;
                          }
                          else
                          {
                          pulse=pulse-1;
                          }


                          }

                          // Timer 2 output compare interrupt service routine
                          interrupt [TIM2_COMP] void timer2_comp_isr(void)
                          {

                          TCNT2=0X3C;
                          sample_count++;
                          motorspeed_pid(ctrl_speed);
                          }

                          // Declare your global variables here
                          char checkpad()
                          {
                          int i,j,keyin;
                          for(i=0;i<4;i++){
                          KEYPAD_PORT=0xFF-(1<<(i+4));
                          delay_ms(1);
                          keyin=KEYPAD_PIN & 0x0F;
                          if(keyin!=0x0F){
                          for(j=0;j<4;j++)
                          if(keyin==scan_code[j])
                          return ASCII_code[j][i];
                          }//else return 0;
                          }
                          }
                          int nhap()
                          {

                          if (checkpad()=='A')
                          l=1;
                          if ((j==0)&&(checkpad()!=0)&&(l==1)&&(checkpad()!='A' )&&checkpad()!='/') ///////////////checkpad()==0co nghia la khong nhan keypad
                          {
                          A1[0]=checkpad() ;
                          lcd_gotoxy(6,1);
                          lcd_putchar(A1[0]);
                          j=1;
                          chophep=1;

                          }
                          if ((checkpad()==0)&&(j==1)) /////////////
                          {
                          j=2;
                          }
                          if ((j==2)&&(checkpad()=='/') )
                          {
                          A1[0]=' ';
                          lcd_gotoxy(6,1);
                          lcd_putchar(A1[0]);
                          j=0;
                          //chophep=1;
                          }

                          if ((j==2)&&(checkpad()!=0)&&(l==1)&&(checkpad()!='A' )&&checkpad()!='/') //////////////
                          {
                          A1[1]=checkpad();
                          lcd_gotoxy(7,1);
                          lcd_putchar(A1[1]);
                          j=3;
                          chophep=1;
                          }
                          if ((checkpad()==0)&&(j==3))
                          {
                          j=4;
                          }
                          if ((j==4)&&(checkpad()=='/'))
                          {
                          A1[1]=' ';
                          lcd_gotoxy(7,1);
                          lcd_putchar(A1[1]);
                          j=5;
                          }
                          if ((checkpad()==0)&&(j==5))
                          {

                          j=2;


                          }
                          if ((j==4)&&(checkpad()!=0)&&(l==1)&&(checkpad()!='A' )&&checkpad()!='/')
                          {
                          A2[0]=checkpad();
                          lcd_gotoxy(8,1);
                          lcd_putchar(A2[0]);
                          j=6;
                          chophep=1;

                          }
                          if ((checkpad()==0)&&(j==6))
                          {
                          j=7;
                          }
                          if ((j==7)&&(checkpad()=='/') )
                          {
                          A2[0]=' ';
                          lcd_gotoxy(8,1);
                          lcd_putchar(A2[0]);
                          j=8;

                          }
                          if ((checkpad()==0)&&(j==8))
                          {
                          j=4;
                          }
                          if ((j==7)&&(checkpad()!=0)&&(l==1)&&(checkpad()!='A' )&&checkpad()!='/')
                          {
                          A2[1]=checkpad();
                          lcd_gotoxy(9,1);
                          lcd_putchar(A2[1]);
                          j=9;
                          // chophep=1;
                          }
                          if ((checkpad()==0)&&(j==9))
                          {
                          j=10;
                          }
                          if ((j==10)&&(checkpad()=='/') )
                          {
                          A2[1]=' ';
                          lcd_gotoxy(9,1);
                          lcd_putchar(A2[1]);
                          j=11;
                          }
                          if ((checkpad()==0)&&(j==11))
                          {
                          j=7;
                          chophep=1;
                          }

                          }
                          void motorspeed_pid(long int des_speed)
                          {
                          rspeed=pulse-prepulse ;
                          prepulse=pulse;

                          if(rspeed<0)
                          {
                          rspeed=rspeed*(-1);
                          }

                          err=des_speed-rspeed;
                          ppart=kp*err;
                          dpart=kd*(err-pre_err)*inv_Sampling_time ;
                          ipart+=ki*sampling_time*(err+pre_err)/1000;
                          output+=ppart+dpart+ipart;
                          //////////////////////////////////////////

                          if(output>=PWM_Period)
                          {
                          output=PWM_Period-1;
                          if(output<0)
                          {
                          output=1;
                          }
                          }


                          OCR1A=output;
                          pre_err=err;
                          }

                          void main(void)
                          { pulse=0;
                          // Declare your local variables here

                          // Input/Output Ports initialization
                          // Port A initialization
                          // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
                          // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
                          PORTA=0x00;
                          DDRA=0xF0;

                          // Port B initialization
                          // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
                          // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
                          PORTB=0x00;
                          DDRB=0x00;

                          // Port C initialization
                          // Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
                          // State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
                          PORTC=0x00;
                          DDRC=0xFF;

                          // Port D initialization
                          // Func7=In Func6=In Func5=Out Func4=In Func3=In Func2=In Func1=In Func0=In
                          // State7=T State6=T State5=0 State4=T State3=T State2=T State1=T State0=T
                          PORTD=0x00;
                          DDRD=0x20;

                          // Timer/Counter 0 initialization
                          // Clock source: System Clock
                          // Clock value: Timer 0 Stopped
                          // Mode: Normal top=FFh
                          // OC0 output: Disconnected
                          TCCR0=0x00;
                          TCNT0=0x00;
                          OCR0=0x00;

                          // Timer/Counter 1 initialization
                          // Clock source: System Clock
                          // Clock value: 1000.000 kHz
                          // Mode: Fast PWM top=ICR1
                          // OC1A output: Inverted
                          // OC1B output: Discon.
                          // Noise Canceler: Off
                          // Input Capture on Falling Edge
                          // Timer 1 Overflow Interrupt: Off
                          // Input Capture Interrupt: Off
                          // Compare A Match Interrupt: Off
                          // Compare B Match Interrupt: Off
                          TCCR1A=0xC2;
                          TCCR1B=0x1A;
                          TCNT1H=0x00;
                          TCNT1L=0x00;
                          ICR1H=0x1F;
                          ICR1L=0x40;
                          OCR1AH=0x00;
                          OCR1AL=0x00;
                          OCR1BH=0x00;
                          OCR1BL=0x00;

                          // Timer/Counter 2 initialization
                          // Clock source: System Clock
                          // Clock value: 1000.000 kHz
                          // Mode: Normal top=FFh
                          // OC2 output: Disconnected
                          ASSR=0x00;
                          TCCR2=0x07;
                          TCNT2=0x00;
                          OCR2=0x00;

                          // External Interrupt(s) initialization
                          // INT0: Off
                          // INT1: Off
                          // INT2: On
                          // INT2 Mode: Falling Edge
                          GICR|=0x20;
                          MCUCR=0x00;
                          MCUCSR=0x00;
                          GIFR=0x20;

                          // Timer(s)/Counter(s) Interrupt(s) initialization
                          TIMSK=0x80;

                          // Analog Comparator initialization
                          // Analog Comparator: Off
                          // Analog Comparator Input Capture by Timer/Counter 1: Off
                          ACSR=0x80;
                          SFIOR=0x00;
                          OCR1A=1;
                          // LCD module initialization
                          lcd_init(16);

                          // Global enable interrupts
                          #asm("sei")

                          while (1)
                          {
                          PORTD.6=1;
                          PORTD.7=1;
                          nhap();
                          if((chophep==1)&&(checkpad()=='A'))
                          {
                          k=so(A1[0]);
                          m=so(A1[1]);
                          x=so(A2[0]);
                          y=so(A2[1]);
                          v=k*10+m;
                          //chophep=0;
                          }
                          if(sample_count>10)
                          {
                          ctrl_speed=v;
                          n= rspeed/1000;
                          t=(rspeed-n*1000)/100;
                          c=(rspeed-t*100)/10;
                          dv=(rspeed-n*1000-t*100-c*10);
                          lcd_gotoxy(0,0);
                          lcd_putchar(n+48);
                          lcd_gotoxy(1,0);
                          lcd_putchar(t+48);
                          lcd_gotoxy(2,0);
                          lcd_putchar(c+48);
                          lcd_gotoxy(3,0);
                          lcd_putchar(dv+48);
                          sample_count=0;
                          }

                          };
                          }
                          int so(char a)
                          {
                          switch (a)
                          {
                          case '0' :
                          return 0;
                          break;
                          case '1' :
                          return 1;
                          break;
                          case '2' :
                          return 2;
                          break;
                          case '3' :
                          return 3;
                          break;
                          case '4' :
                          return 4;
                          break;
                          case '5' :
                          return 5;
                          break;
                          case '6' :
                          return 6;
                          break;
                          case '7' :
                          return 7;
                          break;
                          case '8' :
                          return 8;
                          break;
                          case '9' :
                          return 9;
                          break;

                          }
                          }

                          Comment


                          • #14
                            duty = duty + ..... là vì cái này phải cộng dồn. vì nếu khi động có đã ổn định tốc độ rồi thì giá trị duty lúc đó sẽ = 0, nếu vậy động cơ sẽ dừng. nên phải cộng dồn cả duty trước đó

                            Comment

                            Về tác giả

                            Collapse

                            embsys Tìm hiểu thêm về embsys

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

                            Collapse

                            Đang tải...
                            X