【STM32】单级与串级PID控制的C语言实现

前言

笔者最近在学习PID控制器,本文基于Blog做以总结。CSDN上已有大量PID理论知识的优秀文章,因此本文将略写理论部分,重点放在应用。

PID理论

什么是PID

PID(比例-积分-微分)控制器是一种广泛用于自动控制系统的闭环反馈控制器,能够实现系统的稳定、快速和精确控制。

PID计算过程

单级PID
在这里插入图片描述
串级PID
串级PID分为外环和内环,是两个单级PID嵌套构成的

在这里插入图片描述

PID计算公式

连续型
在这里插入图片描述
离散型
在这里插入图片描述

Pout、Iout、Dout的作用

  • Pout:主要负责控制,使反馈量快速接近目标值,但可能引起振荡
  • Iout:消除稳态误差,但会增加超调
  • Dout:提供阻尼,抑制振荡和超调,但可能降低响应速度

单级PID与串级PID

我们来思考一下:为什么自动控制系统中要引入PID?能否直接控制目标值?先来看个常见的PID应用例子

例子一:水阀控制水量
在这个例子中,目标值是水槽的水量(也即水面高度,量纲为长度,单位为m)。而可直接控制的变量是水阀的开度(即水流速度,量纲为速度,单位为m/s)。
注意,控制量相较于目标值为低一阶物理量,故我们无法直接控制高阶物理量(水面高度),只能控制低阶物理量(水流速度)。
(此时因为控制量与目标值只差一阶,所以单级PID即可。假设我们不能直接控制水流速度,只能直接控制水流的加速度,那么就需要串级PID控制了)
因此引入PID控制器,为了更好的通过控制低阶物理量完成对高阶物理量的控制
ps:水面高度和水体积呈线性关系,水阀开度和水流速也是线性相关,因此通过合理调节PID参数,系统可以自适应这种内含的线性关系

例子二:循迹小车

在这个例子中,目标是控制小车位置,目标值是小车目标位置,而可直接控制的变量是电机电流(可以理解为加速度或力)。由于电流(加速度)与位置之间相差两阶,因此需要使用串级PID控制

具体做法:

  • 外环PID:根据目标位置和当前实际位置的误差,输出一个目标速度给内环PID
  • 内环PID:根据目标速度和当前实际速度的误差,输出控制量(如PWM占空比)来给执行器

这种串级PID控制方法,通过外环控制速度、调节位置,内环控制PWM、调节速度,从而实现了精准的循迹控制

PID应用

单级PID

pid.h

#ifndef __PID_H__
#define __PID_H__

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

//首先定义PID结构体用于存放一个PID的数据
typedef struct
{
   	float kp, ki, kd; //三个系数
    float error, lastError; //误差、上次误差
    float integral, maxIntegral; //积分、积分限幅
    float output, maxOutput; //输出、输出限幅
}PID;

void PID_Init(PID *pid, float p, float i, float d, float maxI, float maxOut);
void PID_Calc(PID *pid, float reference, float feedback);

#endif

pid.c

#include "pid.h"

PID speed_pid_speed = {0}; //定义并初始化一个PID结构体变量,单级速度环

//初始化pid参数的函数
void PID_Init(PID *pid, float p, float i, float d, float maxI, float maxOut)
{
    pid->kp = p;
    pid->ki = i;
    pid->kd = d;
    pid->maxIntegral = maxI;
    pid->maxOutput = maxOut;
}
 
//进行一次PID控制器的计算,更新控制量:pid->output
//参数为(pid结构体,目标值,反馈值),计算结果放在pid结构体的output成员中
void PID_Calc(PID *pid, float reference, float feedback)
{
 	//更新数据
    pid->lastError = pid->error; //将旧error存起来
    pid->error = reference - feedback; //计算新error
    //计算比例
    float pout = pid->error * pid->kp;
    //计算积分
    pid->integral += pid->error * pid->ki;
	//计算微分
    float dout = (pid->error - pid->lastError) * pid->kd;
    //积分限幅
    if(pid->integral > pid->maxIntegral) pid->integral = pid->maxIntegral;
    else if(pid->integral < -pid->maxIntegral) pid->integral = -pid->maxIntegral;
    //计算输出
    pid->output = pout+dout + pid->integral;
    //输出限幅
    if(pid->output > pid->maxOutput) pid->output =  pid->maxOutput;
    else if(pid->output < -pid->maxOutput) pid->output = -pid->maxOutput;
}

my_main.c

void setup(void)
{
	PID_Init(&speed_pid,1,0,0,0,1000);	//举例
}

void loop(void)
{
	//更新被控对象反馈值和目标值(反正要不断更新、获取
	PID_Calc(&speed_pid,targetValue,feedbackValue);
	设置执行器输出大小(speed.output);//在控制电机转速的单级速度环pid中,控制执行器的操作为set_pwm(speed.output);
	HAL_Delay(10);	//控制采样周期,防止频繁计算导致系统抖动与资源消耗过高(根据不同情况合理配置即可
}

串级PID

pid.h

#ifndef __PID_H__
#define __PID_H__

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

//首先定义单级PID结构体
typedef struct
{
   	float kp, ki, kd; //三个系数
    float error, lastError; //误差、上次误差
    float integral, maxIntegral; //积分、积分限幅
    float output, maxOutput; //输出、输出限幅
}PID;

//再定义串级PID的结构体,嵌套两个单级PID
typedef struct
{
    PID inner; //内环
    PID outer; //外环
    float output; //串级输出,等于inner.output
}CascadePID;

void PID_Init(PID *pid, float p, float i, float d, float maxI, float maxOut);
void PID_Calc(PID *pid, float reference, float feedback);
void PID_CascadeCalc(CascadePID *pid, float outerRef, float outerFdb, float innerFdb);

#endif

pid.c

//初始化pid参数的函数
void PID_Init(PID *pid, float p, float i, float d, float maxI, float maxOut)
{
    pid->kp = p;
    pid->ki = i;
    pid->kd = d;
    pid->maxIntegral = maxI;
    pid->maxOutput = maxOut;
}

//进行一次PID控制器的计算,更新控制量:pid->output
//参数为(pid结构体,目标值,反馈值),计算结果放在pid结构体的output成员中
void PID_Calc(PID *pid, float reference, float feedback)
{
 	//更新数据
    pid->lastError = pid->error; //将旧error存起来
    pid->error = reference - feedback; //计算新error
    //计算比例
    float pout = pid->error * pid->kp;
    //计算积分
    pid->integral += pid->error * pid->ki;
	//计算微分
    float dout = (pid->error - pid->lastError) * pid->kd;
    //积分限幅
    if(pid->integral > pid->maxIntegral) pid->integral = pid->maxIntegral;
    else if(pid->integral < -pid->maxIntegral) pid->integral = -pid->maxIntegral;
    //计算输出
    pid->output = pout+dout + pid->integral;
    //输出限幅
    if(pid->output > pid->maxOutput) pid->output =  pid->maxOutput;
    else if(pid->output < -pid->maxOutput) pid->output = -pid->maxOutput;
}

//串级PID的计算函数(调用两次单级PID计算,先算外环再算内环,串级cascade_pid->output被更新
//参数(串级PID结构体,外环目标值,外环反馈值,内环反馈值)
void PID_CascadeCalc(CascadePID *pid, float outerRef, float outerFdb, float innerFdb)
{
    PID_Calc(&pid->outer, outerRef, outerFdb); //先计算外环
    PID_Calc(&pid->inner, pid->outer.output, innerFdb); //再计算内环
    pid->output = pid->inner.output; //控制量 = 内环输出 = 串级PID的输出(所以
}

my_main.c
内环 PID 控制频率应高于外环,以保证内环能快速响应动态变化。建议避免内外环在同一周期更新,最好通过定时中断分别计算单级和串级 PID 控制,确保系统稳定性和响应速度

void setup(void)
{
	CascadePID cascade_pid = {0}; //创建串级PID结构体变量
 	PID_Init(&cascade_pid.inner, 10, 0, 0, 0, 1000); //初始化内环参数
    PID_Init(&cascade_pid.outer, 5, 0, 5, 0, 100); //初始化外环参数	
}

void loop(void)
{
	//更新、获取外环目标值、外环反馈值、内环反馈值
    PID_CascadeCalc(&cascade_pid, outerTarget, outerFeedback, innerFeedback); //进行串级PID计算
    设定执行机构输出大小(cascade_pid.output);	//在循迹控制中,控制执行输出为set_pwm(cascade_pid.output)
	HAL_Delay(10);	//控制采样周期,防止频繁计算导致系统抖动与资源消耗过高(根据不同情况合理配置即可    
}
  • 13
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32小车中实现串级PID控制可以通过以下步骤进行: 1. 首先,了解姿态角的串级PID控制的原理。姿态角包括俯仰角、滚转角和偏航角,它们决定了小车的运动姿态。串级PID控制器由外层角度环和内层角速度环组成,外层角度环控制姿态角的目标值,内层角速度环控制姿态角的变化速度。这种控制方式可以提高系统的响应速度和稳态精度。 2. 在STM32实现PID算法的代码。PID算法是一种常用的控制算法,它根据当前误差、误差的积分和误差的变化率来计算控制量。在实现PID算法的代码时,需要定义PID控制器的参数(比例系数、积分系数和微分系数),并根据当前的姿态角误差计算出控制量。 3. 在STM32实现串级PID算法的代码。串级PID算法是在PID算法的基础上增加了一个内层的角速度环。在实现串级PID算法的代码时,需要先计算出角度环的控制量,然后根据角速度环的目标值和当前角速度计算出角速度环的控制量。 4. 实现UCOS-III姿态控制任务。UCOS-III是一种实时操作系统,可以用于实现多任务的并发执行。在实现姿态控制任务时,可以将串级PID控制算法作为一个任务,在任务中周期性地更新姿态角和角速度,并根据计算出的控制量控制小车的运动。 通过以上步骤,就可以在STM32小车上实现串级PID控制实现对姿态角的精确控制。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* *2* *3* [STM32实现四驱小车(四)姿态控制任务——偏航角串级PID控制算法](https://blog.csdn.net/qq_30267617/article/details/113541033)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值