本文章参考
https://blog.csdn.net/hbsyaaa/article/details/114715319?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167011700616800184129293%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=167011700616800184129293&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-4-114715319-null-null.142v67control,201v3add_ask,213v2t3_control2&utm_term=%E5%AE%9A%E6%97%B6%E5%99%A8%E7%9A%84%E7%BC%96%E7%A0%81%E5%99%A8%E5%8A%9F%E8%83%BD%E4%BD%9C%E7%94%A8%E6%98%AF%E5%95%A5&spm=1018.2226.3001.4187
一.PID概念
p:是一种比例控制,作用是相当与控制现在的输出与要求指令保持一致(存在着一种问题是静差,就不会达到目标值),在惯性系统中很实用(烧水)。
i:是一种积分控制,是用来研究过去产生的误差
D:是一种未来式的控制,用来演算以后可能出现的误差,反应误差信号变化控制的
其实PID是三种控制的和运算,也就是上面那个数学公式,第一项是p控制,第二项是i运算,第三项是d运算
但是对于单片机来说是没有这种连续的数据的,单片机都是离散的数据,所以用积分和微分最基本的定义来说就是:积分表示的是求和而微分所表示的意义就是求变化率。所以我们可以化简出下面的那 一个公式。
这个就是增量式pid算法,其实就是做个差
增量pid计算需要到:这次的误差,上次的误差,上上次的误差。
二。STM32F103C8T6定时器的编码器模式
这里我们使用定时器4作为我们的编码器模式接口
#include "ENCODER.h"
/*TIM4初始化为编码器接口*/
void Encoder_Init_TIM4(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义一个引脚初始化的结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//定义一个定时器初始化的结构体
TIM_ICInitTypeDef TIM_ICInitStructure; //定义一个定时器编码器模式初始化的结构体
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //使能TIM4时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能CPIOB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //PB6、PB7
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据GPIO_InitStructure的参数初始化GPIOB0
TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值
TIM_TimeBaseStructure.TIM_Prescaler = psc; // 预分频器
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //选择时钟分频:不分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct的参数初始化定时器TIM4
TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3:CH1、CH2同时计数,四分频
TIM_ICStructInit(&TIM_ICInitStructure); //把TIM_ICInitStruct 中的每一个参数按缺省值填入
TIM_ICInitStructure.TIM_ICFilter = 10; //设置滤波器长度
TIM_ICInit(TIM4, &TIM_ICInitStructure); //根TIM_ICInitStructure参数初始化定时器TIM4编码器模式
TIM_Cmd(TIM4, ENABLE); //使能定时器4
}
/*TIM5初始化为编码器接口*/
void Encoder_Init_TIM5(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义一个引脚初始化的结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//定义一个定时器初始化的结构体
TIM_ICInitTypeDef TIM_ICInitStructure; //定义一个定时器编码器模式初始化的结构体
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //使能TIM5时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能CPIOB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; //PA0、PA1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据GPIO_InitStructure的参数初始化GPIOB0
TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值
TIM_TimeBaseStructure.TIM_Prescaler = psc; // 预分频器
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //选择时钟分频:不分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct的参数初始化定时器TIM4
TIM_EncoderInterfaceConfig(TIM5, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3:CH1、CH2同时计数,四分频
TIM_ICStructInit(&TIM_ICInitStructure); //把TIM_ICInitStruct 中的每一个参数按缺省值填入
TIM_ICInitStructure.TIM_ICFilter = 10; //设置滤波器长度
TIM_ICInit(TIM5, &TIM_ICInitStructure); //根TIM_ICInitStructure参数初始化定时器TIM4编码器模式
TIM_Cmd(TIM5, ENABLE); //使能定时器4
}
//读取编码器计数
int Read_Encoder_TIM4(void)
{
int Encoder_TIM;
Encoder_TIM=TIM4->CNT; //定时器4取CNT寄存器的值
if(Encoder_TIM>0xefff)Encoder_TIM=Encoder_TIM-0xffff; //转化计数值为有方向的值,大于0正转,小于0反转。
//TIM4->CNT范围为0-0xffff,初值为0。
TIM4->CNT=0; //读取完后计数清零
return Encoder_TIM; //返回值
}
int Read_Encoder_TIM5(void)
{
int Encoder_TIM;
Encoder_TIM=TIM5->CNT; //定时器5取CNT寄存器的值
if(Encoder_TIM>0xefff)Encoder_TIM=Encoder_TIM-0xffff; //转化计数值为有方向的值,大于0正转,小于0反转。
//TIM5->CNT范围为0-0xffff,初值为0。
TIM5->CNT=0; //读取完后计数清零
return Encoder_TIM; //返回值
}
void PID_Init(PID_InitDefStruct* p)//左电机
{
p->velcity_Kp=5;
p->velcity_Ki=0.5;
p->velcity_Kd=0;
p->PID_is_Enable=1;
}
void PID2_Init(PID2_InitDefStruct* p)//右电机
{
p->velcity_Kp2=5;
p->velcity_Ki2=0.5;
p->velcity_Kd2=0;
p->PID2_is_Enable=1;
}
void velocity_PID(int Targetvelocity,int Currentvelocity,PID_InitDefStruct *p)
{
if(p->PID_is_Enable==1)
{
int En=Targetvelocity-Currentvelocity;//求出左电机误差值
p->un+=p->velcity_Kp*(En-p->En_1)+p->velcity_Ki*En+p->velcity_Kd*(En-2*p->En_1+p->En_2);//pid计算
p->En_2=p->En_1;//获取上上一次的误差
p->En_1=En;//获得上次的误差
p->PWM=p->un;//得到计算出来的误差值
}
else
{
PID_Init§;
}
}
void verocity_PID(int Targetvelocity,int Currentvelocity,PID2_InitDefStruct *p)
{
if(p->PID2_is_Enable==1)
{
int En=Targetvelocity-Currentvelocity;//求出右电机误差值
p->un2+=p->velcity_Kp2*(En-p->En_3)+p->velcity_Ki2*En+p->velcity_Kd2*(En-2*p->En_3+p->En_4);//pid计算
p->En_4=p->En_3;//获取上上一次的误差
p->En_3=En;//获得上次的误差
p->PWM2=p->un2;//得到计算出来的误差值
}
else
{
PID2_Init§;
}
}
剩下的内容后续补充