PID算法理论,运用,代码编写详解

这篇具有很好参考价值的文章主要介绍了PID算法理论,运用,代码编写详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

什么是PID

我相信能来看这篇文章的应该都知道什么是PID,PID就是一种控制算法,利用比例运算(P),积分运算(I)和微分运算(D)一起控制某一事件,当然也可以只运用其中一个也可以两两结合。

运用举例:

比如我们家里都会有的那个电热水器,有点热水器会有那个保温功能,假如我们设定的温度保持在60度。一开始热水器开始工作P发现此时水温距离目标温度还差的多就控制加热棒输出较大功率快速加热。随着温度越来越高,P会发现距离目标温度越来越近,输出功率也越来越小,但由于“惯性”实际温度会超过目标温度,此时P就会输出负功率,但实际中没有负功率,我们可以启动制冷,但又会由于惯性温度会低于目标温度,就这样实际温度在目标温度附近徘徊。
pid代码,算法,PID,算法,网络,stm32,c语言
显然这虽然能达到目标,但是不是很理想。于是就引入了D。D会发现当快要到目标温度时温度会上升比较慢(此时温度与目标温度的差值和上次温度和目标温度的差值比较),就表示快要到达目标温度了D就会给出信号以减小功率的输出。但是可能会出现下面这种情况,温度维持在目标温度附近。
pid代码,算法,PID,算法,网络,stm32,c语言
这种虽然温度不会大幅跳动了但是没有到达我们设定的目标温度,此时就引入了I。I的作用就是在一段时间内它发现温度一直没有到达目标温度,此时它就增大输出功率进行加热。
以上就是PID算法在实际的一个应用,那他的一个底层数学逻辑是怎么样的勒?

底层数学逻辑的实现

接下来我们就讨论一下他的一个底层数学逻辑是怎么实现的。我们先来看看他的一个公式:
pid代码,算法,PID,算法,网络,stm32,c语言
可以看出P就是简单的比例运算,I就是积分运算,D就是微分运算;
P:比例运算很简单我就不做阐述了;
I:积分运算,积分两点之间的积分,为两点连线,和两点对x轴的垂线,以及两垂足之间的线段所组成的梯形的面积。
pid代码,算法,PID,算法,网络,stm32,c语言因为我们要编写这个代码,所以就得对其离散化理解。
pid代码,算法,PID,算法,网络,stm32,c语言
D:微分运算,微分就是导线某点的切线斜率。
pid代码,算法,PID,算法,网络,stm32,c语言以上就是PID运算的一个数学理解。我们可以上面的总结一下得到如下的公式:
pid代码,算法,PID,算法,网络,stm32,c语言

其实上面讲解的这个PID也叫位置式PID,还有一种PID是 增量式PID。而增量式PID就是两次位置式PID相减。将两次位置式PID相减得到

pid代码,算法,PID,算法,网络,stm32,c语言

区别
1、增量式只与最近三次的误差有关,运算速度快且无累积误差,而位置式需要对误差进行积分,容易产生累积误差且运算速度慢。
2、增量式的结果是输出量的变化量,即便出现误差影响不会太大,而位置式的结果是输出量,一旦出现错误影响会很大。

C语言实现

我们先用VisualStudio C语言编辑器编写代码验证,代码如下:

#include "stdio.h"
#include "windows.h"


typedef  unsigned char uint8_t;
typedef  unsigned int uint32_t;

//限幅输出预处理命令
#define Astrict_Out(input,min,max)	{  		\
	if(input < min)					\
		input = min;				\
	else if(input >max)				\
		input =max;					\
}

enum PID_MODE
{
	PID_position = 0,   //位置式PID
	PID_increment      //增量式PID
};

typedef struct
{
	uint8_t mode; //PID运行模式
	float Kp;
	float Ki;
	float Kd;

	float max_Out;  //PID最大输出
	float min_Out;  //PID最大输出
	//float max_I_Out;  //PID最大积分输出

	float set_num;  //PID目标值
	float new_num;  //PID当前值

	float All_Out;  //三项叠加输出
	float P_Out;  //比例输出
	float I_Out;  //积分输出
	float D_Out;  //微分输出

	//微分项最近三个值,0最新,1上一次,2上上次
	float D_buf[3];  

	//误差项最近三个值 
	float Err_buf[3];
}PID_struct;

/*
PID初始化函数
参数:PID_struct *pid:PID结构体;
uint8_t mode:选择PID运算模式 0:位置式PID;  1:增量式PID
const float PID[3]:PID系数,Kp,Ki,Kd;
float max_out:PID最大输出值
float min_out:PID最小输出值
*/
void PID_Init(PID_struct *pid,uint8_t mode,const float PID[3],float max_out, float min_out)
{
	if (pid == NULL || PID == NULL)
		return;
	pid->mode = mode;
	pid->Kp = PID[0];
	pid->Ki = PID[1];
	pid->Kp = PID[2];
	pid->max_Out = max_out;
	pid->min_Out = min_out;
	pid->D_buf[0] = pid->D_buf[1] = pid->D_buf[2] = 0.0f;
	pid->Err_buf[0] = pid->Err_buf[1] = pid->Err_buf[2] = pid->P_Out = pid->I_Out = pid->D_Out = 0.0f;
}

/*
PID运算函数
参数:
PID_struct* pid:PID结构体
float new_num:新的数值(比如最新采集到的温度)
float set_num:目标温度
返回值:
PID运算结果
*/
float PID_calc(PID_struct* pid, float new_num, float set_num)
{
	if (pid == NULL || pid == NULL)
		return 0.0f;
	 
	//存放过去两次计算的误差
	pid->Err_buf[2] = pid->Err_buf[1];
	pid->Err_buf[1] = pid->Err_buf[0];
	//设定目标值和当前值
	pid->set_num = set_num;
	pid->new_num = new_num;
	//计算最新误差
	pid->Err_buf[0] = set_num - new_num; //目标值减去最新值

	//判断PID模式
	if (pid->mode == PID_position)  //位置式
	{
		pid->P_Out = pid->Kp * pid->Err_buf[0]; //比例计算
		pid->I_Out += pid->Ki * pid->Err_buf[0];  //积分计算
		//存放过去两次计算的微分误差值
		pid->D_buf[2] = pid->D_buf[1];
		pid->D_buf[1] = pid->D_buf[0];
		//当前误差的微分用本次误差减去上一次误差来计算
		pid->D_buf[0] = pid->Err_buf[0] - pid->Err_buf[1];
		pid->D_Out = pid->Kd * pid->D_buf[0];  //微分输出

		pid->All_Out += pid->P_Out + pid->I_Out + pid->D_Out;  //叠加输出

		Astrict_Out(pid->All_Out, pid->min_Out, pid->max_Out);
	}
	else if(pid->mode == PID_increment)  //增量式PID
	{
		//以本次误差与上次误差的差值作为比例项带入计算
		pid->P_Out = pid->Kp * (pid->Err_buf[0]-pid->Err_buf[1]);
		//以本次误差作为积分项带入计算
		pid->I_Out = pid->Ki * pid->Err_buf[0];
		//迭代微分学的数组
		pid->D_buf[2] = pid->D_buf[1];
		pid->D_buf[1] = pid->D_buf[0];
		//以本次误差与上次误差的差值减去上次误差与上上次误差的差值作为微分项的输入带入计算 \
			Err_buf[0]-Err_buf[1] - (Err_buf[1]-Err_buf[2])
		pid->D_buf[0] = (pid->Err_buf[0] - 2.0f * pid->Err_buf[1] + pid->Err_buf[2]);
		pid->D_Out = pid->Kd * pid->D_buf[0];

		//三项叠加
		pid->All_Out += pid->P_Out + pid->I_Out + pid->D_Out;
		Astrict_Out(pid->All_Out, pid->min_Out, pid->max_Out);
	}
	return pid->All_Out;
}

/*
PID中间数值清除函数
*/
void PID_clear(PID_struct* pid)
{
	if (pid == NULL)
		return;
	//清除当前误差
	pid->Err_buf[0] = pid->Err_buf[1] = pid->Err_buf[2] = 0.0f;
	//微分项清零
	pid->D_buf[0] = pid->D_buf[1] = pid->D_buf[2] = 0.0f;
	//输出清零
	pid->All_Out = pid->P_Out = pid->I_Out = pid->D_Out = 0.0f;
	//目标值和当前值清零
	pid->set_num = pid->new_num = 0.0f;
}

PID_struct pid1;
int main(void)
{	
	int i =0;
	float n = 0;
	const float pid_data[3] = {11,0.5,1};  //初始化PID的系数,Kp,Ki,Kd
	PID_Init(&pid1, 1, pid_data, 100, 0);  //PID初始化
	scanf("%f",&n);  //通过键盘输入数值模拟单片机采集到的信息,如温度
	for (i = 0; i < 50; i++)
	{
		PID_calc(&pid1, n, 90);
		Sleep(200);
		printf("%f\n",pid1.All_Out);   //输出PID运算结果,用于控制,如改变PWM的占空比。
		scanf("%f",&n);
	}
	return 0;
}

我们通过键盘输入模拟数据采集进行PID运算
pid代码,算法,PID,算法,网络,stm32,c语言可以看到效果可以。
然后我们一直到STM32上面:

//.c文件
#include "pid.h"

/*
PID初始化函数
参数:PID_struct *pid:PID结构体;		
uint8_t mode:选择PID运算模式 0:位置式PID;  1:增量式PID
const float PID[3]:PID系数,Kp,Ki,Kd;
float max_out:PID最大输出值
float min_out:PID最小输出值
*/
void PID_Init(PID_struct *pid,uint8_t mode,const float PID[3],\
								float max_out, float min_out)
{
	if (pid == NULL || PID == NULL)
		return;
	pid->mode = mode;  
	pid->Kp = PID[0];
	pid->Ki = PID[1];
	pid->Kp = PID[2];
	pid->max_Out = max_out;
	pid->min_Out = min_out;
	pid->D_buf[0] = pid->D_buf[1] = pid->D_buf[2] = 0.0f;
	pid->Err_buf[0] = pid->Err_buf[1] = pid->Err_buf[2] \
							= pid->P_Out = pid->I_Out = pid->D_Out = 0.0f;
}

/*
PID运算函数
参数:
PID_struct* pid:PID结构体
float new_num:新的数值(比如最新采集到的温度)
float set_num:目标温度
返回值:
PID运算结果
*/
float PID_calc(PID_struct* pid, float new_num, float set_num)
{
	if (pid == NULL || pid == NULL)
		return 0.0f;
	 
	//存放过去两次计算的误差
	pid->Err_buf[2] = pid->Err_buf[1];
	pid->Err_buf[1] = pid->Err_buf[0];
	//设定目标值和当前值
	pid->set_num = set_num;
	pid->new_num = new_num;
	//计算最新误差
	pid->Err_buf[0] = set_num - new_num; //目标值减去最新值

	//判断PID模式
	if (pid->mode == PID_position)  //位置式
	{
		pid->P_Out = pid->Kp * pid->Err_buf[0]; //比例计算
		pid->I_Out += pid->Ki * pid->Err_buf[0];  //积分计算
		//存放过去两次计算的微分误差值
		pid->D_buf[2] = pid->D_buf[1];
		pid->D_buf[1] = pid->D_buf[0];
		//当前误差的微分用本次误差减去上一次误差来计算
		pid->D_buf[0] = pid->Err_buf[0] - pid->Err_buf[1];
		pid->D_Out = pid->Kd * pid->D_buf[0];  //微分输出

		pid->All_Out += pid->P_Out + pid->I_Out + pid->D_Out;  //叠加输出

		Astrict_Out(pid->All_Out, pid->min_Out, pid->max_Out);
	}
	else if(pid->mode == PID_increment)  //增量式PID
	{
		//以本次误差与上次误差的差值作为比例项带入计算
		pid->P_Out = pid->Kp * (pid->Err_buf[0]-pid->Err_buf[1]);
		//以本次误差作为积分项带入计算
		pid->I_Out = pid->Ki * pid->Err_buf[0];
		//迭代微分学的数组
		pid->D_buf[2] = pid->D_buf[1];
		pid->D_buf[1] = pid->D_buf[0];
		//以本次误差与上次误差的差值减去上次误差与上上次误差的差值作为微分项的输入带入计算 \
			Err_buf[0]-Err_buf[1] - (Err_buf[1]-Err_buf[2])
		pid->D_buf[0] = (pid->Err_buf[0] - 2.0f * pid->Err_buf[1] + pid->Err_buf[2]);
		pid->D_Out = pid->Kd * pid->D_buf[0];

		//三项叠加
		pid->All_Out += pid->P_Out + pid->I_Out + pid->D_Out;
		Astrict_Out(pid->All_Out, pid->min_Out, pid->max_Out);
	}
	return pid->All_Out;
}

/*
PID中间数值清除函数
*/
void PID_clear(PID_struct* pid)
{
	if (pid == NULL)
		return;
	//清除当前误差
	pid->Err_buf[0] = pid->Err_buf[1] = pid->Err_buf[2] = 0.0f;
	//微分项清零
	pid->D_buf[0] = pid->D_buf[1] = pid->D_buf[2] = 0.0f;
	//输出清零
	pid->All_Out = pid->P_Out = pid->I_Out = pid->D_Out = 0.0f;
	//目标值和当前值清零
	pid->set_num = pid->new_num = 0.0f;
}

//.h文件
#ifndef __PID_H
#define __PID_H

#include "main.h"

//限幅输出预处理命令
#define Astrict_Out(input,min,max)	{  	\
									if(input < min)				\
										input = min;				\
									else if(input >max)		\
										input =max;					\
								}

enum PID_MODE  //PID模式枚举变量
{
	PID_position = 0,   //位置式PID
	PID_increment      //增量式PID
};

typedef struct
{
	uint8_t mode; //PID运行模式
	float Kp;
	float Ki;
	float Kd;

	float max_Out;  //PID最大输出
	float min_Out;  //PID最大输出

	float set_num;  //PID目标值
	float new_num;  //PID当前值

	float All_Out;  //三项叠加输出
	float P_Out;  //比例输出
	float I_Out;  //积分输出
	float D_Out;  //微分输出

	//微分项最近三个值,0最新,1上一次,2上上次
	float D_buf[3];  
	float Err_buf[3];  	//误差项最近三个值 
}PID_struct;

void PID_Init(PID_struct *pid,uint8_t mode,const float PID[3],\
								float max_out, float min_out);
float PID_calc(PID_struct* pid, float new_num, float set_num);
void PID_clear(PID_struct* pid);

#endif


以上就是我结合其他博主的理解总结我自己对PID算法的理解。文章来源地址https://www.toymoban.com/news/detail-580179.html

到了这里,关于PID算法理论,运用,代码编写详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • PID控制算法详解

    PID 即 Proportional(比例),Integral(积分),Differential(微分)的英文缩写。顾名思义,PID 控制算法是结合比例,积分和微分三种环节于一体的自动控制算法,它是连续系统中技术最为成熟,应用最为广泛的一种控制算法,该控制算法出现于 20 世纪 30 至 40 年代,适用于对被

    2024年02月02日
    浏览(65)
  • PID控制算法代码,供大家参考

    分享一个C语言PID控制器代码: 【最后一个bug】多平台都有更新和发布,大家可以一键三连,关注+星标,不错过精彩内容~~

    2024年02月16日
    浏览(42)
  • PID算法详解及实例分析

    PID算法算是控制领域最经典,最重要,也是最实用的算法了。所谓的PID,指的是proportion,integration,differentiation,比例,积分,微分。 因此,PID是结合了比例积分微分三个模块于一身的控制算法。 先看公式: u ( t ) = K p ( e ( t ) + 1 T i ∫ 0 t e ( t ) d t + T d d e ( t ) d t ) u(t) = K_p

    2024年01月21日
    浏览(41)
  • 【智能车】模糊PID控制原理详解与代码实现

    本文主要由三部分构成:模糊PID控制器的原理,模糊PID控制器C++的实现与测试。 模糊PID控制流程如下图所示,把目标值 Xtarget 与输出值 Xout 的误差 e 与 e 的变化率 de/dt 作为模糊控制器的输入,模糊控制器先对输入进行模糊化处理,接着进行模糊推理,最后把模糊推理的结果

    2024年02月02日
    浏览(39)
  • 浅谈BP神经网络PID控制算法及matlab仿真

    本文是对BP神经网络PID控制算法的数学描述及仿真实验,若有错误之处,欢迎指正! 老规矩不废话,直接上链接 BP神经网络维基百科 BP神经网络是人工神经网络中的一种常用结构,其由输入层(input)-隐含层(hidding)-输出层三层构成(output)。 上图中, B 1 B1 B 1 是输入层, B 2 B2 B

    2024年02月05日
    浏览(39)
  • 增强型PID-自适应-前馈-神经网络控制研究(Matlab代码实现)

    💥💥💞💞 欢迎来到本博客 ❤️❤️💥💥 🏆博主优势: 🌞🌞🌞 博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️ 座右铭: 行百里者,半于九十。 📋📋📋 本文目录如下: 🎁🎁🎁 目录 💥1 概述 📚2 运行结果 2.1 RBFNN_Optimized_hideen_node_20 ​2.2 RBFNN_Lat

    2024年02月01日
    浏览(53)
  • STM32应用(十)经典控制算法PID(单级和串级)原理与代码实现

    PID是什么,P,I,D的分别功能 你和PID调参大神之间,就差这篇文章! P(比例): 简单来说,P就是凉了加热水,热了加凉水。 比目标值小 ,我就 增加 一点, 比目标值大 ,我就 减小 一点。 (现在) P可能出现的问题: 1.P太小,达到目标值需要花费很长的时间,而且会有

    2024年02月03日
    浏览(45)
  • 【MATLAB数据处理实用案例详解(22)】——基于BP神经网络的PID参数整定

    基于BP神经网络的PID控制的系统结构如下图所示: 考虑仿真对象,输入为r(k)=1.0,输入层为4,隐藏层为5,输出层为3,仿真输出满足 a ( k ) = 1.2 ( 1 − 0.8 e x p ( − 0.1 k ) ) , y ( k ) = a ( k ) y − 1 1 + ( y − 1 ) 2 + u − 1 a(k)=1.2(1-0.8exp(-0.1k)),y(k)=a(k) frac{y-1}{1+(y-1)^2}+u-1 a ( k ) = 1.2 ( 1 −

    2024年02月07日
    浏览(59)
  • PID算法(位置式pid算法和增量式pid算法)

    PID 算法是闭环控制系统中常用的算法,PID 分别是 Proportion(比例)、Integral(积分)、 Differential(微分)的首字母缩写。它是一种结合比例、积分和微分三个环节于一体的闭环控 制算法,具体的控制流程如图 8.2.1 所示: u = Kp * e u ----- 输出 Kp ----- 比例系数 e ----- 偏差 以温度

    2024年02月16日
    浏览(57)
  • 智能车竞赛模糊PID过程详解,附matlab模拟代码,使用的C代码在我的另一篇文章中

    目录 普通位置式PID控制  模糊PID控制 区间划分 模糊化 清晰化 改进 模糊PID的MATLAB代码 模糊PID的m测试使用文件,可一步步运行了解详细过程 模糊PID的主函数和功能函数matlab代码 模糊PID的使用和调参技巧 PID 控制分为比例,微分,积分三项,其公式如下: U (t) = Kp ∗ err (t)

    2024年04月16日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包