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
}

<think>好的,用户想了解如何使用STM32实现无人机的PID控制,特别是示例代码。首先,我需要整理已有的引用内容,看看有没有相关的资料。根据提供的引用,引用[2]提到了基于STM32的无人机控制示例代码,包括PID控制、遥控器信号解析和电机控制,使用的是STM32F103系列和Keil C编译器。引用[3]也提到了软件要求和相关库,比如IMU传感器库和PWM控制库。 用户需要具体的教程和代码示例,所以应该从硬件和软件配置开始。需要列出所需的硬件组件,比如STM32主控、IMU传感器、电调、电机等。软件方面,STM32CubeIDE或Keil,以及必要的库文件。 接下来是PID控制的基本原理,这部分需要简明扼要地解释比例、积分、微分项的作用,可能还需要给出PID的结构框图或公式。然后分步骤讲解如何实现,包括初始化外设、读取传感器数据、计算PID、输出PWM信号。 代码示例部分,引用[2]提供了部分代码,比如头文件引入和PID结构体定义。需要扩展这部分,给出完整的初始化函数、PID计算函数、主循环中的处理流程。同时要注意代码的注释,让用户更容易理解。 然后需要讨论参数整定方法,比如手动调整或使用自动调参算法。此外,还要提到常见的优化方法,比如积分限幅、微分滤波等,确保系统的稳定性。 最后生成相关问题,帮助用户深入理解,比如PID参数调整方法、常见问题处理、与其他控制算法的比较等。同时检查是否有引用需要标注,比如引用[2]和[3]中的内容,确保标注正确。 需要确保回答结构清晰,分步骤讲解,代码格式正确,使用中文,并且符合用户的要求。同时要验证代码的正确性,避免语法错误,并确保PID实现的逻辑合理,比如使用定时器中断进行周期性控制。</think>### STM32无人机PID控制实现步骤 (一)硬件准备 - STM32F4/F7系列主控(推荐使用带FPU的型号) - MPU6050/BNO080等IMU传感器 - BLHeli电调及无刷电机 - PWM遥控接收机 - 3.3V稳压电路 - SWD调试接口 (二)软件配置 1. 使用STM32CubeMX生成基础工程 2. 启用以下外设: - TIM1/TIM8高级定时器(生成PWM信号) - I2C/SPI(IMU通信) - USART(调试输出) - ADC(电池电压检测) 3. 安装CMSIS-DSP库(用于浮点运算) (三)PID控制核心实现 $$ u(t) = K_p e(t) + K_i \int_{0}^{t} e(\tau) d\tau + K_d \frac{de(t)}{dt} $$ 其中$e(t)$为设定值与实际值的偏差 ```c // PID结构体定义 typedef struct { float Kp, Ki, Kd; float integral; float prev_error; float output; float dt; } PID_Controller; // PID计算函数 void PID_Update(PID_Controller* pid, float setpoint, float measurement) { float error = setpoint - measurement; pid->integral += error * pid->dt; float derivative = (error - pid->prev_error) / pid->dt; pid->output = pid->Kp * error + pid->Ki * pid->integral + pid->Kd * derivative; pid->prev_error = error; } ``` (四)姿态控制流程 1. 读取IMU数据(1000Hz中断) 2. 四元数解算姿态角 3. 遥控器信号解析(PPM/PWM解码) 4. 三轴PID计算(俯仰/横滚/偏航) 5. 混控算法输出电机PWM 6. 安全监控(过压/过流保护) (五)参数整定建议 1. 先调$K_p$至系统出现小幅震荡 2. 加入$K_d$抑制震荡 3. 最后加入$K_i$消除静差 4. 使用Ziegler-Nichols法进行初步整定 (六)优化建议 - 加入积分限幅防止windup - 对微分项进行低通滤波 - 使用串级PID结构 - 实现动态参数调整 [^1]: 引用[2]提供的代码框架 [^2]: 引用[3]的软件配置要求
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

洲洲不是州州

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

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

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

打赏作者

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

抵扣说明:

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

余额充值