直流电机的简单详细的pid控制

对于PID的基本原理在这里就不概述了,想必大家找PID控制资料更多的是代码的实现,而不是理论概述。如果真的不懂理论,大家可以在CSDN上面搜索。废话不多说,上代码。

 如果这个提示还不能满足你,可以参考链接里面的代码,里面有非常详细的代码解释,理论讲解链接:https://pan.baidu.com/s/1-voedPOlFjSzMwYV-vdzLQ?pwd=1234 提取码:1234

跳转不了可以复制新建窗口打开 链接:https://pan.baidu.com/s/1-voedPOlFjSzMwYV-vdzLQ?pwd=1234 
提取码:1234

PID公式:

根据增量式离散PID 公式
Pwnt=Kp [e (k) -e (k-1)]+Ki*e (k) +kd[e (k) -2e (k-1) te (k-2) ]
e(k): 本次偏差
e(k-1):上一次的偏差
e(k-2):上上次的偏差
Pwm代表增量输出

速度闭环控制PI

Pwm=Kp [e (k) -e (k-1)]+Ki*e (k) 

int speed_PI (int Encoder,int Target)
{ 	
     float Kp=20,Ki=30;	
	 static int Bias,Pwm,Last_bias;
	 Bias=Encoder-Target;                //计算偏差
	 Pwm+=Kp*(Bias-Last_bias)+Ki*Bias;   //pi控制
	 Last_bias=Bias;	                   // 保存上一次偏差
	 return Pwm;                         //输出到PWM
}

位置闭环控制pd

int Position_PID (int Encoder,int Target)
{ 	
	 float Position_KP=80,Position_KI=0.1,Position_KD=500;
	 static float Bias,Pwm,Integral_bias,Last_Bias;
	 Bias=Encoder-Target;                                  //计算偏差
	 Integral_bias+=Bias;	                                 //求出偏差积分
	 Pwm=Position_KP*Bias+Position_KI*Integral_bias+Position_KD*(Bias-Last_Bias);   //位置pid
	 Last_Bias=Bias;                                       //保存上一次偏差 
	 return Pwm;                                           //输出pwm
}

完整历程,一个编码电机,速度控制

PWM输出

#include "pwm.h"

void TIM3_Init(int arr, int psc)
{ 
// 定义相关结构体:
GPIO_InitTypeDef GPIO_InitStructure; //定义一个引脚初始化的结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrue; //定义一个定时中断的结构体
TIM_OCInitTypeDef TIM_OCInitTypeStrue; //定义一个 PWM 输出的结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能GPIOB时钟,GPIOB挂载在APB2时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能通用定时器 3 时钟
// 初始化方向控制 GPIO:
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12|GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //引脚输入输出模式为推挽输出模式
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //引脚输出速度为 50MHZ
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据上面设置好的GPIO_InitStructure参数,初始化引脚
GPIO_ResetBits(GPIOB, GPIO_Pin_12|GPIO_Pin_13); //初始化设置引脚低电平
//初始化 PWM 输出 GPIO:
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;//引脚 0
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出模式,定时器功能为B1引脚复用功能
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //定义该引脚输出速度为 50MHZ
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化引脚 GPIOB1

TIM_TimeBaseInitStrue.TIM_Period=arr; //计数模式为向上计数时,定时器从0开始计数,计数超过到 arr 时触发定时中断服务函数
TIM_TimeBaseInitStrue.TIM_Prescaler=psc; //预分频系数, 决定每一个计数的时长
TIM_TimeBaseInitStrue.TIM_CounterMode=TIM_CounterMode_Up; //计数模式: 向上计数
TIM_TimeBaseInitStrue.TIM_ClockDivision=TIM_CKD_DIV1; //一般不使用,默认TIM_CKD_DIV1
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStrue); 

TIM_OCInitTypeStrue.TIM_OCMode=TIM_OCMode_PWM1; //PWM 模式 1
TIM_OCInitTypeStrue.TIM_OCPolarity=TIM_OCNPolarity_High; //输出有效电平为高电平
TIM_OCInitTypeStrue.TIM_OutputState=TIM_OutputState_Enable; //使能 PWM 输出
TIM_OCInitTypeStrue.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OC4Init(TIM3, &TIM_OCInitTypeStrue); //根 TIM_OCInitTypeStrue 参数初始化定时器 3 通道 4
//TIM_CtrlPWMOutputs(TIM3,ENABLE); //MOE 主输出使能

TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Disable); //CH4 预装载使能,
TIM_ARRPreloadConfig(TIM3, ENABLE); //TIM3 预装载使能
TIM_Cmd(TIM3, ENABLE); //使能定时器 TIM3
} 

//电机控制函数, 通过控制方向引脚和 PWM 引脚实现:
void SetPWM(int pwm)
{
if(pwm>=0)//pwm>=0 (BIN1, BIN2)=(0, 1) 正转 顺时针
{
PBout(13)=0; //BIN1=0
PBout(12)=1; //BIN2=1
TIM3->CCR4=pwm;
TIM_SetCompare4(TIM3, pwm);
} 
else if(pwm<0)//pwm<0 (BIN1, BIN2)=(1, 0) 反转 逆时针
{
PBout(13)=1; //BIN1=1
PBout(12)=0; //BIN2=0
TIM3->CCR4=-pwm;
TIM_SetCompare4(TIM3, -pwm);
}
}

主函数

#include "delay.h"
#include "usart.h"
#include "pwm.h"
#include "encoder.h"
#include "usart.h"
#include "led.h"
#include "oled.h"
#include "key.h"

int TargetVelocity=77, Encoder,PWM; 
int Velcity_Kp=4, Velcity_Ki=5, Velcity_Kd; //PID 参数
int Mortor; //电机控制标志位


void Oled_Show(void)
{
OLED_Refresh_Gram(); //显示屏实时刷新
OLED_ShowString(00,00,"VelocityFeedback"); //速度闭环
//目标速度
OLED_ShowString(00,10,"Target_V :");
if(TargetVelocity>=0)
{
OLED_ShowString(80,10,"+");
OLED_ShowNumber(90,10,TargetVelocity,5,12);
} 
else
{
OLED_ShowString(80,10,"-");
OLED_ShowNumber(90,10,-TargetVelocity,5,12);
} 
OLED_ShowString(00,20,"Current_V:");
if(Encoder>=0)
{
OLED_ShowString(80,20,"+");
OLED_ShowNumber(90,20,Encoder,5,12);
} e
lse
{
OLED_ShowString(80,20,"-");
OLED_ShowNumber(90,20,-Encoder,5,12);
} /
/显示速度控制值, 即 PWM, 分正负
OLED_ShowString(00,30,"PWM :");
if(PWM>=0)
{
OLED_ShowString(80,30,"+");
OLED_ShowNumber(90,30,PWM,5,12);
} 
else
{
OLED_ShowString(80,30,"-");
OLED_ShowNumber(90,30,-PWM,5,12);
}
} 

int main(void)
{ 

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组
delay_init(); //延迟函数初始化
JTAG_Set(JTAG_SWD_DISABLE); //关JTAG才能开OLED显示
LED_Init(); 
OLED_Init();
//uart_init(15200);
Encoder_Init(); //编码器初始化
pwm_Init(7199, 0); //电机驱动初始化
EncoderRead_TIM2(7199, 99); 
delay_ms(1000); //等待初始化完成
while(1)
{

delay_ms(100); 
LED=!LED; //LED 灯闪烁

if(KEY_Scan())MortorRun=!Mortor; 

Oled_Show(); //OLED 显示屏显示内容

//printf("TargetVelocity: %d\r\n",TargetVelocity);
//printf("Encoder: %d\r\n",Encoder);
//printf("转速: %.3fr/s\r\n", Encoder/1.04);
}
}

编码器设置

#include "encoder.h"

extern int Encoder; //当前速度
extern int TargetVelocity, Encoder,PWM; //目标速度、 编码器读数、 PWM 控制变量
extern float Velcity_Kp, Velcity_Ki, Velcity_Kd; //速度 PID 参数
extern int MortorRun; 

void MotorEncoder_Init(void)
{ 
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; //TIM4_CH1、2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure); 

TIM_TimeBaseStructure.TIM_Period = 0xffff; //设定计数器自动重装值
TIM_TimeBaseStructure.TIM_Prescaler = 0; // 预分频器
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_ICInitStructure.TIM_ICFilter = 10; //设置滤波器长度
TIM_ICInit(TIM4, &TIM_ICInitStructure); //初始化定时器 TIM4 编码器模式

TIM_ClearFlag(TIM4, TIM_FLAG_Update);//清除 TIM 的更新标志位
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); //更新中断使能
TIM_SetCounter(TIM4,0); //初始化清空编码器数值
TIM_Cmd(TIM4, ENABLE); //使能定时器 4
}
//以上结束编码器设置


int Read_Encoder(void)
{
int Encoder_TIM;
Encoder_TIM=TIM4->CNT; //读取计数
if(Encoder_TIM>0xefff)Encoder_TIM=Encoder_TIM-0xffff; //转化计数值为有方向的值大于0正转
//TIM4->CNT 范围为0-0xffff, 初值为 0。
TIM4->CNT=0; //读取完后计数清零
return Encoder_TIM; //返回
} 


void TIM4_IRQHandler(void)
{
if(TIM4->SR&0X0001)//溢出中断
{ 

} 
TIM4->SR&=~(1<<0);//清除中断标志位
}

void EncoderRead_TIM2(u16 arr, u16 psc)
{ 
//读取编码器初始化
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrue; //定义一个定时中断的结构体
NVIC_InitTypeDef NVIC_InitStrue; //定义一个中断优先级初始化的结构体

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能通用定时器 2 时钟

TIM_TimeBaseInitStrue.TIM_Period=arr; // 定时器开始计数,计数超过到arr时触发定时中断服务函数
TIM_TimeBaseInitStrue.TIM_Prescaler=psc; //预分频系数, 决定每一个计数的时长
TIM_TimeBaseInitStrue.TIM_CounterMode=TIM_CounterMode_Up; //计数模式: 向上计数
TIM_TimeBaseInitStrue.TIM_ClockDivision=TIM_CKD_DIV1; 
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStrue); 

TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //使能TIM2中断,中断模式为更新中断TIM_IT_Update

NVIC_InitStrue.NVIC_IRQChannel=TIM2_IRQn; //属于 TIM2 中断
NVIC_InitStrue.NVIC_IRQChannelCmd=ENABLE; //中断使能
NVIC_InitStrue.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级
NVIC_InitStrue.NVIC_IRQChannelSubPriority=1; //响应优先级

NVIC_Init(&NVIC_InitStrue); //设置 TIM2 中断

TIM_Cmd(TIM2, ENABLE); //使能定时器 TIM2
}
//以上结束定时读取编码器设置


void TIM2_IRQHandler()
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update)==1) 
{
Encoder=Read_Encoder(); 
if(MortorRun) //如果按键按下, 运行电机控制程
{
PWM=Velocity_FeedbackControl(TargetVelocity, Encoder); //速度环闭环控制
SetPWM(PWM); //设置 PWM
 }
 else PWM=0,SetPWM(PWM); //按键再次按下, 电机停止
 TIM_ClearITPendingBit(TIM2, TIM_IT_Update); 
 }
} 


//速度闭环控制
int Velocity_FeedbackControl(int TargetVelocity, int CurrentVelocity)
{
int Bias; 
static int ControlVelocity, Last_bias; //静态变量, 函数调用结束后其值依然存在
Bias=TargetVelocity-CurrentVelocity; //求速度偏差
ControlVelocity+=Velcity_Kp*(Bias-Last_bias)+Velcity_Ki*Bias; 
Last_bias=Bias;
return ControlVelocity; //返回速度控制值
}

  • 14
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

漏洞嵌入式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值