赞
踩
本例基于STM32F103C8T6与CubeMax
要对直流电机进行速度环控制,首先先给电机输出一个初始占空比的PWM信号,然后适用编码器读取电机的实时转速,观察电机是转得快了还是慢了,如果转得快了就减小占空比,转的快了就增大占空比,直到达到目标转速。
算法的具体思路看之前讲PID原理的那一篇文章
CubeMax的配置与编码器测速模块一致,这里直接参考上一篇博文
接线也与编码器测速中的一致,这里也直接参考上一篇博文
pid.c的内容如下
#ifndef _PID_H_ #define _PID_H_ #include "stm32f1xx.h" #include "encoder.h" #include <stdio.h> //PID三个参数的值 #define KP1 27 //便于以后写多个pid算法并行,这里加序号1 #define KI1 25 #define KD1 2 typedef struct _PID//PID参数结构体 { float kp,ki,kd; float err,lastErr; float integral,maxIntegral; //积分值 float output,maxOutput; }PID; void PID_Init(void); void PID_SingleCal(PID* pid,float target,float feedback);//一次PID计算 #endif
pid.c内容如下
#include "pid.h" PID pid1; void PID_Init(void)//PID参数初始化 { pid1.err = 0; pid1.integral = 0; pid1.maxIntegral = 2000; pid1.maxOutput = __HAL_TIM_GetAutoreload(&PWM_TIM); pid1.lastErr = 0; pid1.output = 0; pid1.kp = KP1; pid1.ki = KI1; pid1.kd = KD1; } /**************************************** * 作用:进行一次PID计算 * 参数:PID参数结构体地址;目标值;反馈值 * 返回值:无 * ****************************************/ void PID_SingleCal(PID* pid,float target,float feedback)//一次PID计算 { pid->err = target - feedback; //printf("pid->err = %f\r\n",pid->err); pid->integral += pid->err; //printf("pid->integral = %f\r\n",pid->integral); if(pid->integral < -pid1.maxIntegral) pid->integral = -pid1.maxIntegral;//限制积分值 else if(pid->integral > pid1.maxIntegral) pid->integral = pid1.maxIntegral; //pid->output = pid->err * pid->kp ; pid->output = (pid->kp * pid->err) + (pid->ki * pid->integral) + (pid1.kd * (pid->err - pid->lastErr));//全量式PID if(pid->output < 0) pid->output = 0;//限制积分值 else if(pid->output > pid1.maxOutput) pid->output = pid1.maxOutput; pid->lastErr = pid->err; }
此外要在编码器测速函数中增加对输出PWM占空比的控制,encoder,c文件如下
#include "encoder.h" Motor motor1; extern PID pid1; uint8_t voidErr = 0;//第一次速度计算有问题,直接规避掉不输出 void Motor_Init(void) { HAL_TIM_Encoder_Start(&ENCODER_TIM, TIM_CHANNEL_ALL); //开启编码器定时器 __HAL_TIM_ENABLE_IT(&ENCODER_TIM,TIM_IT_UPDATE); //开启编码器定时器更新中断,防溢出处理 HAL_TIM_Base_Start_IT(&GAP_TIM); //开启10ms定时器中断 HAL_TIM_PWM_Start(&PWM_TIM, TIM_CHANNEL_2); //开启PWM HAL_TIM_PWM_Start(&PWM_TIM, TIM_CHANNEL_1); //开启PWM __HAL_TIM_SET_COUNTER(&ENCODER_TIM, 10000); //编码器定时器初始值设定为10000 motor1.lastCount = 10000; //结构体内容初始化 motor1.totalCount = 10000; motor1.overflowNum = 0; motor1.speed = 0; motor1.direct = 0; } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//定时器回调函数,用于计算速度 { if(htim->Instance==ENCODER_TIM.Instance)//编码器输入定时器溢出中断 { if(COUNTERNUM < 10000) motor1.overflowNum++; //如果是向上溢出 else if(COUNTERNUM >= 10000) motor1.overflowNum--; //如果是向下溢出 //__HAL_TIM_SetCounter(&ENCODER_TIM, 10000); //重新设定初始值 } else if(htim->Instance==GAP_TIM.Instance)//间隔定时器中断,是时候计算速度了 { motor1.direct = __HAL_TIM_IS_TIM_COUNTING_DOWN(&ENCODER_TIM);//如果向上计数(正转),返回值为0,否则返回值为1 motor1.totalCount = COUNTERNUM + motor1.overflowNum * RELOADVALUE;//一个周期内的总计数值等于目前计数值加上溢出的计数值 motor1.speed = (float)(motor1.totalCount - motor1.lastCount) / (4 * MOTOR_SPEED_RERATIO * PULSE_PRE_ROUND) * 600;//算得每秒多少转,除以4是因为4倍频 motor1.lastCount = motor1.totalCount; //记录这一次的计数值 if(voidErr == 0) voidErr++; else { printf("%.3f\r\n",motor1.speed); PID_SingleCal(&pid1,200,motor1.speed);//进行一次PID计算 //printf("pid.ouput = %f\r\n",pid1.output); __HAL_TIM_SetCompare(&PWM_TIM, TIM_CHANNEL_1, pid1.output);//修改电机PWM占空比 } } }
设置目标速度为200,最后还是能比较稳定地维持在200,只是过程比较曲折,说明参数没有调得足够好,还有改进空间
参数整定可以参考野火的教程
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。