搜索
bottom↓
回复: 422

【分享】增量式PID的stm32实现,整定过程

  [复制链接]

出5入4汤圆

发表于 2014-4-6 00:30:13 | 显示全部楼层 |阅读模式
本帖最后由 tim4146 于 2014-4-6 08:32 编辑

感谢大家最近的帮忙,让我顺利做完增量PID功能,虽然PID不是什么牛逼的东西,但是真心希望以后刚刚接触这块的人能尽快进入状态。
也下面我分享一下近期的这些工作吧。欢迎大家批评指点~

首先说说增量式PID的公式,这个关系到MCU算法公式的书写,实际上两个公式的写法是同一个公式变换来得,不同的是系数的差异。
资料上比较多的是:

还有一种是:

感觉第二种的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.        

  10.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
  11.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟使能
  12.        
  13.   GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE); //Timer3全映射 GPIOC-> 6,7,8,9                                                                             //用于TIM3的CH2输出的PWM通过该LED显示

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

  20.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
  21.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  这里是72分频,那么时钟频率就是1M
  22.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  23.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  24.         TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  25.        
  26.          
  27.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
  28.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
  29.         TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
  30.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
  31.        
  32.         TIM_OC1Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
  33.         TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR1上的预装载寄存器
  34.        
  35.        
  36.         TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
  37.         TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
  38.        
  39.         TIM_OC3Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
  40.         TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR3上的预装载寄存器
  41.        
  42.         TIM_OC4Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
  43.         TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR4上的预装载寄存器
  44.        
  45.         TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的预装载寄存器
  46.        

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


  48. }



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

  51. void TIM4_PWMINPUT_INIT(u16 arr,u16 psc)
  52. {
  53.   
  54.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体
  55.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置
  56.         TIM_ICInitTypeDef  TIM4_ICInitStructure;                 //TIM4  PWM配置结构体
  57.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体

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

  60.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;             //GPIO 7
  61.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;          //上拉输入
  62.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  63.   GPIO_Init(GPIOB, &GPIO_InitStructure);

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

  69.        
  70.         /*配置中断优先级*/
  71.         NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;                     
  72.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  73.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  74.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  75.   NVIC_Init(&NVIC_InitStructure);

  76.   TIM4_ICInitStructure.TIM_Channel = TIM_Channel_2;                  
  77.   TIM4_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;      
  78.   TIM4_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;   
  79.   TIM4_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  80.   TIM4_ICInitStructure.TIM_ICFilter = 0x3;   //Filter:过滤

  81.   TIM_PWMIConfig(TIM4, &TIM4_ICInitStructure);     //PWM输入配置           
  82.   TIM_SelectInputTrigger(TIM4, TIM_TS_TI2FP2);     //选择有效输入端        
  83.   TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Reset);  //配置为主从复位模式
  84.   TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发                                       
  85.   TIM_ITConfig(TIM4, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置
  86.   TIM_ClearITPendingBit(TIM4, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  87.   TIM_Cmd(TIM4, ENABLE);   
  88. }


  89. void TIM4_IRQHandler(void)
  90. {

  91.                 if (TIM_GetITStatus(TIM4, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
  92.                         {       
  93.                                 duty_TIM4    =   TIM_GetCapture1(TIM4);          //采集占空比               
  94.                if  (TIM_GetCapture2(TIM4)>600)         period_TIM4        =        TIM_GetCapture2(TIM4);//简单的处理
  95.                                 CollectFlag_TIM4 = 0;                       
  96.         }       
  97.                 TIM_ClearITPendingBit(TIM4, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  98. }


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

  101. void TIM1_PWMINPUT_INIT(u16 arr,u16 psc)
  102. {
  103.   
  104.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体
  105.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置
  106.         TIM_ICInitTypeDef  TIM1_ICInitStructure;                 //PWM配置结构体
  107.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体

  108.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);     //Open TIM1 clock
  109.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);  //open gpioE clock
  110.    GPIO_PinRemapConfig(GPIO_FullRemap_TIM1, ENABLE); //Timer1完全重映射  TIM1_CH2->PE11       
  111.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;             //GPIO 11
  112.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;          //上拉输入
  113.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  114.   GPIO_Init(GPIOE, &GPIO_InitStructure);

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

  120.        
  121.         /*配置中断优先级*/
  122.   NVIC_InitStructure.NVIC_IRQChannel =  TIM1_CC_IRQn;   //TIM1捕获中断                     
  123.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  124.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  125.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  126.   NVIC_Init(&NVIC_InitStructure);

  127.   TIM1_ICInitStructure.TIM_Channel = TIM_Channel_2;                  
  128.   TIM1_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;      
  129.   TIM1_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;   
  130.   TIM1_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  131.   TIM1_ICInitStructure.TIM_ICFilter = 0x03;   //Filter:过滤

  132.   TIM_PWMIConfig(TIM1, &TIM1_ICInitStructure);     //PWM输入配置           
  133.   TIM_SelectInputTrigger(TIM1, TIM_TS_TI2FP2);     //选择有效输入端        
  134.   TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Reset);  //配置为主从复位模式
  135.   TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发                                       
  136. // TIM_ITConfig(TIM1, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置
  137.   TIM_ITConfig(TIM1, TIM_IT_CC2, ENABLE); //通道2 捕获中断打开
  138.   //TIM_ClearITPendingBit(TIM1, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  139.   TIM_Cmd(TIM1, ENABLE);   
  140. }


  141. void TIM1_CC_IRQHandler(void)
  142. {

  143.         {
  144.                 if (TIM_GetITStatus(TIM1, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
  145.                         {       
  146.                                 duty_TIM1    =   TIM_GetCapture1(TIM1);          //采集占空比               
  147.                            if  (TIM_GetCapture2(TIM1)>600)         period_TIM1        =        TIM_GetCapture2(TIM1);
  148.                                 CollectFlag_TIM1 = 0;
  149.                         }       
  150.         }       
  151.                 TIM_ClearITPendingBit(TIM1, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  152. }


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

  155. void TIM2_PWMINPUT_INIT(u16 arr,u16 psc)
  156. {
  157.   
  158.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体
  159.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置
  160.         TIM_ICInitTypeDef  TIM2_ICInitStructure;                 //TIM2  PWM配置结构体
  161.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体

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

  167.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;             //GPIO 3
  168.         GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_IPU;          //浮空输入 上拉输入
  169.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  170.         GPIO_Init(GPIOB, &GPIO_InitStructure);

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

  176.        
  177.         /*配置中断优先级*/
  178.         NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;                     
  179.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  180.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  181.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  182.   NVIC_Init(&NVIC_InitStructure);

  183.   TIM2_ICInitStructure.TIM_Channel = TIM_Channel_2;                  
  184.   TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;      
  185.   TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;   
  186.   TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  187.   TIM2_ICInitStructure.TIM_ICFilter = 0x3;   //Filter:过滤

  188.   TIM_PWMIConfig(TIM2, &TIM2_ICInitStructure);     //PWM输入配置           
  189.   TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);     //选择有效输入端        
  190.   TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);  //配置为主从复位模式
  191.   TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发                                       
  192.   TIM_ITConfig(TIM2, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置
  193.   TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  194.   TIM_Cmd(TIM2, ENABLE);   
  195. }


  196. void TIM2_IRQHandler(void)
  197. {
  198.         {
  199.                 if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
  200.                         {       
  201.                                 duty_TIM2    =   TIM_GetCapture1(TIM2);          //采集占空比               
  202.                            if  (TIM_GetCapture2(TIM2)>600)         period_TIM2        =        TIM_GetCapture2(TIM2);
  203.                                 CollectFlag_TIM2 = 0;
  204.                         }                       
  205.         }       
  206.                 TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  207. }

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

  210. void TIM5_PWMINPUT_INIT(u16 arr,u16 psc)
  211. {
  212.   
  213.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体
  214.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置
  215.         TIM_ICInitTypeDef  TIM5_ICInitStructure;                 //TIM4  PWM配置结构体
  216.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体

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

  219.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;             //GPIO 1
  220.   GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_IPU;          //浮空输入 上拉输入
  221.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  222.   GPIO_Init(GPIOA, &GPIO_InitStructure);

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

  228.        
  229.         /*配置中断优先级*/
  230.         NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;                     
  231.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  232.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  233.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  234.   NVIC_Init(&NVIC_InitStructure);

  235.   TIM5_ICInitStructure.TIM_Channel = TIM_Channel_2;                  
  236.   TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;      
  237.   TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;   
  238.   TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  239.   TIM5_ICInitStructure.TIM_ICFilter = 0x3;   //Filter:过滤

  240.   TIM_PWMIConfig(TIM5, &TIM5_ICInitStructure);     //PWM输入配置           
  241.   TIM_SelectInputTrigger(TIM5, TIM_TS_TI2FP2);     //选择有效输入端        
  242.   TIM_SelectSlaveMode(TIM5, TIM_SlaveMode_Reset);  //配置为主从复位模式
  243.   TIM_SelectMasterSlaveMode(TIM5, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发                                       
  244.   TIM_ITConfig(TIM5, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置
  245.   TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  246.   TIM_Cmd(TIM5, ENABLE);   
  247. }


  248. void TIM5_IRQHandler(void)
  249. {
  250.         {
  251.                 if (TIM_GetITStatus(TIM5, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
  252.                         {       
  253.                                 duty_TIM5    =   TIM_GetCapture1(TIM5);          //采集占空比               
  254.                         if  (TIM_GetCapture2(TIM5)>600)         period_TIM5        =        TIM_GetCapture2(TIM5);
  255.                                 CollectFlag_TIM5 = 0;
  256.                         }                       
  257.         }       
  258.                 TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  259. }
复制代码


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. }
复制代码

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

  1. void TIM6_IRQHandler(void)        //        采样时间到,中断处理函数
  2. {          
  3.        
  4.  if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)//更新中断
  5.         {
  6.         frequency1=1000000/period_TIM4        ; //通过捕获的波形的周期算出频率
  7.         frequency2=1000000/period_TIM1        ;
  8.         frequency3=1000000/period_TIM2        ;
  9.         frequency4=1000000/period_TIM5        ;
  10. /********PID1处理**********/
  11.         PID1.sum_error+=(incPIDcalc(&PID1,frequency1));         //计算增量并累加 
  12.        pwm1=PID1.sum_error*4.6875  ;   //pwm1 代表将要输出PWM的占空比
  13.           frequency1=0; //清零
  14.      period_TIM4=0;
  15. /********PID2处理**********/
  16.          PID2.sum_error+=(incPIDcalc(&PID2,frequency2));         //计算增量并累加  Y=Y+Y'               
  17.          pwm2=PID2.sum_error*4.6875 ;   //将要输出PWM的占空比 
  18.         frequency2=0;
  19.         period_TIM1=0;
  20. /********PID3处理**********/
  21.          PID3.sum_error+=(incPIDcalc(&PID3,frequency3));          //常规PID控制
  22.         pwm3=PID3.sum_error*4.6875 ;   //将要输出PWM的占空比
  23.         frequency3=0;
  24.         period_TIM2=0;
  25. /********PID4处理**********/
  26.             PID4.sum_error+=(incPIDcalc(&PID4,frequency4));         //计算增量并累加
  27.          pwm4=PID4.sum_error*4.6875 ;   //将要输出PWM的占空比 
  28.         frequency4=0;
  29.         period_TIM5=0; 
  30.           }
  31. TIM_SetCompare(pwm1,pwm2,pwm3,pwm4);             //重新设定PWM值
  32.  TIM_ClearITPendingBit(TIM6, TIM_IT_Update); //清除中断标志位               
  33. }
复制代码


上面几个代码是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功能输出和转速对应的电压,用示波器采集的。
最后的波形是这样的:

最后欢迎大家拍砖,有批评才会让我更加进步!
最后把PID文件放上来


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出20入26汤圆

发表于 2014-4-6 00:58:41 | 显示全部楼层
mark                                             

出0入0汤圆

发表于 2014-4-6 06:22:46 来自手机 | 显示全部楼层
mark   哈哈

出0入0汤圆

发表于 2014-4-6 06:49:41 | 显示全部楼层
学习一下,虽然说明很详细,但无完整代码,不便于学习和重现实验现象,希望能上传完整工程文件
另外,这种调速方法,有没有经过脉冲负载加载到电机上,看看速度调整的响应时间,还能和你示波器输出的波形差不多

出5入4汤圆

 楼主| 发表于 2014-4-6 08:36:36 | 显示全部楼层
小溪 发表于 2014-4-6 06:49
学习一下,虽然说明很详细,但无完整代码,不便于学习和重现实验现象,希望能上传完整工程文件
另外,这种 ...


示波器显示的是反馈信号的频率f 对应的DAC电压,想不出别的办法来看响应曲线了。也用示波器观看电机过的输入电压值,发现看不出什么东西..

帖子已修改,文件已经给出

出5入4汤圆

 楼主| 发表于 2014-4-6 08:39:03 | 显示全部楼层
今天女朋友过来,得在镇江玩一天,大家周末快乐~晚饭再来逛论坛。

出0入0汤圆

发表于 2014-4-6 08:41:14 | 显示全部楼层
谢谢楼主分享。。。。

出10入10汤圆

发表于 2014-4-6 08:51:55 | 显示全部楼层
关注PID,谢谢

出100入101汤圆

发表于 2014-4-6 08:54:41 | 显示全部楼层
学习了,pid做好还是用处很大。

出0入0汤圆

发表于 2014-4-6 09:46:40 | 显示全部楼层
最有价值但还是楼主的理解。

出0入0汤圆

发表于 2014-4-6 09:53:51 | 显示全部楼层
谢谢楼主分享,对于学习PID十分有帮助

出0入0汤圆

发表于 2014-4-6 10:42:31 | 显示全部楼层
大哥能提供完整的程序吗?我最近也在搞啊

出0入0汤圆

发表于 2014-4-6 14:04:51 | 显示全部楼层
谢谢楼主分享,对于学习PID十分有帮助

出5入4汤圆

 楼主| 发表于 2014-4-6 15:40:41 | 显示全部楼层
aming2046 发表于 2014-4-6 10:42
大哥能提供完整的程序吗?我最近也在搞啊

完整的程序里面的PID部分只是很少一部分,头文件和源文件已经做成附件了

出5入4汤圆

 楼主| 发表于 2014-4-6 15:43:02 | 显示全部楼层
fengyunyu 发表于 2014-4-6 08:54
学习了,pid做好还是用处很大。

其实大部分时候用PID就能胜任了,有些高端的理论算法用来谢谢论文还不错。
我后来在PID的基础上上做了模糊控制,可能是参数没有优化好吧,反正动态响应曲线不如之前的常规PID好

出0入0汤圆

发表于 2014-4-6 16:00:35 | 显示全部楼层
先支持再收藏

出0入0汤圆

发表于 2014-4-6 17:08:38 | 显示全部楼层
好好研究下。先收藏了。。。

出0入0汤圆

发表于 2014-4-7 08:25:46 | 显示全部楼层
大哥能详细分析下这句吗? pwm3=PID3.sum_error*4.6875 ;   //将要输出PWM的占空比,他的pid输出与pwm是怎么对应的 啊

出5入4汤圆

 楼主| 发表于 2014-4-7 12:35:23 | 显示全部楼层
aming2046 发表于 2014-4-7 08:25
大哥能详细分析下这句吗? pwm3=PID3.sum_error*4.6875 ;   //将要输出PWM的占空比,他的pid输出与pwm是怎 ...

电机反馈信号的频率f 和电机是成正比的,也就是说,我如果需要电机转速 w=2pi rad/s的话,我对应的捕获频率是电机在这个转速时光电编码器反馈波形的频率大小。所以我PID计算的其实是以这个频率为标准的调整量,PID的设定值也是频率,增量计算的也是频率,当然你用转速 w 和PWM占空比 a 做PID的计算也是可行的,只要找到 转速=(系数1)*频率=(系数2)*占空比 这个关系里面的系数就行。
我来说说这个4.6875怎么来的吧,这里我把占空比看成“占空量”(占空比:在一串理想的脉冲周期序列中(如方波),正脉冲的持续时间与脉冲总周期的比值。),如果PWM周期是1000份,高电平是300份,那我的占空量就是300,在STM32里面这个占空量是可以直接作为参数设定给定时器的,用的函数就是setcompare(),你自己查一下历程看看。
好,我的电机的最大转速是2rps,也就是一秒2圈,我的编码器这个时候的反馈频率应该是1536(查看编码器的参数),这个时候占空比需要100%,也就是1000的占空量。电机不转呢,反馈频率就是0,占空量就是0。
简单的就是占空量对应0频率,1000占空量对应1536频率。得到 占空量=0.651*频率。
那我程序是4.6875呢?我继续说!
例程里面关于PWM波形输出的TIM3的初始化是:TIM3_PWM_Init(1000-1,72-1);
这样就是72分频,定时器频率变成1M,对应成1 us计算很方便。1000是指PWM波形的周期是1000,这里正好是1000us(注意:这里1000这个参数越大,说明占空比的分辨率越高,但是在定时器频率不变的前提下,pwm周期越大,输出PWM的频率就越小)。但是这样的参数下,PWM的频率只有1K,电机产出明显的噪音,经过调试,电机在10K的频率下控制的效果比较好。也就是说我要凑个10Kpwm输出,但是pwm周期不能太小,咱要保证控制精度啊。
所以通过计算:TIM3_PWM_Init(7200-1,1-1);//定时器72M运行,周期7200份,电机频率正好是10K
好了这里的7200放到之前的计算中,系数就是4.6875了。

小弟的方法都是自己的摸索出来的,在资料里面根本没人说这个细节,相关讨论群里面大家也不说。所以,虽然我实现了PID并且能得到较好的动态响应曲线,但是我能保证这样的办法是否存在漏洞,以分享的方式公布出来,就是希望各位路过的同仁能给予指点。

出5入4汤圆

 楼主| 发表于 2014-4-7 12:37:13 | 显示全部楼层
关于定时器的一些设置做在了main函数里面,上面没给出,现在贴出来!
float  Kp =     0.32  ; //比例常数
float  Ti =                0.09 ; //积分常数
float Td =                0.015 ;  //微分常数
#define T                  0.02 //采样周期
//#define Ka         Kp*(1+(T/Ti)+(Td/T)) //另一种公式的三个参数
//#define Kb     (Kp)*(1+(2*Td/T))
//#define Kc                Kp*Td/T

#define Ki     Kp*(T/Ti)        // Kp Ki Kd 三个主要参数
#define Kd                Kp*(Td/T)


u8 start_flag=0;

u16 pwm1=0,pwm2=0,pwm3=0,pwm4=0;  //PWM 波形占空量 占空比=PWMx/7200

u8 flag_lcd=0;//液晶屏幕更新标志
u8 flag_bluetooth =0;//蓝牙验证状态      1:已发出验证信息  0:未发出验证信息
u8 status_bluetooth=0;//蓝牙连接状态位   1:已连接                         0:未连接
int main(void)
{

        u8 len ,t;

        SystemInit();
        delay_init(72);                             //延时初始化
        NVIC_Configuration();          //中断配置  中断分组2:2位抢占优先级,2位响应优先级
        init_LCD_IO() ;                                   //初始化LCD控制引脚 PG4 5
        uart_init(9600);                                //串口初始化

   lcd_init();                                           //LCD显示
   LED_GPIO_Config();             // led 初始化
   
   MOTOR_INIT();
           Dac1_Init();                                //DAC初始化
           incPIDinit();                  //PID初始化 置零
fuzzy_init()  ;
    KEY_Init();
        EXTIX_Init();                         //外部中断初始化
   
        TIM3_PWM_Init(7200-1,1-1);           //参数1*参数2/(72e6)=1/f    f:需要的电机频率
        //PWM输出  频率:1KHZ  pwm周期:1000us        参数: 1000-1 72-1        定时器频率 1M  特点;电机频率太低,电机噪音,精度Vmax/1000
        //PWM输出  频率:10KHZ  pwm周期:100us        参数: 100-1 72-1          定时器频率 1M  特点;频率合适,控制精度太低
        //PWM输出  频率:10KHZ  周期:7200        参数: 1000-1 72-1

        TIM4_PWMINPUT_INIT(0xffff,72-1);   //pwm输入初始化以1M的频率捕捉
        TIM1_PWMINPUT_INIT(0xffff,72-1);   //pwm输入初始化以1M的频率捕捉
        TIM2_PWMINPUT_INIT(0xffff,72-1);   //pwm输入初始化以1M的频率捕捉
        TIM5_PWMINPUT_INIT(0xffff,72-1);   //pwm输入初始化以1M的频率捕捉
   MOTOR_OUT(1,0,1,0,1,0,1,0);//转速全为正,速度都是0
        PID_set(Kp,Ki,Kd);                        //初始化 PID参数                 
    printf("Kp=%f\r\n",Kp);           //输出参数:便于调试观察
    printf("Ki=%f\r\n",Ki);
    printf("Kd=%f\r\n",Kd);

//   PID_setpoint(&PID1,500);   //开机就设定轮子转动,便于调试,可注释掉
//        PID_setpoint(&PID2,200);
//        PID_setpoint(&PID3,500);
//        PID_setpoint(&PID4,300);

        PIDperiodinit(40,36000-1);
                  //设定PID采样周期 T=20ms          72000 000/36 000 = 2 KHz          和 T 对应
   //set_speed(3,3,3,3);
   TIM_Cmd(TIM6, ENABLE);  //使能TIMx 开启PID处理
   
   while(1)
   {
//   printf("Kp=%f\r\n",PID3.proportion2);           //输出参数:便于调试观察

           if(USART_RX_STA&0x8000)          //如果完成一次接收
                {       
                   TIM_Cmd(TIM6, DISABLE);  //         关闭PID运算
                   stop(); //PID相关参数清零,并且小车停止运动                                  
                        len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度,本控制系统应该len==15  或者 2
                        printf("MCU_GET:");
                        for(t=0;t<len;t++) //返回所以数值
                        {
                                USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据
                                while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
                        }
                        printf("\r\n");//插入换行

                        if(len==15)         //长度15的是速度控制报文
                  {                  LED1=0;// LIGHT ON
                //报文格式 :x+正负号+x速度+y+正负号+y速度+w+正负号+w速度
            //例如     :x+123y+000w+000,表示只有x方向速度的,其余均为零的
                        table1[3]=USART_RX_BUF[1];table1[4]=USART_RX_BUF[2]; table1[5]=USART_RX_BUF[3];table1[6]=USART_RX_BUF[4];        //更新到LCD
                        table2[3]=USART_RX_BUF[6];table2[4]=USART_RX_BUF[7]; table2[5]=USART_RX_BUF[8];table2[6]=USART_RX_BUF[9];
                        table3[3]=USART_RX_BUF[11];table3[4]=USART_RX_BUF[12]; table3[5]=USART_RX_BUF[13];table3[6]=USART_RX_BUF[14];
                        lcd_init();                                           //LCD显示

                        X=(USART_RX_BUF[2]-0x30)*100+(USART_RX_BUF[3]-0x30)*10+(USART_RX_BUF[4]-0x30) ;          //得到Vx
                        if(USART_RX_BUF[1]=='-')X=-X; //加上正负号
                                               
                        Y=(USART_RX_BUF[7]-0x30)*100+(USART_RX_BUF[8]-0x30)*10+(USART_RX_BUF[9]-0x30) ;         //得到Xy
                    if(USART_RX_BUF[6]=='-')Y=-Y;
                                       
                        W=(USART_RX_BUF[12]-0x30)*100+(USART_RX_BUF[13]-0x30)*10+(USART_RX_BUF[14]-0x30) ; //得到w
                        W=W/100.0;          //实际的W值缩小100倍
                    if(USART_RX_BUF[11]=='-')W=-W;
                                       
                        printf("X=%d\r\n",X);  //参数反馈,便于调试
                        printf("Y=%d\r\n",Y);
                        printf("W=%f\r\n",W);

                        kinematics(X,Y,W,&W1,&W2,&W3,&W4); //运动学方程计算,得到四个轮子的转速
                        printf("W1=%f\r\n",W1);
                        printf("W2=%f\r\n",W2);
                        printf("W3=%f\r\n",W3);
                        printf("W4=%f\r\n",W4);
                //        LED0=!LED0;                  //LED翻转
             set_speed(W1,W2,W3,W4);  //设定轮子转速,实际是更新了PID目标值
                            TIM_Cmd(TIM6, ENABLE);  //使能TIMx 开启PID
                                 LED1=1;// LIGHT Off
                   }

                   if(len==2)
                   {
                   if ((USART_RX_BUF[0]=='B') && (USART_RX_BUF[1]=='L') && (flag_bluetooth==1))
                                 {  status_bluetooth=1 ;
                                   flag_bluetooth=0;
                                   printf("blue_OK\r\n");
                                  }
                   }
           USART_RX_STA=0;          //数据处理完毕,清除状态寄存器,准备下组数据接收
           len=0;
}   
}
}

出0入0汤圆

发表于 2014-4-7 13:49:42 | 显示全部楼层
大哥辛苦了,多谢分享,我要慢慢体会,我最近在用PID做温控方面的

出0入0汤圆

发表于 2014-4-7 16:38:39 | 显示全部楼层
大哥,看了你的帖子收益很多 啊,我现在对pid的理解写给你看看,不知道对不对啊,假设我2v的电压---对应1000的占空量也就是100%,3v电压对应------0的占空量也就是0%,2v和3v是我的最大和最小温度时对应的电压,采集到的温度的电压与占空比成反比,那我根据你的方法计算,占空量=1000/(3-2)*Vpid,我的设定值也是以电压为单位的,假设设定的温度对应的电压值为2.5v,采集到的电压为2.3v,那么把2.5v和2.3v带入pid计算也就是Vpid=pid(2.5,2.3);那么最终的pwm值=(1000/(3-2))*pid(2.5,2.3);请大哥帮忙分析一下啊,多谢了

出5入4汤圆

 楼主| 发表于 2014-4-7 17:00:08 | 显示全部楼层
aming2046 发表于 2014-4-7 16:38
大哥,看了你的帖子收益很多 啊,我现在对pid的理解写给你看看,不知道对不对啊,假设我2v的电压---对应100 ...

“2v的电压---对应1000的占空量也就是100%,3v电压对应------0的占空量也就是0%,2v和3v是我的最大和最小温度时对应的电压”光是这句话,你下面的式子就貌似有问题了。电压以mV为单位,1000对应2000mV,0对应3000mV,那么占空量a和电压u的关系就是:3000-u=a

出5入4汤圆

 楼主| 发表于 2014-4-7 17:03:53 | 显示全部楼层
aming2046 发表于 2014-4-7 16:38
大哥,看了你的帖子收益很多 啊,我现在对pid的理解写给你看看,不知道对不对啊,假设我2v的电压---对应100 ...

个人认为,我这个思路对于线性系统或者近似线性系统应该问题不大,传统PID对于线性系统是比较适用的,你这里一定要把握好各个物理量之间的变换关系。

出0入0汤圆

发表于 2014-4-7 17:38:20 来自手机 | 显示全部楼层
wow,楼主好给力。正研究这个,都忘了。

出0入0汤圆

发表于 2014-4-7 18:01:38 | 显示全部楼层
哎又是一个被以讹传讹毒害的青年,世界上哪有什么增量PID啊!

出5入4汤圆

 楼主| 发表于 2014-4-7 18:26:08 | 显示全部楼层
zf12862177 发表于 2014-4-7 18:01
哎又是一个被以讹传讹毒害的青年,世界上哪有什么增量PID啊!

求大神指点明津

出0入0汤圆

发表于 2014-4-7 18:26:48 来自手机 | 显示全部楼层
MarK 好资料

出0入0汤圆

发表于 2014-4-7 18:27:10 | 显示全部楼层
好帖,留名,必火,

出0入0汤圆

发表于 2014-4-7 18:45:44 | 显示全部楼层
位置式PID和增量式PID区别

(1)位置式PID控制的输出与整个过去的状态有关,用到了误差的累加值;而增量式PID的输出只与当前拍和前两拍的误差有关,因此位置式PID控制的累积误差相对更大;
(2)增量式PID控制输出的是控制量增量,并无积分作用,因此该方法适用于执行机构带积分部件的对象,如步进电机等,而位置式PID适用于执行机构不带积分部件的对象,如电液伺服阀。
(3)由于增量式PID输出的是控制量增量,如果计算机出现故障,误动作影响较小,而执行机构本身有记忆功能,可仍保持原位,不会严重影响系统的工作,而位置式的输出直接对应对象的输出,因此对系统影响较大。


出5入4汤圆

 楼主| 发表于 2014-4-7 20:45:03 | 显示全部楼层
not_at_all 发表于 2014-4-7 18:45
位置式PID和增量式PID区别

(1)位置式PID控制的输出与整个过去的状态有关,用到了误差的累加值;而增量式 ...

学习了,感觉第二点对于两种PID的选择很有用,但是其中“执行机构带积分部件”这个不是很懂,能不能用通俗的语言给我解释一下..

出100入101汤圆

发表于 2014-4-7 21:07:48 | 显示全部楼层
zf12862177 发表于 2014-4-7 18:01
哎又是一个被以讹传讹毒害的青年,世界上哪有什么增量PID啊!

没有增量式PID?

出0入0汤圆

发表于 2014-4-7 21:20:01 | 显示全部楼层
tim4146 发表于 2014-4-7 20:45
学习了,感觉第二点对于两种PID的选择很有用,但是其中“执行机构带积分部件”这个不是很懂,能不能用通 ...

反应迟钝的东西    如加热器这种执行机构,不是一通电就立刻加热达到目标温度   

出0入0汤圆

发表于 2014-4-8 08:06:26 | 显示全部楼层
留个脚印~

出0入0汤圆

发表于 2014-4-8 08:07:40 | 显示全部楼层
大哥,电压以mV为单位,1000对应2000mV,0对应3000mV,那么占空量a和电压u的关系就是:3000-u=a;你这里的3000是3000mv吗?u是pid运算后得到的电压吗?你是把电压放大了1000倍吗?还有就是如果我的周期的份量是500,那么100%的时候占空量是500,那么他们的对应关系应该是500对应1000mv,0对应1500mv吗?也就是把500对应2v,0对应3v时的电压放大了500倍是吗?

出5入4汤圆

 楼主| 发表于 2014-4-8 09:31:48 | 显示全部楼层
aming2046 发表于 2014-4-8 08:07
大哥,电压以mV为单位,1000对应2000mV,0对应3000mV,那么占空量a和电压u的关系就是:3000-u=a;你这里的3 ...

和PID处理无关,U就是你的电压啊,a就是占空量

出0入0汤圆

发表于 2014-4-8 09:53:10 | 显示全部楼层
好贴,跟着学习。。。

出0入0汤圆

发表于 2014-4-8 10:39:27 | 显示全部楼层
好喜欢,谢楼主!

出0入0汤圆

发表于 2014-4-8 12:00:25 | 显示全部楼层
tim4146 发表于 2014-4-7 18:26
求大神指点明津

   增量式PID是在位置式PID的基础上,等式两边取微分得到的,因此应该叫做位置式PID的增量形式,叫增量式PID有点不严谨,不过好像一般工程师都是实用主义,对概念没有区分的那么清楚,好多讲义也都这么讲的,附一个网上找的资料供参考!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出5入4汤圆

 楼主| 发表于 2014-4-8 15:54:53 | 显示全部楼层
coleyao 发表于 2014-4-8 12:00
增量式PID是在位置式PID的基础上,等式两边取微分得到的,因此应该叫做位置式PID的增量形式,叫增量式 ...

谢谢了,资料说的很详细。虽然会用PID,但是回头看看还是发现自己有很多不理解的地方。学海无涯~

出0入0汤圆

发表于 2014-4-10 10:44:31 | 显示全部楼层
not_at_all 发表于 2014-4-7 18:45
位置式PID和增量式PID区别

(1)位置式PID控制的输出与整个过去的状态有关,用到了误差的累加值;而增量式 ...

控制论里面根本就没有增量PID这个东西,PID是串联校正,原系统是什么样,该校正成什么样是有定数的,根本就不是什么增量PID可以随便替换的,按增量PID那种算法,本质上是更加降低了系统带宽,增加了积分环节。

出0入0汤圆

发表于 2014-4-10 10:48:04 | 显示全部楼层

控制论里面没有增量式PID这个东西,PID就是串联校正P,I,D三个环节,所谓的增量式PID本质上是减慢了系统响应速度,增加了积分环节,所以有的系统看上去超调不明显了。但是确是错误的用法。比真正的好的调节性能差N倍。不然你让用所谓的增量式PID的人把整个系统的传递函数写出来,bode图奈氏图画出来。一般都画不出来。

出0入0汤圆

发表于 2014-4-10 10:48:24 | 显示全部楼层
tim4146 发表于 2014-4-7 18:26
求大神指点明津

好好把控制理论学一遍,世界上根本就没有什么增量式PID,纯粹是只会搞IT的工程师用不来PID,发现PID效果不过,从数学的角度推出来一个增量式PID,使得最终效果看起来OK了。其实本质上是减慢了系统响应性,增加了积分环节!
凡是那些说增量PID的,你让他画一下整个系统的bode图,截止频率,奈氏图,一般都画不出来!

出5入4汤圆

 楼主| 发表于 2014-4-10 23:14:27 | 显示全部楼层
zf12862177 发表于 2014-4-10 10:48
好好把控制理论学一遍,世界上根本就没有什么增量式PID,纯粹是只会搞IT的工程师用不来PID,发现PID效果 ...

所谓的位置式和增量式都是根据PID算法离散化得到的,后者是根据增量式PID推出来的,这个说法应该没有错吧。

出0入0汤圆

发表于 2014-4-10 23:25:37 | 显示全部楼层
介绍经验的贴 都是好帖

出0入0汤圆

发表于 2014-4-11 05:57:40 来自手机 | 显示全部楼层
本帖最后由 coleyao 于 2014-4-11 11:50 编辑
zf12862177 发表于 2014-4-10 10:48
好好把控制理论学一遍,世界上根本就没有什么增量式PID,纯粹是只会搞IT的工程师用不来PID,发现PID效果 ...


所谓经典控制理论PID控制都是频域的理论,不过增量式PID好用是因为其实它从本质上反映了控制的原理(当然只是形式上的),而且是时域内的理论,其详细细节可参考预测式负反馈理论。
   http://www.amobbs.com/thread-5484520-1-1.html  27F (内含预测式负反馈的说明和一个用来仿真PID控制算法、预测式负反馈控制算法的仿真软件)。

出0入0汤圆

发表于 2014-4-11 06:49:28 来自手机 | 显示全部楼层
路过,学习一下

出0入0汤圆

发表于 2014-4-11 07:00:40 来自手机 | 显示全部楼层
很好的pid分享贴

出0入0汤圆

发表于 2014-4-11 07:45:50 | 显示全部楼层
一直疑惑的问题貌似解决了

出0入0汤圆

发表于 2014-4-12 10:54:26 | 显示全部楼层
zf12862177 发表于 2014-4-7 18:01
哎又是一个被以讹传讹毒害的青年,世界上哪有什么增量PID啊!

《自动控制原理与设计》第五版 中文版 P147 例4.7 P148例4.8的用法不知道算不算正确? 就是增量式PID

出0入0汤圆

发表于 2014-4-12 10:55:11 | 显示全部楼层
自动控制原理与设计》第五版 中文版 P147 例4.7 P148例4.8的用法不知道算不算正确? 就是增量式PID

出0入0汤圆

发表于 2014-4-12 10:56:07 | 显示全部楼层
zf12862177 发表于 2014-4-10 10:48
好好把控制理论学一遍,世界上根本就没有什么增量式PID,纯粹是只会搞IT的工程师用不来PID,发现PID效果 ...

如果不离散化  那请问如何进行计算机控制??

出0入0汤圆

发表于 2014-4-12 10:57:26 | 显示全部楼层
有些事情说的太绝对的话 只怕会这样不好用那也不好用~

出0入0汤圆

发表于 2014-4-12 14:43:44 | 显示全部楼层
tangcangeng 发表于 2014-4-12 10:56
如果不离散化  那请问如何进行计算机控制??

能不能用PID,跟离散化没有关系。离散化还是可以用所谓的位置式PID。

出0入0汤圆

发表于 2014-4-12 16:30:49 | 显示全部楼层
zf12862177 发表于 2014-4-12 14:43
能不能用PID,跟离散化没有关系。离散化还是可以用所谓的位置式PID。

是啊 我是说帖子里头你离散化后不是得用增量式数字PID表达式了么   那你离散化后不用增量式 请问一下用什么方式呢? 不存在增量式那么如何做呢?  离散化后叫我不用增量式我真不知道如何使用PID了 或者叫数字PID了  你又说增量式PID不存在?  我很疑惑,另附《自动控制原理与设计》一书 中文版 第五版 G.F.Franklin写的 P147的例4.7与P148  例4.8 不是用的是增量式的么? 那真的不知道如果这个增量式不存在的话我怎么在数字处理器中使用数字式的PID了 请指教

出0入0汤圆

发表于 2014-4-12 17:06:49 | 显示全部楼层
tangcangeng 发表于 2014-4-12 16:30
是啊 我是说帖子里头你离散化后不是得用增量式数字PID表达式了么   那你离散化后不用增量式 请问一下用什 ...

举个栗子:
你有一系统,是一阶惯性环节,理论上这个系统是实时响应的,但是现在你将他无限放慢,放到从控制信号输入系统到被控量输出系统用时20ms, 假设你用电路搭一个校正系统,那么根据计算最优的校正系统应该是PI校正。假设你的电路PI校正一次需要1ms,也就是说从信号输入到电路,到控制电压输出,用1ms。

那么你直接将PI校正串联到被控系统就行了。整个系统就变成了一个比例环节。此时系统循环一次需要21ms。在计算电路参数的时候你会用到21ms这个时间参数。

世界本质上就是离散的。只不过离散时间常数足够小的话,从宏观看起来就是连续了。

出0入0汤圆

发表于 2014-4-13 10:48:54 | 显示全部楼层
zf12862177 发表于 2014-4-12 17:06
举个栗子:
你有一系统,是一阶惯性环节,理论上这个系统是实时响应的,但是现在你将他无限放慢,放到从 ...

虽然不知道你的具体意思,可能自动控制原理学没到你那个境界吧,但是个人觉得增量式PID是数字化PID最好理解的方式,也是用的最基本的方法,只是由于需要采样和计算会出现系统延时,相对用纯拉氏域进行校正会有些出入罢了,这种增量式只不过是一种对这种离散化过程的一种叫法 懂得如何用就是了 名称只是一个代号、  

出0入0汤圆

发表于 2014-4-15 18:19:20 | 显示全部楼层
谢谢楼主的讲解!
不知楼主的电机加速过程中有加速算法没有,我一直弄不明白加速算法怎么和PID控制结合起来?
我想做步进电机位置环和速度环,加速算法想用梯形加速或者S形加速。

出5入4汤圆

 楼主| 发表于 2014-4-15 18:33:53 | 显示全部楼层
小混hun 发表于 2014-4-15 18:19
谢谢楼主的讲解!
不知楼主的电机加速过程中有加速算法没有,我一直弄不明白加速算法怎么和PID控制结合起来 ...

加速算法是不是目的是让小车走出设定的轨迹啊?
我样机做出来了,可能会写个矩形的运动轨迹,看看回程误差。X Y方向不停测距差不多能实现。

出0入0汤圆

发表于 2014-4-21 11:10:13 | 显示全部楼层
mark

出0入0汤圆

发表于 2014-4-27 22:32:43 | 显示全部楼层
很好的东西

出0入0汤圆

发表于 2014-4-27 23:14:58 | 显示全部楼层
大婶们的讨论很精彩啊,学习了

出0入42汤圆

发表于 2014-4-28 06:47:25 来自手机 | 显示全部楼层
mark,学习。

出0入0汤圆

发表于 2014-4-28 10:23:09 | 显示全部楼层
研究研究

出0入0汤圆

发表于 2014-4-28 10:33:02 | 显示全部楼层
mark,,虽然看不懂,但是以前在大学的时候,上过一门叫计算机控制系统,去实验室也做了几个实验,就是PID控制的,当时在试验台上使用VB上位机控制的,水位控制,

出0入0汤圆

发表于 2014-4-28 10:53:51 | 显示全部楼层
很不错呢

出0入0汤圆

发表于 2014-4-28 18:27:40 | 显示全部楼层
很好,我来详细看看

出0入0汤圆

发表于 2014-4-28 18:57:21 | 显示全部楼层
很不错,对pid有了更好的理解。

出0入0汤圆

发表于 2014-4-28 22:28:59 | 显示全部楼层
MARK,占位

出0入0汤圆

发表于 2014-4-28 23:01:53 | 显示全部楼层
学习一下                                                                                                     

出0入0汤圆

发表于 2014-4-28 23:09:23 | 显示全部楼层
楼主,你已经很牛B了

出0入0汤圆

发表于 2014-5-6 14:02:20 | 显示全部楼层
谢谢分享

出5入4汤圆

 楼主| 发表于 2014-5-7 14:30:10 | 显示全部楼层
craigtao 发表于 2014-4-28 10:33
mark,,虽然看不懂,但是以前在大学的时候,上过一门叫计算机控制系统,去实验室也做了几个实验,就是PID ...

我们学校也有试验,当时压根没看懂硬件是怎么搭的(怪我模电学得太渣)

出0入0汤圆

发表于 2014-5-7 14:49:21 | 显示全部楼层
tim4146 发表于 2014-5-7 14:30
我们学校也有试验,当时压根没看懂硬件是怎么搭的(怪我模电学得太渣) ...

硬件我也是不会,,,

出0入0汤圆

发表于 2014-5-7 17:03:59 | 显示全部楼层
学习   

出0入0汤圆

发表于 2014-5-15 10:52:22 | 显示全部楼层
楼主真厉害!

出0入0汤圆

发表于 2014-5-15 11:58:34 | 显示全部楼层
学习了!~~~

出0入0汤圆

发表于 2014-5-15 16:56:01 | 显示全部楼层
学习了!~~~

出0入0汤圆

发表于 2014-5-15 17:29:10 | 显示全部楼层
学习下~

出0入0汤圆

发表于 2014-5-15 17:31:41 | 显示全部楼层
学习一下~

出0入0汤圆

发表于 2014-5-15 20:06:34 | 显示全部楼层
mark                                       

出0入0汤圆

发表于 2014-5-18 21:22:48 | 显示全部楼层
标记一下

出0入0汤圆

发表于 2014-5-19 02:30:18 来自手机 | 显示全部楼层
mark!'ccvghhhjj

出0入0汤圆

发表于 2014-5-19 09:46:14 | 显示全部楼层
mark一下

出0入0汤圆

发表于 2014-5-20 18:57:09 | 显示全部楼层

好东东,很有用的

出0入0汤圆

发表于 2014-5-24 23:34:48 | 显示全部楼层
先收藏下,回头研究,谢谢分享

出0入0汤圆

发表于 2014-5-31 23:00:53 | 显示全部楼层
我想问一下啊  pwm输出时钟是72M,周期是7200,pwm捕捉又用的是1M时钟,那捕捉到的占空量为什么会是一样的啊

出0入0汤圆

发表于 2014-6-1 09:58:19 | 显示全部楼层
ding!!!!!!!!

出5入4汤圆

 楼主| 发表于 2014-6-3 19:41:04 | 显示全部楼层
本帖最后由 tim4146 于 2014-6-3 19:57 编辑
glgincuit 发表于 2014-5-31 23:00
我想问一下啊  pwm输出时钟是72M,周期是7200,pwm捕捉又用的是1M时钟,那捕捉到的占空量为什么会是一样的啊 ...


捕获的频率1M能保证捕获到的信号的周期是正负1us
输出PWM的定时器72M的频率。而PWM的周期是7200“份”,算一下PWM的频率是10K。
捕获到的是确确实实的高电平时间,能直接用读取寄存器读出来。和我输出的时候设置的占空比或“占高量”没有直接地联系。

出0入0汤圆

发表于 2014-6-4 22:10:42 | 显示全部楼层
如何整定的讲了么?

出5入4汤圆

 楼主| 发表于 2014-6-4 22:14:09 | 显示全部楼层
Jun120036 发表于 2014-6-4 22:10
如何整定的讲了么?

用的试凑法

出0入0汤圆

发表于 2014-6-5 00:10:11 | 显示全部楼层
MARK.....学习

出0入0汤圆

发表于 2014-6-5 07:49:27 来自手机 | 显示全部楼层
不错,回头在研究一下

出0入0汤圆

发表于 2014-6-5 15:38:21 | 显示全部楼层
哥们,可以上传你的工程吗  万分感谢啊  

出0入0汤圆

发表于 2014-6-5 15:55:39 | 显示全部楼层
标记,增量式PID的stm32实现,整定过程

出0入0汤圆

发表于 2014-6-5 22:43:30 | 显示全部楼层
先标记一下子

出0入0汤圆

发表于 2014-7-1 14:52:28 | 显示全部楼层
不错!!!!

出0入0汤圆

发表于 2014-7-1 15:26:16 | 显示全部楼层
多谢分享,学习了。

出0入0汤圆

发表于 2014-7-1 15:40:50 | 显示全部楼层
回复过楼主齿轮传感器,看来也是在玩电机控制

出0入0汤圆

发表于 2014-7-1 16:52:12 | 显示全部楼层
很好,留名
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-26 16:30

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表