STM32—PID结构体使用讲解

文章介绍了如何利用C语言的结构体和函数指针来优化PID算法的实现,通过创建PID结构体保存参数并简化函数调用,以提高代码复用性和便捷性。文中提供了一组可以直接使用的PID函数,并展示了如何初始化和应用这些函数来控制多个电机。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 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
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

洲洲不是州州

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值