STM32实现电机PID控制详解
PID 乃是控制领域中极为经典且关键的控制算法。
PID 即“比例(proportional)、积分(integral)、微分(derivative)”,属于一种司空见惯的控制算法。其应用范畴极为宽泛。小至我们日常玩耍的无人机、平衡车,大至工业领域中的温度、液位以及流量控制,PID 的踪迹皆有显现。
1.器件准备
STM32F103ZET6最小系统板
电机驱动TB6612
电机 带有 霍尔编码器12V,减速比1/30,速度330rpm
引脚接线:
模块引脚 | 单片机引脚 |
---|---|
TB6612_AIN1 | PB12 |
TB6612_AIN2 | PB13 |
TB6612_PWMA | PD5 |
编码器A相 | PA0 |
编码器B相 | PA1 |
基本代码模块
电机驱动模块
#include "motor.h"
#define PWM_MAX 7200
#define PWM_MIN -7200
int abs(int p)
{
if(p>0)
return p;
else
return -p;
}
void Load(int moto1,int moto2) //-7200~7200
{
if(moto1<0)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_13,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_13,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);
}
__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_4,abs(moto1));
}
Encoder.c
这里AB相都计数
#include "encoder.h"
#include "stdio.h"
#include "stdlib.h"
int Read_Speed(TIM_HandleTypeDef *htim)
{
int temp;
temp=(short)__HAL_TIM_GetCounter(htim);//让速度在-32768 到 32767 之间变化
__HAL_TIM_SetCounter(htim,0);
return temp;
}
中断处理函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM6) //10ms一次
{
Control();
}
}
2.软件准备
使用VOFA
https://www.vofa.plus/
Keil
CubeMX
https://blog.csdn.net/Brendon_Tan/article/details/107685563
3.PID基本概念
PID 存在众多的变形形式,其中最基础的当属增量式 PID 和位置式 PID 。此外,还有诸如去掉 PID 某一部分的 PD 算法、PI 算法,更高级些的抗饱和 PID、微分先行 PID、自适应 PID 以及模糊 PID 等。这些 PID 算法尽管形式多样,但本质相通,只要熟练掌握基本 PID 的使用规律,无论采用何种 PID ,效果都相差无几。位置式 PID 与增量式 PID 存在一定差异,主要表现为:位置式 PID 控制器的输出是一个绝对值,意味着控制器对系统控制作用量的绝对数值大小。它通过比例项、积分项以及微分项直接算出最终的输出值。然而,增量式 PID 控制器的输出是一个增量值,也就是控制器对系统控制作用量的变化量。它依据比例增量、积分增量和微分增量来计算最终的输出增量,随后将此增量值与前一时刻的输出值相加,从而得出当前的输出值。
P(比例)参数的作用:
P 参数主要影响控制系统的响应速度和稳定性。较大的 P 值会使系统对偏差的响应更迅速,但可能导致超调量大,甚至使系统不稳定;较小的 P 值会使系统响应较慢,但能减少超调,提高系统的稳定性。
例如,在温度控制系统中,较大的 P 值会让温度快速接近设定值,但可能会超过设定值后再回调;较小的 P 值会让温度缓慢接近设定值,波动较小。
I(积分)参数的作用:
I 参数用于消除系统的稳态误差。当系统存在持续的偏差时,积分作用会不断累积,从而逐渐消除偏差。但过大的 I 值可能会导致系统响应迟缓,甚至出现积分饱和现象。
比如,在压力控制系统中,如果存在一个小的恒定压力误差,积分作用会逐渐增加输出,直到消除这个误差。
D(微分)参数的作用:
D 参数的作用是预测偏差的变化趋势,提前给出控制作用,从而抑制系统的超调,改善系统的动态性能。但 D 参数对噪声敏感,不适当的 D 值可能会使系统不稳定。
以位置控制系统为例,当物体快速接近目标位置时,微分作用会提前减小控制输出,防止位置超调。
PID基本代码
// 定义位置型PID控制器结构体
typedef struct {
float kp; // 比例增益
float ki; // 积分增益
float kd; // 微分增益
float bias; // 当前偏差
float last_bias; // 上次偏差
float integral_bias; // 积分偏差
int pwm; // PWM输出
int Reality_Position; // 当前值
int Target_Position; // 期望值
int Moto;
} PositionPIDController;
// 定义增量型PID控制器结构体
typedef struct {
float kp; // 比例增益
float ki; // 积分增益
float kd; // 微分增益
float bias; // 当前偏差
float last_bias; // 上次偏差
float prev_bias; // 上上次偏差
int pwm; // PWM输出
int Reality_Velocity; // 当前值
int Target_Velocity; // 期望值
int Moto;
} IncrementalPIDController;、
/ 位置型PID控制器计算函数
int Position_PID(PositionPIDController *pid, int reality, int target) {
pid->bias = target - reality; // 计算偏差
pid->integral_bias += pid->bias; // 偏差累积
// 积分限幅
if (pid->integral_bias > 5000) pid->integral_bias = 5000;
if (pid->integral_bias < -5000) pid->integral_bias = -5000;
// 计算PWM输出
pid->pwm = (pid->kp * pid->bias) +
(pid->ki * pid->integral_bias) +
(pid->kd * (pid->bias - pid->last_bias));
pid->last_bias = pid->bias; // 保存上次偏差
return (int)pid->pwm; // 输出结果
}
// 增量型PID控制器计算函数
int Incremental_PID(IncrementalPIDController *pid, int reality, int target) {
pid->bias = target - reality; // 计算偏差
// 计算PWM输出增量
pid->pwm += (pid->kp * (pid->bias - pid->last_bias)) +
(pid->ki * pid->bias) +
(pid->kd * (pid->bias - 2 * pid->last_bias + pid->prev_bias));
pid->prev_bias = pid->last_bias; // 保存上上次偏差
pid->last_bias = pid->bias; // 保存上次偏差
return (int)pid->pwm; // 输出结果
}
增量式PID
电机编码器速度:增量式 PID 亦可称为速度环 PID ,速度闭环控制指的是依据单位时间获取的脉冲数来测定电机的速度信息,并将其与目标值加以对比,获取控制偏差,接着通过针对偏差的比例、积分、微分实施控制,促使偏差趋向于零的流程。
void PID_Init(void)
{
V1.kp=12;
V1.Target_Velocity=10;
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM6) //10ms一次
{
//Control();
V1.Reality_Velocity=-Read_Speed(&htim5); //速度测速
V1.Moto+=Incremental_PID(&V1,V1.Reality_Velocity,V1.Target_Velocity);
Load(V1.Moto,0);
printf("Postion:%d ,%d\r\n",V1.Reality_Velocity,V1.Target_Velocity);
}
}
进入仿真后看到PID里面各个数据的值
可以看到只调节p就能让电机达到所需要的状态 只不过还是有误差需要微调
位置式PID
其实和上面差不多
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM6) //10ms一次
{
//Control();
V1.Reality_Velocity=-Read_Speed(&htim5); //速度测速
P1.Reality_Position+=V1.Reality_Velocity; //速度累加就是位置
P1.pwm= Position_PID(&P1, P1.Reality_Position, P1.Target_Position); //位置PID
Load(P1.pwm,0);
if(abs(P1.Reality_Position-P1.Target_Position)<=5) //这里牺牲精度来让电机停下
{
Load(0,0);
}
printf("Postion:%d ,%d\r\n",P1.Reality_Position,P1.Target_Position);
}
}
串级PID
串级PID就是先输入位置PID再经过速度PID,最后再输出。
void Control(){
V1.Reality_Velocity=-Read_Speed(&htim5); //速度测速
P1.Reality_Position+=V1.Reality_Velocity; //速度累加
if(abs(P1.Reality_Position-P1.Target_Position)<=5)
{
Load(0,0);
}
else{
P1.pwm= Position_PID(&P1, P1.Reality_Position, P1.Target_Position); //位置PID
P1.pwm=xian_fu(P1.pwm,V1.Target_Velocity); //位置累加给速度输入
Incremental_PID(&V1,V1.Reality_Velocity,P1.pwm);
V1.Moto+=V1.pwm;
Load(V1.Moto,0);
}
printf("%d ,%d\r\n",P1.Target_Position,P1.Reality_Position);
}
仿真参数:
一般而言,P 意味着比例,其决定着响应的快慢。I 象征积分,是对误差的累加,反映出稳定后的波动幅度。D 表示微分,代表对误差变化的趋向,能够视作一种预测。当然,一切都要以实际情形为准。
具体参数不存在固定范围。就像控制周期分别为 5 毫秒和 10 毫秒时,它们的参数并非呈两倍的对应关系。
作者:通行证