STM32F103直流有刷电机速度闭环控制
在《基于直流有刷电机的速度闭环控制以及matlab仿真》我们介绍了速度闭环控制的实现,其采用的是PID控制算法,本节我们就基于STM32F103来实现直流电机的增量式PID速度闭环控制。
一、软件实现
我们需要将PID控制器应用于STM32F103的速度闭环电机控制,以下是接下来的步骤:
- 电机速度测量:使用编码器来测量电机的实际转速,需要配置定时器的编码器模式来读取编码器脉冲,从而计算速度;具体参考《
STM32F103霍尔编码器测速》; - 电机驱动:通常使用
PWM驱动电机,需要配置定时器的PWM输出模式,并设置占空比来控制电机速度;具体参考《基于直流有刷电机的开环调速控制以及matlab仿真》; - 系统定时:
PID计算需要定期执行,因此需要配置一个定时器中断,在中断服务函数中执行PID计算和更新PWM输出; - 主程序初始化:初始化所有外设(编码器定时器、电机
PWM、中断定时器等)和PID参数; - 主循环:通常主循环可以处理一些非实时任务,比如接收用户指令(例如通过串口改变目标速度)等。
1.1 pid.h
在control目录下新建pid.h文件,自定义PID结构体;
/******************************************************************************************************/
#ifndef _STM32f10x_PID__H
#define _STM32f10x_PID__H
typedef struct {
float Kp, Ki, Kd; // 比例、积分、微分系数
float Ts; // 周期
float prev_error; // e(k-1)
float prev_error2; // e(k-2)
float out_max; // 输出的最大值(占空比)
float out_min; // 输出的最小值(占空比)
float output; // 当前总输出 u(k)
float target_val; // 目标值
} PID;
extern PID g_pid; // 全局PID参数
extern void pid_param_init(void); // 初始化PID参数
extern float pid_compute(float measurement); // PID计算
#endif
1.2 pid.c
在control目录下新建pid.c文件,
1.2.1 初始化PID参数
/*****************************************************************************/
#include "pid.h"
PID g_pid; // 全局pid参数
/*****************************************************************************
*
* Description: 初始化PID参数
********************************************************************************/
void pid_param_init()
{
g_pid.target_val = 5000.0f; // 目标值5000 RPM
g_pid.Ts = 0.01f; // 10ms控制周期
g_pid.output = 0.0f;
g_pid.prev_error = 0.0f;
g_pid.prev_error2 = 0.0f;
g_pid.out_max = 100.0f; // 最大输出(占空比)
g_pid.out_min = 0.0f; // 最小输出
g_pid.Kp = 0.6f;
g_pid.Ki = 0.4f;
g_pid.Kd = 0.2f;
}
pid_param_init()函数把结构体g_pid参数初始化,将目标值g_pid.target_val设置为5000.00;将实际值、上一次偏差值和上上次偏差值等初始化为0。
1.2.2 PID算法实现
增量式PID算法:
\[Δu(k)=K_p[e(k)-e(k-1)]+K_iT_se(k)+\frac{K_d}{T_s}[e(k)-2e(k-1)+e(k-2)]
\]
\[u(k)=u(k-1)+Δu(k)
\]
PID算法实现:
/**************************************************************************************************************
*
* Description: PID控制算法
* Parameter : measurement:测量值
* Return : PID输出值
*
**************************************************************************************************************/
float pid_compute(float measurement) {
float error = g_pid.target_val - measurement;
// 计算控制增量
float delta_u = g_pid.Kp * (error - g_pid.prev_error)
+ g_pid.Ki * g_pid.Ts * error
+ (g_pid.Kd / g_pid.Ts) * (error - 2.0f * g_pid.prev_error + g_pid.prev_error2);
// 计算新的总输出
g_pid.output = g_pid.output + delta_u;
// 输出限幅
if (g_pid.output > g_pid.out_max)
g_pid.output = g_pid.out_max;
if (g_pid.output < g_pid.out_min)
g_pid.output = g_pid.out_min;
// 更新误差历史
g_pid.prev_error2 = g_pid.prev_error;
g_pid.prev_error = error;
return g_pid.output;
}
1.3 TIM6_IRQHandler中断处理函数
开启定时中断,这里使用的定时器6,定时周期为10ms,在定时器溢出中断中执行PID运算;修改time.c文件:
#include "encoder.h"
#include "pid.h"
/*******************************************************************************
* Function Name : TIM6_IRQHandler
* Description : This function handles TIM6 global interrupt request.
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void TIM6_IRQHandler(void)
{
//**********************自定义用户任务****************************//
int res_pwm = 0; /*PWM值(PID输出)*/
// 1. 获取编码器速度
g_motor_speed = get_encoder_speed(10);
// 2. 进行PID运算,得到PWM输出值
res_pwm = pid_compute(g_motor_speed);
// 3. 更新PWM输出
motor_set_duty(res_pwm);
//*****************************************************************//
TIM6->SR &= ~(1 << 0); // 清中断标志
}
其中motor_set_duty定义在motor.c文件中;
/**************************************************************************************************
*
* Function : 设置电机PWM占空比
* Parameter : duty:PWM占空比
*
**************************************************************************************************/
void motor_set_duty(int duty)
{
g_motor_pwm = duty;
if(g_motor_pwm < 0)
{
g_motor_pwm = 0;
}
if(g_motor_pwm >100)
{
g_motor_pwm = 100;
}
motor_update_speed();
}
1.4 主函数
#include "common.h"
#include "stdio.h"
#include "pid.h"
int main()
{
int duty;
int speed;
STM32_Clock_Init(9); // 系统时钟初始化
// 串口初始化
STM32_NVIC_Init(2, USART1_IRQn, 0, 1); // 串口中断优先级初始化,其中包括中断使能
usart_init(USART_1, 115200); // 串口1初始化,波特率115200 映射到PA9 PA10
// 按键KEY初始化
gpio_init(PC5, GPI_UP, HIGH); // PC5接按键KEY0
gpio_init(PA15, GPI_UP, HIGH); // PA15接按键KEY1
Ex_NVIC_Congig(PC5, FALLING); // 按键KEY0按下触发 高电平->低电平
Ex_NVIC_Congig(PA15, FALLING); // 按键KEY1按下触发 高电平->低电平
STM32_NVIC_Init(2, EXTI9_5_IRQn, 2, 2); // EXTI线[9:5]中断优先级初始化,其中包括中断使能
STM32_NVIC_Init(2, EXTI15_10_IRQn, 2, 2); // EXTI线[15:10]中断优先级初始化,其中包括中断使能
motor_init(); // 电机初始化,使用的定时器2,PA0/PA1
encoder_init(); // 编码器初始化,使用的定时器4,PB6/PB7
pid_param_init(); // 初始化PID参数
STM32_NVIC_Init(2, TIM6_IRQn, 0, 0); // TIM6溢出中端使能
TIM_Init_MS(TIMER6, 10); // TIM6计数到10ms发生中断,在TIM6_IRQHandler中获取转速,并保存到全局变量g_motor_speed
while (1)
{
duty = get_motor_duty();
printf("duty: %d\n", duty);
delay_ms(1000);
printf("Speed: %d RPM\n", g_motor_speed); // 打印转速
printf("PID Output: %f\n", g_pid.output); // PID输出
}
}
二、 测试
编译程序并下载测试,可以通过串口查看当前输出的占空比以及对应的转速;
三、源码下载
源码下载路径:stm32f103。
参考文章
[2] STM32F103霍尔编码器测速

浙公网安备 33010602011771号