你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

(转载)增量式PID的stm32实现,整定过程

[复制链接]
nyszx 发布时间:2017-12-26 15:40
本帖最后由 nyszx 于 2017-12-26 19:31 编辑

原帖:【分享】增量式PID的stm32实现,整定过程
原文网址:http://www.amobbs.com/thread-5575823-1-1.html
出处:阿莫电子论坛
作者:tim4146
感谢大家最近的帮忙,让我顺利做完增量PID功能,虽然PID不是什么牛逼的东西,但是真心希望以后刚刚接触这块的人能尽快进入状态。
也下面我分享一下近期的这些工作吧。欢迎大家批评指点~

首先说说增量式PID的公式,这个关系到MCU算法公式的书写,实际上两个公式的写法是同一个公式变换来得,不同的是系数的差异。
资料上比较多的是:
1.jpg
还有一种是:
2.jpg
感觉第二种的Kp Ki Kd比较清楚,更好理解,下面介绍的就以第二种来吧。(比例、积分、微分三个环节的作用这里就详细展开,百度会有很多)

硬件部分:
控制系统的控制对象是4个空心杯直流电机,电机带光电编码器,可以反馈转速大小的波形。电机驱动模块是普通的L298N模块。
芯片型号,STM32F103ZET6

软件部分:
PWM输出:TIM3,可以直接输出4路不通占空比的PWM波
PWM捕获:STM32除了TIM6 TIM7其余的都有捕获功能,使用TIM1 TIM2 TIM4 TIM5四个定时器捕获四个反馈信号
PID的采样和处理:使用了基本定时器TIM6,溢出时间就是我的采样周期,理论上T越小效果会越好,这里我取20ms,依据控制对象吧,如果控制水温什么的采样周期会是几秒几分钟什么的。

上面的PWM输出和捕获关于定时器的设置都有例程,我这里是这样的:
TIM3输出四路PWM,在引脚 C 的 GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9输出
四路捕获分别是TIM4  TIM1  TIM2  TIM5   ,对应引脚是:  PB7 PE11 PB3 PA1

高级定时器tim1的初始化略不同,它的中断”名称“和通用定时器不同,见代码:
  1. /*功能名称IM3_PWM_Init(u16 arr,u16 psc)
  2.         描述      TIM3产生四路PWM
  3. */
  4. void TIM3_PWM_Init(u16 arr,u16 psc)
  5. {
  6.         GPIO_InitTypeDef GPIO_InitStructure;
  7.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  8.         TIM_OCInitTypeDef  TIM_OCInitStructure;


  9.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
  10.          RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟使能

  11.   GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE); //Timer3全映射 GPIOC-> 6,7,8,9                                                                             //用于TIM3的CH2输出的PWM通过该LED显示

  12.    //设置该引脚为复用输出功能,输出TIM3 CH1 CH2 CH3 CH4 的PWM脉冲波形
  13.         GPIO_InitStructure.GPIO_Pin =GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; //初始化GPIO
  14.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
  15.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  16.         GPIO_Init(GPIOC, &GPIO_InitStructure);
  17.         GPIO_ResetBits(GPIOC,GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9);//默认电机使能端状态:不使能

  18.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
  19.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  这里是72分频,那么时钟频率就是1M
  20.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  21.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  22.         TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位


  23.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
  24.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
  25.         TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
  26.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高

  27.         TIM_OC1Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
  28.         TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR1上的预装载寄存器


  29.         TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
  30.         TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器

  31.         TIM_OC3Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
  32.         TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR3上的预装载寄存器

  33.         TIM_OC4Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
  34.         TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR4上的预装载寄存器

  35.         TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的预装载寄存器


  36.         TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设


  37. }



  38. /*功能名称TIM4_PWMINPUT_INIT(u16 arr,u16 psc)
  39.   描述      PWM输入初始化*/

  40. void TIM4_PWMINPUT_INIT(u16 arr,u16 psc)
  41. {

  42.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体
  43.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置
  44.         TIM_ICInitTypeDef  TIM4_ICInitStructure;                 //TIM4  PWM配置结构体
  45.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体

  46.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);     //Open TIM4 clock
  47.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //open gpioB clock

  48.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;             //GPIO 7
  49.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;          //上拉输入
  50.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  51.   GPIO_Init(GPIOB, &GPIO_InitStructure);

  52.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
  53.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
  54.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  55.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  56.         TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位


  57.         /*配置中断优先级*/
  58.         NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
  59.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  60.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  61.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  62.   NVIC_Init(&NVIC_InitStructure);

  63.   TIM4_ICInitStructure.TIM_Channel = TIM_Channel_2;
  64.   TIM4_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
  65.   TIM4_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  66.   TIM4_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  67.   TIM4_ICInitStructure.TIM_ICFilter = 0x3;   //Filter:过滤

  68.   TIM_PWMIConfig(TIM4, &TIM4_ICInitStructure);     //PWM输入配置
  69.   TIM_SelectInputTrigger(TIM4, TIM_TS_TI2FP2);     //选择有效输入端
  70.   TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Reset);  //配置为主从复位模式
  71.   TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发
  72.   TIM_ITConfig(TIM4, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置
  73.   TIM_ClearITPendingBit(TIM4, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  74.   TIM_Cmd(TIM4, ENABLE);
  75. }


  76. void TIM4_IRQHandler(void)
  77. {

  78.                 if (TIM_GetITStatus(TIM4, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
  79.                         {
  80.                                 duty_TIM4    =   TIM_GetCapture1(TIM4);          //采集占空比
  81.                if  (TIM_GetCapture2(TIM4)>600)         period_TIM4        =        TIM_GetCapture2(TIM4);//简单的处理
  82.                                 CollectFlag_TIM4 = 0;
  83.         }
  84.                 TIM_ClearITPendingBit(TIM4, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  85. }


  86. /*功能名称TIM1_PWMINPUT_INIT(u16 arr,u16 psc)
  87.   描述      PWM输入初始化*/

  88. void TIM1_PWMINPUT_INIT(u16 arr,u16 psc)
  89. {

  90.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体
  91.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置
  92.         TIM_ICInitTypeDef  TIM1_ICInitStructure;                 //PWM配置结构体
  93.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体

  94.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);     //Open TIM1 clock
  95.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);  //open gpioE clock
  96.    GPIO_PinRemapConfig(GPIO_FullRemap_TIM1, ENABLE); //Timer1完全重映射  TIM1_CH2->PE11
  97.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;             //GPIO 11
  98.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;          //上拉输入
  99.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  100.   GPIO_Init(GPIOE, &GPIO_InitStructure);

  101.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
  102.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
  103.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  104.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  105.         TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位


  106.         /*配置中断优先级*/
  107.   NVIC_InitStructure.NVIC_IRQChannel =  TIM1_CC_IRQn;   //TIM1捕获中断
  108.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  109.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  110.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  111.   NVIC_Init(&NVIC_InitStructure);

  112.   TIM1_ICInitStructure.TIM_Channel = TIM_Channel_2;
  113.   TIM1_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
  114.   TIM1_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  115.   TIM1_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  116.   TIM1_ICInitStructure.TIM_ICFilter = 0x03;   //Filter:过滤

  117.   TIM_PWMIConfig(TIM1, &TIM1_ICInitStructure);     //PWM输入配置
  118.   TIM_SelectInputTrigger(TIM1, TIM_TS_TI2FP2);     //选择有效输入端
  119.   TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Reset);  //配置为主从复位模式
  120.   TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发
  121. // TIM_ITConfig(TIM1, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置
  122.   TIM_ITConfig(TIM1, TIM_IT_CC2, ENABLE); //通道2 捕获中断打开
  123.   //TIM_ClearITPendingBit(TIM1, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  124.   TIM_Cmd(TIM1, ENABLE);
  125. }


  126. void TIM1_CC_IRQHandler(void)
  127. {

  128.         {
  129.                 if (TIM_GetITStatus(TIM1, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
  130.                         {
  131.                                 duty_TIM1    =   TIM_GetCapture1(TIM1);          //采集占空比
  132.                            if  (TIM_GetCapture2(TIM1)>600)         period_TIM1        =        TIM_GetCapture2(TIM1);
  133.                                 CollectFlag_TIM1 = 0;
  134.                         }
  135.         }
  136.                 TIM_ClearITPendingBit(TIM1, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  137. }


  138. /*功能名称TIM2_PWMINPUT_INIT(u16 arr,u16 psc)
  139.   描述      PWM输入初始化*/

  140. void TIM2_PWMINPUT_INIT(u16 arr,u16 psc)
  141. {

  142.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体
  143.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置
  144.         TIM_ICInitTypeDef  TIM2_ICInitStructure;                 //TIM2  PWM配置结构体
  145.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体

  146.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);     //Open TIM2 clock
  147. // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //open gpioB clock
  148. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟
  149. GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);          //关闭JTAG
  150.          GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE); //Timer2完全重映射  TIM2_CH2->PB3

  151.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;             //GPIO 3
  152.         GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_IPU;          //浮空输入 上拉输入
  153.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  154.         GPIO_Init(GPIOB, &GPIO_InitStructure);

  155.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
  156.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
  157.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  158.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  159.         TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位


  160.         /*配置中断优先级*/
  161.         NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
  162.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  163.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  164.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  165.   NVIC_Init(&NVIC_InitStructure);

  166.   TIM2_ICInitStructure.TIM_Channel = TIM_Channel_2;
  167.   TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
  168.   TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  169.   TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  170.   TIM2_ICInitStructure.TIM_ICFilter = 0x3;   //Filter:过滤

  171.   TIM_PWMIConfig(TIM2, &TIM2_ICInitStructure);     //PWM输入配置
  172.   TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);     //选择有效输入端
  173.   TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);  //配置为主从复位模式
  174.   TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发
  175.   TIM_ITConfig(TIM2, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置
  176.   TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  177.   TIM_Cmd(TIM2, ENABLE);
  178. }


  179. void TIM2_IRQHandler(void)
  180. {
  181.         {
  182.                 if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
  183.                         {
  184.                                 duty_TIM2    =   TIM_GetCapture1(TIM2);          //采集占空比
  185.                            if  (TIM_GetCapture2(TIM2)>600)         period_TIM2        =        TIM_GetCapture2(TIM2);
  186.                                 CollectFlag_TIM2 = 0;
  187.                         }
  188.         }
  189.                 TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  190. }

  191. /*功能名称TIM5_PWMINPUT_INIT(u16 arr,u16 psc)
  192.   描述      PWM输入初始化*/

  193. void TIM5_PWMINPUT_INIT(u16 arr,u16 psc)
  194. {

  195.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体
  196.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置
  197.         TIM_ICInitTypeDef  TIM5_ICInitStructure;                 //TIM4  PWM配置结构体
  198.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体

  199.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);     //Open TIM4 clock
  200.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //open gpioB clock

  201.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;             //GPIO 1
  202.   GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_IPU;          //浮空输入 上拉输入
  203.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  204.   GPIO_Init(GPIOA, &GPIO_InitStructure);

  205.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
  206.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
  207.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  208.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  209.         TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位


  210.         /*配置中断优先级*/
  211.         NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
  212.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  213.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  214.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  215.   NVIC_Init(&NVIC_InitStructure);

  216.   TIM5_ICInitStructure.TIM_Channel = TIM_Channel_2;
  217.   TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
  218.   TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  219.   TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  220.   TIM5_ICInitStructure.TIM_ICFilter = 0x3;   //Filter:过滤

  221.   TIM_PWMIConfig(TIM5, &TIM5_ICInitStructure);     //PWM输入配置
  222.   TIM_SelectInputTrigger(TIM5, TIM_TS_TI2FP2);     //选择有效输入端
  223.   TIM_SelectSlaveMode(TIM5, TIM_SlaveMode_Reset);  //配置为主从复位模式
  224.   TIM_SelectMasterSlaveMode(TIM5, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发
  225.   TIM_ITConfig(TIM5, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置
  226.   TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  227.   TIM_Cmd(TIM5, ENABLE);
  228. }


  229. void TIM5_IRQHandler(void)
  230. {
  231.         {
  232.                 if (TIM_GetITStatus(TIM5, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
  233.                         {
  234.                                 duty_TIM5    =   TIM_GetCapture1(TIM5);          //采集占空比
  235.                         if  (TIM_GetCapture2(TIM5)>600)         period_TIM5        =        TIM_GetCapture2(TIM5);
  236.                                 CollectFlag_TIM5 = 0;
  237.                         }
  238.         }
  239.                 TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  240. }
复制代码


PID部分:
准备部分:先定义PID结构体:

  1.     typedef struct
  2.     {
  3.     int setpoint;//设定目标
  4.     int sum_error;//误差累计
  5.     float proportion ;//比例常数
  6.     float integral ;//积分常数
  7.     float derivative;//微分常数
  8.     int last_error;//e[-1]
  9.     int prev_error;//e[-2]
  10.     }PIDtypedef;
复制代码



这里注意一下成员的数据类型,依据实际需要来定的。
在文件中定义几个关键变量:
  1. float  Kp =     0.32  ; //比例常数
  2. float  Ti =                0.09 ; //积分时间常数
  3. float Td =                0.0028 ;  //微分时间常数
  4. #define T                  0.02 //采样周期
  5. #define Ki     Kp*(T/Ti)        // Kp Ki Kd 三个主要参数
  6. #define Kd                Kp*(Td/T)
复制代码


C语言好像用#define 什么什么对程序不太好,各位帮忙写个优化办法看看呢? 用const?

PID.H里面主要的几个函数:
  1. void PIDperiodinit(u16 arr,u16 psc);        //PID 采样定时器设定
  2. void incPIDinit(void);                //初始化,参数清零清零
  3. int incPIDcalc(PIDtypedef*PIDx,u16 nextpoint);           //PID计算
  4. void PID_setpoint(PIDtypedef*PIDx,u16 setvalue);  //设定 PID预期值
  5. void PID_set(float pp,float ii,float dd);//设定PID  kp ki kd三个参数
  6. void set_speed(float W1,float W2,float W3,float W4);//设定四个电机的目标转速
复制代码


PID处理过程:
岔开一下:这里我控制的是电机的转速w,实际上电机的反馈波形的频率f、电机转速w、控制信号PWM的占空比a三者是大致线性的正比的关系,这里强调这个的目的是
因为楼主在前期一直搞不懂我控制的转速怎么和TIM4输出的PWM的占空比联系起来,后来想清楚里面的联系之后通过公式把各个系数算出来了。


正题:控制流程是这样的,首先我设定我需要的车速(对应四个轮子的转速),然后PID就是开始响应了,它先采样电机转速,得到偏差值E,带入PID计算公式,得到调整量也就是最终更改了PWM的占空比,不断调节,直到转速在稳态的一个小范围上下浮动。
上面讲到的“得到调整量”就是增量PID的公式:
  1.     int incPIDcalc(PIDtypedef *PIDx,u16 nextpoint)
  2.     {
  3.     int iError,iincpid;
  4.     iError=PIDx->setpoint-nextpoint;  //当前误差
  5.     /*iincpid=                                               //增量计算
  6.     PIDx->proportion*iError                //e[k]项
  7.     -PIDx->integral*PIDx->last_error          //e[k-1]
  8.     +PIDx->derivative*PIDx->prev_error;//e[k-2]
  9.     */
  10.     iincpid=                                                          //增量计算
  11.     PIDx->proportion*(iError-PIDx->last_error)
  12.     +PIDx->integral*iError
  13.     +PIDx->derivative*(iError-2*PIDx->last_error+PIDx->prev_error);

  14.     PIDx->prev_error=PIDx->last_error; //存储误差,便于下次计算
  15.     PIDx->last_error=iError;
  16.     return(iincpid) ;
  17.     }


  18. 复制代码

  19. 注释掉的是第一种写法,没注释的是第二种以Kp KI kd为系数的写法,实际结果是一样的。
  20. 处理过程放在了TIM6,溢出周期时间就是是PID里面采样周期(区分于反馈信号的采样,反馈信号采样是1M的频率)
  21. 相关代码:


  22.     void TIM6_IRQHandler(void)        //        采样时间到,中断处理函数
  23.     {

  24.     if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)//更新中断
  25.             {
  26.             frequency1=1000000/period_TIM4        ; //通过捕获的波形的周期算出频率
  27.             frequency2=1000000/period_TIM1        ;
  28.             frequency3=1000000/period_TIM2        ;
  29.             frequency4=1000000/period_TIM5        ;
  30.     /********PID1处理**********/
  31.             PID1.sum_error+=(incPIDcalc(&PID1,frequency1));         //计算增量并累加
  32.            pwm1=PID1.sum_error*4.6875  ;   //pwm1 代表将要输出PWM的占空比
  33.               frequency1=0; //清零
  34.          period_TIM4=0;
  35.     /********PID2处理**********/
  36.              PID2.sum_error+=(incPIDcalc(&PID2,frequency2));         //计算增量并累加  Y=Y+Y'
  37.              pwm2=PID2.sum_error*4.6875 ;   //将要输出PWM的占空比
  38.             frequency2=0;
  39.             period_TIM1=0;
  40.     /********PID3处理**********/
  41.              PID3.sum_error+=(incPIDcalc(&PID3,frequency3));          //常规PID控制
  42.             pwm3=PID3.sum_error*4.6875 ;   //将要输出PWM的占空比
  43.             frequency3=0;
  44.             period_TIM2=0;
  45.     /********PID4处理**********/
  46.                 PID4.sum_error+=(incPIDcalc(&PID4,frequency4));         //计算增量并累加
  47.              pwm4=PID4.sum_error*4.6875 ;   //将要输出PWM的占空比
  48.             frequency4=0;
  49.             period_TIM5=0;
  50.               }
  51.     TIM_SetCompare(pwm1,pwm2,pwm3,pwm4);             //重新设定PWM值
  52.     TIM_ClearITPendingBit(TIM6, TIM_IT_Update); //清除中断标志位
  53.     }
复制代码


上面几个代码是PID实现的关键部分

整定过程:
办法有不少,这里用的是先KP,再TI,再TD,在微调。其他的办法特别是有个尼古拉斯法我发现不适合我这个控制对象。
先Kp,就是消除积分和微分部分的影响,这里我纠结过到底是让Ti 等于一个很大的值让Ki=Kp*(T/Ti)里面的KI接近零,还是直接定义KI=0,TI=0.
然后发现前者没法找到KP使系统震荡的临界值,第二个办法可以得到预期的效果:即KP大了会产生震荡,小了会让系统稳定下来,当然这个时候是有稳态误差的。
随后把积分部分加进去,KI=Kp*(T/Ti)这个公式用起来,并且不断调节TI 。TI太大系统稳定时间比较长。
然后加上Kd        =Kp*(Td/T),对于系统响应比较滞后的情况效果好像好一些,我这里的电机反映挺快的,所以Td值很小。
最后就是几个参数调节一下,让波形好看一点。这里的波形实际反映的是采集回来的转速值,用STM32的DAC功能输出和转速对应的电压,用示波器采集的。
最后的波形是这样的:
3.jpg
最后欢迎大家拍砖,有批评才会让我更加进步!
最后把PID文件放上来 pid.zip (2.88 KB, 下载次数: 119)
收藏 5 评论16 发布时间:2017-12-26 15:40

举报

16个回答
nyszx 回答时间:2017-12-26 15:41:49
再付个网上的资料 PID控制经典培训教程.pdf (406.07 KB, 下载次数: 152)
zero99 回答时间:2017-12-26 16:11:12
大佬,这样插代码效果更加哦

  1. 测试代码
  2. 测试代码
  3. 测试代码
  4. 测试代码
复制代码


333333.png
nyszx 回答时间:2017-12-26 19:24:38
我马上改过来
shaidn 回答时间:2017-12-26 20:48:07
学习了,谢谢!
板子粉丝 回答时间:2018-5-12 12:58:31
不错,学习了
zts329547875 回答时间:2018-7-22 02:13:36
学习了,正好需要
libin1009 回答时间:2018-7-22 15:44:52
学习一下!
shwp 回答时间:2018-8-20 14:08:14
学习
wjwjwjwj997 回答时间:2018-8-21 08:14:50
学习了,正好需要
12下一页

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版