前言
本篇使用到的基于这个STM32CubeMX 直流电机PID速度控制、HAL库、cubemx、PID、速度控制、增量式
由于上次使用的pid没有模块化,当多出使用pid的时候就会很麻烦
所以这次使用的模块化的
一、相关PID源码
.c
/* 包含头文件 ----------------------------------------------------------------*/
#include "pid.h"
/* 私有类型定义 --------------------------------------------------------------*/
/* 私有宏定义 ----------------------------------------------------------------*/
/* 私有变量 ------------------------------------------------------------------*/
/* 扩展变量 ------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
/* 函数体 --------------------------------------------------------------------*/
void abs_limit(float *a, float ABS_MAX)// 对输入 a 进行限制,使其在 [-ABS_MAX, ABS_MAX] 区间内
{
if (*a > ABS_MAX)
*a = ABS_MAX;
if (*a < -ABS_MAX)
*a = -ABS_MAX;
}
// 初始化 PID 参数
static void pid_param_init(
pid_t* pid, // PID 控制器结构体
uint32_t mode, // PID 控制器模式
uint32_t maxout, // PID 控制器输出最大值
uint32_t intergral_limit, // PID 控制器积分限制
float kp, // PID 控制器 P 项系数
float ki, // PID 控制器 I 项系数
float kd) // PID 控制器 D 项系数
{
pid->integral_limit = intergral_limit;
pid->max_out = maxout;
pid->pid_mode = mode;
pid->p = kp;
pid->i = ki;
pid->d = kd;
}
/**
* @brief modify pid parameter when code running
* @param[in] pid: control pid struct
* @param[in] p/i/d: pid parameter
* @retval none
*/
static void pid_reset(pid_t *pid, float kp, float ki, float kd)// 重置 PID 控制器的参数
{
pid->p = kp;
pid->i = ki;
pid->d = kd;
pid->pout = 0;
pid->iout = 0;
pid->dout = 0;
pid->out = 0;
}
/**
* @brief calculate delta PID and position PID
* @param[in] pid: control pid struct
* @param[in] get: measure feedback value
* @param[in] set: target value
* @retval pid calculate output
*/
float pid_calc(pid_t *pid, float get, float set)// 计算 PID 控制器的输出
{
pid->get = get;
pid->set = set;
pid->err[NOW] = set - get;
if ((pid->input_max_err != 0) && (pid->err[NOW] > pid->input_max_err))
pid->err[NOW] = pid->input_max_err;
if ((pid->input_min_err != 0) && (pid->err[NOW] < pid->input_min_err))
pid->err[NOW] = pid->input_min_err;
if (pid->pid_mode == POSITION_PID) //position PID// 位置式 PID 控制器
{
pid->pout = pid->p * pid->err[NOW];
pid->iout += pid->i * pid->err[NOW];
pid->dout = pid->d * (pid->err[NOW] - pid->err[LAST]);
abs_limit(&(pid->iout), pid->integral_limit);
pid->out = pid->pout + pid->iout + pid->dout;
abs_limit(&(pid->out), pid->max_out);
}
else if (pid->pid_mode == DELTA_PID) //delta PID// 增量式 PID 控制器
{
pid->pout = pid->p * (pid->err[NOW] - pid->err[LAST]);
pid->iout = pid->i * pid->err[NOW];
pid->dout = pid->d * (pid->err[NOW] - 2 * pid->err[LAST] + pid->err[LLAST]);
pid->out += pid->pout + pid->iout + pid->dout;
abs_limit(&(pid->out), pid->max_out);
}
pid->err[LLAST] = pid->err[LAST];
pid->err[LAST] = pid->err[NOW];
if ((pid->output_deadband != 0) && (fabs(pid->out) < pid->output_deadband))
return 0;
else
return pid->out;
}
void pid_ClearIntegrals(pid_t* pid)// 清除积分项
{
pid->pout = 0;
pid->iout = 0;
pid->dout = 0;
pid->out = 0;
}
/**
* @brief initialize pid parameter
* @retval none
*/
void PID_struct_init( // 初始化 PID 结构体
pid_t* pid,
uint32_t mode,
uint32_t maxout,
uint32_t intergral_limit,
float kp,
float ki,
float kd)
{
pid->f_param_init = pid_param_init;
pid->f_pid_reset = pid_reset;
pid->f_pid_calc = pid_calc;
pid->f_pid_ClearIntegrals = pid_ClearIntegrals;
pid->f_param_init(pid, mode, maxout, intergral_limit, kp, ki, kd);
pid->f_pid_reset(pid, kp, ki, kd);
}
.h
#ifndef __PID_H__
#define __PID_H__
/* 包含头文件 ----------------------------------------------------------------*/
#include "stdint.h"
#include "math.h"
/* 类型定义 ------------------------------------------------------------------*/
enum
{
LLAST = 0,
LAST,
NOW,
POSITION_PID,
DELTA_PID,
};
typedef struct pid_t
{
float p;
float i;
float d;
float set;
float get;
float err[3];
float pout;
float iout;
float dout;
float out;
float input_max_err; //input max err;
float input_min_err; //input max err;
float output_deadband; //output deadband;
uint32_t pid_mode;
uint32_t max_out;
uint32_t integral_limit;
void (*f_param_init)(struct pid_t *pid,
uint32_t pid_mode,
uint32_t max_output,
uint32_t inte_limit,
float p,
float i,
float d);
void (*f_pid_reset)(struct pid_t *pid, float p, float i, float d);
float (*f_pid_calc)(struct pid_t *pid, float get, float set);
void (*f_pid_ClearIntegrals)(struct pid_t* pid);
} pid_t;
/* 宏定义 --------------------------------------------------------------------*/
/* 扩展变量 ------------------------------------------------------------------*/
/* 函数声明 ------------------------------------------------------------------*/
void PID_struct_init(
pid_t* pid,
uint32_t mode,
uint32_t maxout,
uint32_t intergral_limit,
float kp,
float ki,
float kd);
float pid_calc(pid_t *pid, float get, float set);
#endif // __PID_H__
二、如何使用
1.创建变量
在main.c或者其他位置创建pid的变量
pid_t a_moto_pid;
pid_t b_moto_pid;
2.初始化
注意一定要在pid计算之前初始化all_moto_pid_init
,不然会导致stm32硬件错误!!!!
void all_moto_pid_init(void)
{
PID_struct_init(
&a_moto_pid, // PID 控制器对象
DELTA_PID, // 控制器模式
7500, // 输出最大值
0, // 积分限制
45.0f, // P 项系数
25.0f, // I 项系数
0.0f // D 项系数
);
PID_struct_init(
&b_moto_pid, // PID 控制器对象
DELTA_PID, // 控制器模式
7500, // 输出最大值
0, // 积分限制
45.0f, // P 项系数
25.0f, // I 项系数
0.0f // D 项系数
);
}
3.运算
int a_moto_pid_calc(float current_value,float target_value)/*current_value当前值target_value目标值*/
{
// 使用 PID 控制器计算控制输出
int control_output = a_moto_pid.f_pid_calc(&a_moto_pid, current_value, target_value);
return control_output;
}
int b_moto_pid_calc(float current_value,float target_value)/*current_value当前值target_value目标值*/
{
// 使用 PID 控制器计算控制输出
int control_output = b_moto_pid.f_pid_calc(&b_moto_pid, current_value, target_value);
return control_output;
}
4.修改pid参数
/*
修改pid的值
*/
void angle_pid_set(float p,float i ,float d )
{
angle_pid.f_pid_reset(&angle_pid, p, i, d);
}
三、积分分离、变速积分的实现
积分分离
当我们在遇到一些情况的时候不希望pid带来超调,比如给小车进行位置控制的时候。
如果使用一般的pid位置式我们会知道,他会从一开始就进行积分的累加,如果我们设置积分的上限为1万,我们的目标是2千。
那么就会导致在前半段路程中的error(假设error为正)一直都比较大导致积分很快趋向饱和,当趋近2千这个目标位置的时候积分计算出来的值就过大,那必定会超过2000,此时error就变为了负,才逐渐较少积分的值。反应在车辆上就是车子先跑到2200的位置,再回1900再逐渐摆动到2000.
上述的情况我通过调节参数,p、i、d、积分上限的值都无法避免,所以可以在积分前乘以一个系数,
这个系数是个分段函数,当error的值小于设定的值的时候再进行积分,这样可以有效减少积分饱和的现象导致我们的超调。
代码上也很简单系数的分段点 #define integral_threshold 100
代码如下:
float pid_calc(pid_t *pid, float get, float set) // 计算 PID 控制器的输出
{
pid->get = get;
pid->set = set;
pid->err[NOW] = set - get;
if ((pid->input_max_err != 0) && (pid->err[NOW] > pid->input_max_err))
pid->err[NOW] = pid->input_max_err;
if ((pid->input_min_err != 0) && (pid->err[NOW] < pid->input_min_err))
pid->err[NOW] = pid->input_min_err;
float integral_factor;//这就是那个分段函数的值
#define integral_threshold 100//分段函数的分界点
// 根据误差的大小来确定积分的速度
if (fabs(pid->err[NOW]) <integral_threshold) {
// 当误差小于 integral_threshold 时,允许积分
integral_factor = 1.0;
} else {
// 当误差大于等于 integral_threshold 时,禁止积分
integral_factor = 0.0;
}
if (pid->pid_mode == POSITION_PID) // position PID// 位置式 PID 控制器
{
pid->pout = pid->p * pid->err[NOW];
pid->iout += pid->i * pid->err[NOW] * integral_factor; // 乘以积分因子@@@@@@@@@@@@
pid->dout = pid->d * (pid->err[NOW] - pid->err[LAST]);
abs_limit(&(pid->iout), pid->integral_limit);
pid->out = pid->pout + pid->iout + pid->dout;
abs_limit(&(pid->out), pid->max_out);
}
else if (pid->pid_mode == DELTA_PID) // delta PID// 增量式 PID 控制器
{
pid->pout = pid->p * (pid->err[NOW] - pid->err[LAST]);
pid->iout = pid->i * pid->err[NOW] * integral_factor; // 乘以积分因子
pid->dout = pid->d * (pid->err[NOW] - 2 * pid->err[LAST] + pid->err[LLAST]);
pid->out += pid->pout + pid->iout + pid->dout;
abs_limit(&(pid->out), pid->max_out);
}
pid->err[LLAST] = pid->err[LAST];
pid->err[LAST] = pid->err[NOW];
if ((pid->output_deadband != 0) && (fabs(pid->out) < pid->output_deadband))
return 0;
else
return pid->out;
}
文章来源:https://www.toymoban.com/news/detail-621584.html
变速积分
总结
简述一下,不喜勿喷谢谢。文章来源地址https://www.toymoban.com/news/detail-621584.html
到了这里,关于PID模块化__以stm32直流电机速度为例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!