1、这篇文章的目的
由于本人之前做过的很多项目中,基本上都用到了pid算法,发现pid用起来是真香,不仅算法简单,控制的效果也是很不错的,基本上都能够达到要求。但是随着我做过的项目多了,发现每次做一个新的项目后,总是又回过头来找寻以前写的pid函数,然后又重复的写pid函数调节pid的参数,一次又一次在宏定义上修改这么多参数,真是太麻烦了(本人太懒了)。
既然我们不是学过c语言中的结构体和函数指针吗,能不能用pid结构体保存于pid运算有关的参数数据,用结构体直接保存函数指针,然后在调pid参数和使用函数时直接修改这个结构体,这样多方便啊。
2、整理出来的可以直接使用的函数
(1) pid.c函数
#include "pid.h"
//绝对值函数
#define ABS(x) ((x>0)? x: -x)
/*参数初始化--------------------------------------------------------------*/
static void pid_param_init(
PID_TypeDef * pid,
PID_ID id,
uint16_t maxout,
uint16_t intergral_limit,
float deadband,
uint16_t period,
int16_t max_err,
int16_t target,
float kp,
float ki,
float kd)
{
pid->id = id;
pid->ControlPeriod = period; //没用到
pid->DeadBand = deadband;
pid->IntegralLimit = intergral_limit;
pid->MaxOutput = maxout;
pid->Max_Err = max_err;
pid->target = target;
pid->kp = kp;
pid->ki = ki;
pid->kd = kd;
pid->output = 0;
}
/*中途更改参数设定--------------------------------------------------------------*/
static void pid_reset(PID_TypeDef * pid, float kp, float ki, float kd)
{
pid->kp = kp;
pid->ki = ki;
pid->kd = kd;
}
/*pid计算-----------------------------------------------------------------------*/
static float pid_calculate(PID_TypeDef* pid, float measure,int16_t target)
{
// uint32_t time,lasttime;
pid->lasttime = pid->thistime;
pid->thistime = HAL_GetTick();
pid->dtime = pid->thistime-pid->lasttime;
pid->measure = measure;
pid->target = target;
pid->last_err = pid->err;
pid->last_output = pid->output;
pid->err = pid->target - pid->measure;
//是否进入死区
if((ABS(pid->err) > pid->DeadBand))
{
pid->pout = pid->kp * pid->err;
pid->iout += (pid->ki * pid->err);
pid->dout = pid->kd * (pid->err - pid->last_err);
//积分是否超出限制
if(pid->iout > pid->IntegralLimit)
pid->iout = pid->IntegralLimit;
if(pid->iout < - pid->IntegralLimit)
pid->iout = - pid->IntegralLimit;
//pid输出和
pid->output = pid->pout + pid->iout + pid->dout;
//选择是否滤波?
//pid->output = pid->output*0.7f + pid->last_output*0.3f;
//输出限幅
if(pid->output>pid->MaxOutput)
{
pid->output = pid->MaxOutput;
}
if(pid->output < -(pid->MaxOutput))
{
pid->output = -(pid->MaxOutput);
}
}
return pid->output;
}
/*pid结构体初始化,每一个pid参数需要调用一次--------------------------------------*/
void pid_init(PID_TypeDef* pid)
{
pid->f_param_init = pid_param_init;
pid->f_pid_reset = pid_reset;
pid->f_cal_pid = pid_calculate;
}
(2)pid.h函数
#ifndef _PID_H
#define _PID_H
#include "stdint.h"
//PID优化功能枚举
typedef enum
{
PID_Position,
PID_Speed
}PID_ID;
typedef struct _PID_TypeDef
{
PID_ID id;
float target; //目标值
float lastNoneZeroTarget;
float kp;
float ki;
float kd;
float measure; //测量值
float err; //误差
float last_err; //上次误差
float pout;
float iout;
float dout;
float output; //本次输出
float last_output; //上次输出
float MaxOutput; //输出限幅
float IntegralLimit; //积分限幅
float DeadBand; //死区(绝对值)
float ControlPeriod; //控制周期
float Max_Err; //最大误差
uint32_t thistime;
uint32_t lasttime;
uint8_t dtime;
void (*f_param_init)(
struct _PID_TypeDef *pid, //PID参数初始化
PID_ID id,
uint16_t maxOutput,
uint16_t integralLimit,
float deadband,
uint16_t controlPeriod,
int16_t max_err,
int16_t target,
float kp,
float ki,
float kd
);
//pid三个参数修改
void (*f_pid_reset)(struct _PID_TypeDef *pid, float kp,float ki, float kd);
//pid计算
float (*f_cal_pid)(struct _PID_TypeDef *pid, float measure);
}PID_TypeDef;
void pid_init(PID_TypeDef* pid);
#endif
(3)pid函数的使用
#include "pid.h"
//有几个电机就用几个pid结构体
PID_TypeDef motor_pid[4];
//初始化PID参数
static void motor_init(void)
{
for(int i=0; i<4; i++)
{
pid_init(&motor_pid[i]);
motor_pid[i].f_param_init(&motor_pid[i],PID_Speed,16384,5000,10,0,8000,0,1.5,0.1,0);
//类型id,输出限幅,积分限幅,绝对值,控制周期,最大误差,目标值, p, i, d
}
}
//对四个电机同时进行pid计算
static void motor_control(int32_t set_spd1,int32_t set_spd2,int32_t set_spd3,int32_t set_spd4)
{
//motor_pid[0].target = set_spd1;
motor_pid[0].f_cal_pid(&motor_pid[0],now_speed_1,set_spd1);//PID计算
motor_pid[1].f_cal_pid(&motor_pid[1],now_speed_2,set_spd2);//PID计算
motor_pid[2].f_cal_pid(&motor_pid[2],now_speed_3,set_spd3);//PID计算
motor_pid[3].f_cal_pid(&motor_pid[3],now_speed_4,set_spd4);//PID计算
//将PID的计算结果 motor_pid[0].output 控制电机
osDelay(10); //PID控制频率100HZ
}