新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > 增量式PID的stm32实现,整定过程

增量式PID的stm32实现,整定过程

作者: 时间:2016-11-28 来源:网络 收藏
  • void TIM5_PWMINPUT_INIT(u16 arr,u16 psc)
  • {
  • TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;//TIM的初始化结构体
  • NVIC_InitTypeDef NVIC_InitStructure;//中断配置
  • TIM_ICInitTypeDefTIM5_ICInitStructure;//TIM4PWM配置结构体
  • GPIO_InitTypeDef GPIO_InitStructure;//IO口配置结构体
  • RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);//Open TIM4 clock
  • RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//open gpioB clock
  • GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//GPIO 1
  • GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IPU;//浮空输入 上拉输入
  • GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  • GPIO_Init(GPIOA, &GPIO_InitStructure);
  • TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
  • TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
  • TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  • TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//TIM向上计数模式
  • TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  • NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
  • NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  • NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  • NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  • NVIC_Init(&NVIC_InitStructure);
  • TIM5_ICInitStructure.TIM_Channel = TIM_Channel_2;
  • TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
  • TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  • TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  • TIM5_ICInitStructure.TIM_ICFilter = 0x3;//Filter:过滤
  • TIM_PWMIConfig(TIM5, &TIM5_ICInitStructure);//PWM输入配置
  • TIM_SelectInputTrigger(TIM5, TIM_TS_TI2FP2);//选择有效输入端
  • TIM_SelectSlaveMode(TIM5, TIM_SlaveMode_Reset);//配置为主从复位模式
  • TIM_SelectMasterSlaveMode(TIM5, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发
  • TIM_ITConfig(TIM5, TIM_IT_CC2|TIM_IT_Update, ENABLE);//中断配置
  • TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  • TIM_Cmd(TIM5, ENABLE);
  • }
  • void TIM5_IRQHandler(void)
  • {
  • {
  • if (TIM_GetITStatus(TIM5, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
  • {
  • duty_TIM5=TIM_GetCapture1(TIM5); //采集占空比
  • if(TIM_GetCapture2(TIM5)>600)period_TIM5=TIM_GetCapture2(TIM5);
  • CollectFlag_TIM5 = 0;
  • }
  • }
  • TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
  • }
  • 复制代码


    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. floatKp =0.32; //比例常数
    2. floatTi =0.09 ; //积分时间常数
    3. float Td =0.0028 ;//微分时间常数
    4. #define T0.02 //采样周期
    5. #define KiKp*(T/Ti)// Kp Ki Kd 三个主要参数
    6. #define KdKp*(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);//设定PIDkp 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-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. }
    复制代码

    注释掉的是第一种写法,没注释的是第二种以Kp KI kd为系数的写法,实际结果是一样的。
    处理过程放在了TIM6,溢出周期时间就是是PID里面采样周期(区分于反馈信号的采样,反馈信号采样是1M的频率)
    相关代码:
    1. void TIM6_IRQHandler(void)//采样时间到,中断处理函数
    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));//常规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);//重新设定PWM值
    27. TIM_ClearITPendingBit(TIM6, TIM_IT_Update); //清除中断标志位
    28. }
    复制代码


    上面几个代码是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功能输出和转速对应的电压,用示波器采集的。
    上一页 1 2 下一页

    关键词: 增量式PIDstm32整定过

    评论


    相关推荐

    技术专区

    关闭