久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 2936|回復: 0
打印 上一主題 下一主題
收起左側

零基礎制作平衡小車【連載】9---增量式PID實戰(附源碼)

[復制鏈接]
跳轉到指定樓層
樓主
ID:223481 發表于 2020-12-2 17:19 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式

粘貼過來的代碼都亂了,貼個原文鏈接吧:

https://blog.csdn.net/D_SEngineer?spm=1011.2124.3001.5113

不定時發送福利,歡迎小伙伴們交流學習。


終于到實戰階段了,前面學習的理論部分著實令人乏味呀。今天來看看增量式PID怎么在STM32中實現控制電機恒速運行的。

硬件連接

我用的戰艦開發板和配套的屏幕,電機是帶編碼器的電機,第一章有關于硬件的介紹,這里就不多少了。
1.使用通用定時器3 通道1 PA6引腳輸出PWM
2.編碼器輸入捕獲 用的是 通用定時器2 PA0 PA1引腳
3.電機引腳如下圖

電機電源正負極接到TB6612驅動的AO1和AO2(或者BO1和BO2,看你驅動器左側接的是AIN1、AIN2還是BIN1、BIN2了),編碼器電源接5V電源,黃綠信號線接單片機PA0和PA1,如果接反系統會出現異常,可以仿真看下接收到的脈沖值是不是很大(本例中的count),很大說明接反了。將線反接即可。另外就是驅動的PWMA(或者PWMB)接到單片機的PA6引腳。驅動器的STBY接到5V電源上,BIN1和BIN2一個接高一個接低即可。
接線很簡單,不會的直接問淘寶賣家。其實看了代碼只要知道PWM輸出引腳和編碼器輸入引腳就可以接線了,其他都是固定接線方式。下圖是我的硬件圖,很亂,小車還沒組裝好,都是散件,湊合看吧。

代碼實現的功能
  • 按鍵設定目標轉速
  • stm32通過pwm控制電機維持目標轉速運轉
  • 并且具備顯示功能,把重要參數顯示在屏幕上
代碼思路
  • 要先把PWM調通,實現PWM控制電機旋轉
  • 在實現PWM控制電機旋轉之后,加上按鍵調節占空比
  • 將編碼器檢測加進去,能實現檢測到電機反饋的脈沖數
  • 將編碼器檢測的脈沖數顯示出來,或者串口打印出來,總之要實時知道這個值,方便后面調試PID參數
  • 之后再將PID加進去
  • 整理代碼
部分重要源碼講解
  • PWM
    PWM的生成之前章節已經講解過了,在這里就不復述了,直接上代碼。在這里需要注意一點就是之前PWM設置的是1Khz,這里我們設置成20Khz。大家可以看看注釋,理解一下怎么設置成不同的頻率。
/*使用通用定時器3  通道1  PA6引腳輸出PWM------------ PWM信號 周期和占空比的計算 --------------- ARR :自動重裝載寄存器的值 CLK_cnt:計數器的時鐘,等于 Fck_int / (psc+1) = 72M/(psc+1) PWM 信號的周期 T = ARR * (1/CLK_cnt) = ARR*(PSC+1) / 72M 占空比P=CCR/(ARR+1) 時鐘分頻71,系統總線時鐘72M,可以算出定時器時鐘為72/(3+1)=18M在這里我們需要設定PWM頻率為20Khz=0.05ms,因此自動重裝載的值ARR=900.因為定時器時鐘為18M=0.0556us,因此只需要累加900次,就等于0.05ms=20Khz了。因此我們配置如下TIM_TimeBaseInitTypeStructure.TIM_Prescaler = 3;                   //定時器時鐘分頻TIM_TimeBaseInitTypeStructure.TIM_Period = (900 - 1);                //自動重裝載的值 */void PWM_Init(void){    //基本定時器初始化部分        GPIO_InitTypeDef GPIO_InitStructure;    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;    TIM_OCInitTypeDef TIM_OCInitStructure;    //初始化定時器    TIM_DeInit(TIM3);        //GPIO初始化部分    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE);    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    GPIO_Init(GPIOA,&GPIO_InitStructure);        //使能定時器時鐘    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);        //死區時間時鐘源,該例程只進行PWM輸出,沒有用到死區,因此不設定    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;    //向上計數模式    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;    //自動重裝載的值設定為1000    TIM_TimeBaseInitStructure.TIM_Period = (900 - 1);    //定時器周期分頻數    TIM_TimeBaseInitStructure.TIM_Prescaler = 3;    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;        //初始化定時器3    TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);            //PWM初始化部分    //pwm1模式    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;    //初始化電平    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;    //使能輸出比較    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;    //初始化PWM占空比為0    TIM_OCInitStructure.TIM_Pulse = 0;    //初始化pwm    TIM_OC1Init(TIM3,&TIM_OCInitStructure);        //使能TIM3在CCR2上的預裝載寄存器    TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);     //使能定時器3    TIM_Cmd(TIM3,ENABLE);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 編碼器輸入
    不懂的可以仔細看看代碼注釋,注釋也看不懂的,可以翻翻我之前的帖子,代碼如下:
//輸入捕獲  通用定時器2  PA0  PA1void Advance_TIM_Init(void){    //GPIO初始化        GPIO_InitTypeDef GPIO_InitStructure;    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;    TIM_ICInitTypeDef  TIM_ICInitStructure;    NVIC_InitTypeDef NVIC_InitStructure;                             // 設置中斷來源    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;                         // 設置搶占優先級                      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;                   // 設置子優先級    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;            NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;    NVIC_Init(&NVIC_InitStructure);        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        GPIO_Init(GPIOA,&GPIO_InitStructure);            //基本定時器初始化部分    //使能定時器時鐘    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);//設置缺省值    // 自動重裝載寄存器的值,累計TIM_Period+1個頻率后產生一個更新或者中斷        TIM_TimeBaseStructure.TIM_Period = 60000;                // 驅動CNT計數器的時鐘 = Fck_int/(psc+1)        TIM_TimeBaseStructure.TIM_Prescaler = 0;                // 時鐘分頻因子 ,配置死區時間時需要用到        TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;                        // 計數器計數模式,設置為向上計數        TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;                        // 重復計數器的值,沒用到不用管        TIM_TimeBaseStructure.TIM_RepetitionCounter=0;                // 初始化定時器        TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);        //編碼器模式    TIM_EncoderInterfaceConfig(TIM2,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);            TIM_ICStructInit(&TIM_ICInitStructure);    TIM_ICInitStructure.TIM_ICFilter = 10;                //10經驗值,不必糾結,起濾波作用    TIM_ICInit(TIM2, &TIM_ICInitStructure);            //使能中斷    TIM_ITConfig(TIM2,TIM_IT_Update, ENABLE);                // 清除中斷標志位        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);        // 使能高級控制定時器,計數器開始計數    TIM_Cmd(TIM2, ENABLE);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 按鍵和屏幕顯示
    按鍵很簡單,我注釋的也很詳細,這是放在主函數中循環檢測的,這個也不用多說了吧,直接代碼:
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) == 0)  {     SetPoint=160;           //設定目標     z=1; } if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2) == 0) {     SetPoint=120;           //設定目標     z=2; }  if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) == 0) {     SetPoint=20;           //設定目標     z=3; }  if(z != j) {     LCD_Clear(WHITE);       //delay_ms(120); } j = z ;  LCD_Fill(0,SetPoint,lcddev.width,SetPoint+2,BLUE);        //目標值設定,一條直線          //如果FLAG=1時,說明200ms到了,將采樣的count值打印在屏幕上,         //k是x軸坐標,200MS+1,conunt是y軸坐標,代表當前時刻的采樣的脈沖值 if(flag == 1)    {     k++;     LCD_Fill(k,count,k+2,count+2,BLUE);     flag = 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 增量式PID
    重點來了,學了這么多理論,你會發現,其實代碼就一點點,兩三行就完事了。先看代碼:
/********************增量式PID控制設計************************************/int IncPIDCalc(int PresentPoint) {    int iError , iIncpid;       //iIncpid增量值   iError當前誤差    iError = SetPoint-PresentPoint;                    //增量計算    iIncpid =(Proportion * iError)                  //E[k]項              -(Integral * LastError)     //E[k-1]項              +(Derivative * PrevError);  //E[k-2]項    PrevError = LastError;    LastError=iError;    return (iIncpid);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

是不是很簡單,就這么幾行代碼,但是我卻寫了好幾篇帖子來學習它的理論部分。過來之后就能看清廬山真面目了。

根據前面推導的公式,這里只是將這些公式用C寫出來。首先定義一個當前誤差iError和一個計算出來的增量值iIncpid。當前誤差iError=目標值SetPoint-傳感器采集的當前值PresentPoint,之后進行增量式PID運算,得出增量值iIncpid。

將式子和程序對比下,是不是一樣的?再看看還是不一樣。上面式子中沒有減號,代碼中卻有;上面式子中明明微分項是𝐄𝐤−𝟐𝐄(𝐤−𝟏)+𝐄(𝐤−𝟐),即便將Kp*Td/T看成Kd也還是和程序不一樣呀。程序中確直接乘上一個上上次誤差PrevError,你說一樣嗎?你心中有沒有這樣的疑問?有的話也不奇怪,我朋友看了也有(斜眼笑)。

解惑

看完上面的推導,應該能解決你心中的疑惑了吧。

源碼

昨天開通了一個公眾號,以后代碼就放到公眾號了,后續我會將之前的代碼也都放到公眾號上,歡迎小伙伴們交流學習哈。關注公眾號回復
“增量式PID”
獲取源碼。

調試過程

本來想寫在這個帖子上的,不過一看時間有點晚了,明天再開一貼吧,明天還有工作呢,不熬夜,早睡早起。晚安,小伙伴們。


分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏2 分享淘帖 頂 踩
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 成人精品视频在线观看 | 涩涩视频网站在线观看 | 国产乱性 | 999久久久| 天堂av中文 | 日韩欧美久久精品 | 中文字幕视频在线看 | 免费网站国产 | 欧美一区二区 | 犬夜叉在线观看 | 国产精品久久久精品 | 91一区二区 | 男人天堂视频在线观看 | 国产99小视频 | 亚洲综合在线一区 | 欧美精品一二区 | 日韩成人在线看 | 国产精品a久久久久 | 亚洲欧美日韩高清 | 久久久久国产精品午夜一区 | 欧美在线视频网站 | 欧美激情一区二区三级高清视频 | se婷婷| 欧美成人自拍 | 亚洲精品国产区 | 午夜影院在线视频 | 手机在线观看av | 久久久久久久电影 | 91精品国产91久久综合桃花 | 色www精品视频在线观看 | 久久久久久久网 | 久久骚 | 一区二区电影网 | 亚洲人人 | 成人黄色电影在线观看 | 亚洲天堂一区二区 | 日日摸夜夜添夜夜添精品视频 | 欧美亚洲国产一区二区三区 | 国产一区久久久 | 免费成人在线网站 | 99亚洲精品 |