STM32 CubeMax直流电机速度环控制
本例基于STM32F103C8T6与CubeMax
1. 速度环控制的思路
要对直流电机进行速度环控制,首先先给电机输出一个初始占空比的PWM信号,然后适用编码器读取电机的实时转速,观察电机是转得快了还是慢了,如果转得快了就减小占空比,转的快了就增大占空比,直到达到目标转速。
算法的具体思路看之前讲PID原理的那一篇文章
2. CubeMax配置
CubeMax的配置与编码器测速模块一致
3. 接线
接线也与编码器测速中的一致
4. 代码编写
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,只是过程比较曲折,说明参数没有调得足够好,还有改进空间
参数整定可以参考野火的教程
评论(0)
您还未登录,请登录后发表或查看评论