<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > 增量式PID的stm32實(shí)現(xiàn),整定過程

          增量式PID的stm32實(shí)現(xiàn),整定過程

          作者: 時間:2016-11-28 來源:網(wǎng)絡(luò) 收藏
        1. void TIM5_PWMINPUT_INIT(u16 arr,u16 psc)
        2. {
        3. TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;//TIM的初始化結(jié)構(gòu)體
        4. NVIC_InitTypeDef NVIC_InitStructure;//中斷配置
        5. TIM_ICInitTypeDefTIM5_ICInitStructure;//TIM4PWM配置結(jié)構(gòu)體
        6. GPIO_InitTypeDef GPIO_InitStructure;//IO口配置結(jié)構(gòu)體
        7. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);//Open TIM4 clock
        8. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//open gpioB clock
        9. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//GPIO 1
        10. GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IPU;//浮空輸入 上拉輸入
        11. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        12. GPIO_Init(GPIOA, &GPIO_InitStructure);
        13. TIM_TimeBaseStructure.TIM_Period = arr; //設(shè)置在下一個更新事件裝入活動的自動重裝載寄存器周期的值
        14. TIM_TimeBaseStructure.TIM_Prescaler =psc; //設(shè)置用來作為TIMx時鐘頻率除數(shù)的預(yù)分頻值
        15. TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設(shè)置時鐘分割:TDTS = Tck_tim
        16. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//TIM向上計數(shù)模式
        17. TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根據(jù)TIM_TimeBaseInitStruct中指定的參數(shù)初始化TIMx的時間基數(shù)單位
        18. NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
        19. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
        20. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
        21. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        22. NVIC_Init(&NVIC_InitStructure);
        23. TIM5_ICInitStructure.TIM_Channel = TIM_Channel_2;
        24. TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
        25. TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
        26. TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
        27. TIM5_ICInitStructure.TIM_ICFilter = 0x3;//Filter:過濾
        28. TIM_PWMIConfig(TIM5, &TIM5_ICInitStructure);//PWM輸入配置
        29. TIM_SelectInputTrigger(TIM5, TIM_TS_TI2FP2);//選擇有效輸入端
        30. TIM_SelectSlaveMode(TIM5, TIM_SlaveMode_Reset);//配置為主從復(fù)位模式
        31. TIM_SelectMasterSlaveMode(TIM5, TIM_MasterSlaveMode_Enable);//啟動定時器的被動觸發(fā)
        32. TIM_ITConfig(TIM5, TIM_IT_CC2|TIM_IT_Update, ENABLE);//中斷配置
        33. TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中斷標(biāo)志位
        34. TIM_Cmd(TIM5, ENABLE);
        35. }
        36. void TIM5_IRQHandler(void)
        37. {
        38. {
        39. if (TIM_GetITStatus(TIM5, TIM_IT_CC2) != RESET)//捕獲1發(fā)生捕獲事件
        40. {
        41. duty_TIM5=TIM_GetCapture1(TIM5); //采集占空比
        42. if(TIM_GetCapture2(TIM5)>600)period_TIM5=TIM_GetCapture2(TIM5);
        43. CollectFlag_TIM5 = 0;
        44. }
        45. }
        46. TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中斷標(biāo)志位
        47. }
        48. 復(fù)制代碼


          PID部分:
          準(zhǔn)備部分:先定義PID結(jié)構(gòu)體:
          1. typedef struct
          2. {
          3. int setpoint;//設(shè)定目標(biāo)
          4. int sum_error;//誤差累計
          5. float proportion ;//比例常數(shù)
          6. float integral ;//積分常數(shù)
          7. float derivative;//微分常數(shù)
          8. int last_error;//e[-1]
          9. int prev_error;//e[-2]
          10. }PIDtypedef;
          復(fù)制代碼

          這里注意一下成員的數(shù)據(jù)類型,依據(jù)實(shí)際需要來定的。
          在文件中定義幾個關(guān)鍵變量:
          1. floatKp =0.32; //比例常數(shù)
          2. floatTi =0.09 ; //積分時間常數(shù)
          3. float Td =0.0028 ;//微分時間常數(shù)
          4. #define T0.02 //采樣周期
          5. #define KiKp*(T/Ti)// Kp Ki Kd 三個主要參數(shù)
          6. #define KdKp*(Td/T)
          復(fù)制代碼

          C語言好像用#define 什么什么對程序不太好,各位幫忙寫個優(yōu)化辦法看看呢? 用const?

          PID.H里面主要的幾個函數(shù):
          1. void PIDperiodinit(u16 arr,u16 psc);//PID 采樣定時器設(shè)定
          2. void incPIDinit(void);//初始化,參數(shù)清零清零
          3. int incPIDcalc(PIDtypedef*PIDx,u16 nextpoint);//PID計算
          4. void PID_setpoint(PIDtypedef*PIDx,u16 setvalue);//設(shè)定 PID預(yù)期值
          5. void PID_set(float pp,float ii,float dd);//設(shè)定PIDkp ki kd三個參數(shù)
          6. void set_speed(float W1,float W2,float W3,float W4);//設(shè)定四個電機(jī)的目標(biāo)轉(zhuǎn)速
          復(fù)制代碼


          PID處理過程:
          岔開一下:這里我控制的是電機(jī)的轉(zhuǎn)速w,實(shí)際上電機(jī)的反饋波形的頻率f、電機(jī)轉(zhuǎn)速w、控制信號PWM的占空比a三者是大致線性的正比的關(guān)系,這里強(qiáng)調(diào)這個的目的是
          因為樓主在前期一直搞不懂我控制的轉(zhuǎn)速怎么和TIM4輸出的PWM的占空比聯(lián)系起來,后來想清楚里面的聯(lián)系之后通過公式把各個系數(shù)算出來了。

          正題:控制流程是這樣的,首先我設(shè)定我需要的車速(對應(yīng)四個輪子的轉(zhuǎn)速),然后PID就是開始響應(yīng)了,它先采樣電機(jī)轉(zhuǎn)速,得到偏差值E,帶入PID計算公式,得到調(diào)整量也就是最終更改了PWM的占空比,不斷調(diào)節(jié),直到轉(zhuǎn)速在穩(wěn)態(tài)的一個小范圍上下浮動。
          上面講到的“得到調(diào)整量”就是增量PID的公式:
          1. int incPIDcalc(PIDtypedef *PIDx,u16 nextpoint)
          2. {
          3. int iError,iincpid;
          4. iError=PIDx->setpoint-nextpoint;//當(dāng)前誤差
          5. iincpid=//增量計算
          6. PIDx->proportion*(iError-PIDx->last_error)
          7. +PIDx->integral*iError
          8. +PIDx->derivative*(iError-2*PIDx->last_error+PIDx->prev_error);
          9. PIDx->prev_error=PIDx->last_error; //存儲誤差,便于下次計算
          10. PIDx->last_error=iError;
          11. return(iincpid) ;
          12. }
          復(fù)制代碼

          注釋掉的是第一種寫法,沒注釋的是第二種以Kp KI kd為系數(shù)的寫法,實(shí)際結(jié)果是一樣的。
          處理過程放在了TIM6,溢出周期時間就是是PID里面采樣周期(區(qū)分于反饋信號的采樣,反饋信號采樣是1M的頻率)
          相關(guān)代碼:
          1. void TIM6_IRQHandler(void)//采樣時間到,中斷處理函數(shù)
          2. {
          3. if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)//更新中斷
          4. {
          5. frequency1=1000000/period_TIM4; //通過捕獲的波形的周期算出頻率
          6. frequency2=1000000/period_TIM1;
          7. frequency3=1000000/period_TIM2;
          8. frequency4=1000000/period_TIM5;
          9. PID1.sum_error+=(incPIDcalc(&PID1,frequency1));//計算增量并累加
          10. pwm1=PID1.sum_error*4.6875 ; //pwm1 代表將要輸出PWM的占空比
          11. frequency1=0; //清零
          12. period_TIM4=0;
          13. PID2.sum_error+=(incPIDcalc(&PID2,frequency2));//計算增量并累加 Y=Y+Y
          14. pwm2=PID2.sum_error*4.6875 ; //將要輸出PWM的占空比
          15. frequency2=0;
          16. period_TIM1=0;
          17. PID3.sum_error+=(incPIDcalc(&PID3,frequency3));//常規(guī)PID控制
          18. pwm3=PID3.sum_error*4.6875 ; //將要輸出PWM的占空比
          19. frequency3=0;
          20. period_TIM2=0;
          21. PID4.sum_error+=(incPIDcalc(&PID4,frequency4));//計算增量并累加
          22. pwm4=PID4.sum_error*4.6875 ; //將要輸出PWM的占空比
          23. frequency4=0;
          24. period_TIM5=0;
          25. }
          26. TIM_SetCompare(pwm1,pwm2,pwm3,pwm4);//重新設(shè)定PWM值
          27. TIM_ClearITPendingBit(TIM6, TIM_IT_Update); //清除中斷標(biāo)志位
          28. }
          復(fù)制代碼


          上面幾個代碼是PID實(shí)現(xiàn)的關(guān)鍵部分

          整定過程:
          辦法有不少,這里用的是先KP,再TI,再TD,在微調(diào)。其他的辦法特別是有個尼古拉斯法我發(fā)現(xiàn)不適合我這個控制對象。
          先Kp,就是消除積分和微分部分的影響,這里我糾結(jié)過到底是讓Ti 等于一個很大的值讓Ki=Kp*(T/Ti)里面的KI接近零,還是直接定義KI=0,TI=0.
          然后發(fā)現(xiàn)前者沒法找到KP使系統(tǒng)震蕩的臨界值,第二個辦法可以得到預(yù)期的效果:即KP大了會產(chǎn)生震蕩,小了會讓系統(tǒng)穩(wěn)定下來,當(dāng)然這個時候是有穩(wěn)態(tài)誤差的。
          隨后把積分部分加進(jìn)去,KI=Kp*(T/Ti)這個公式用起來,并且不斷調(diào)節(jié)TI 。TI太大系統(tǒng)穩(wěn)定時間比較長。
          然后加上Kd=Kp*(Td/T),對于系統(tǒng)響應(yīng)比較滯后的情況效果好像好一些,我這里的電機(jī)反映挺快的,所以Td值很小。
          最后就是幾個參數(shù)調(diào)節(jié)一下,讓波形好看一點(diǎn)。這里的波形實(shí)際反映的是采集回來的轉(zhuǎn)速值,用STM32的DAC功能輸出和轉(zhuǎn)速對應(yīng)的電壓,用示波器采集的。
          上一頁 1 2 下一頁

          關(guān)鍵詞: 增量式PIDstm32整定過

          評論


          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();