PID控制算法:8、Arduino PID 库讲解

news/2024/9/18 6:36:09/

Arduino PID 库讲解

First Pass – Initial Input and Proportional-Mode selection

/*working variables*/
unsigned long lastTime;
double Input, Output, Setpoint;
double ITerm, lastInput;
double kp, ki, kd;
int SampleTime = 1000; //1 sec
double outMin, outMax;
bool inAuto = false;#define MANUAL 0
#define AUTOMATIC 1#define DIRECT 0
#define REVERSE 1
int controllerDirection = DIRECT;#define P_ON_M 0
#define P_ON_E 1
bool pOnE = true;
double initInput;void Compute()
{if(!inAuto) return;unsigned long now = millis();int timeChange = (now - lastTime);if(timeChange>=SampleTime){/*Compute all the working error variables*/double error = Setpoint - Input;ITerm+= (ki * error);if(ITerm > outMax) ITerm= outMax;else if(ITerm < outMin) ITerm= outMin;double dInput = (Input - lastInput);/*Compute P-Term*/if(pOnE) Output = kp * error;else Output = -kp * (Input-initInput);/*Compute Rest of PID Output*/Output += ITerm - kd * dInput;if(Output > outMax) Output = outMax;else if(Output < outMin) Output = outMin;/*Remember some variables for next time*/lastInput = Input;lastTime = now;}
}void SetTunings(double Kp, double Ki, double Kd, int pOn)
{if (Kp<0 || Ki<0|| Kd<0) return;pOnE = pOn == P_ON_E;double SampleTimeInSec = ((double)SampleTime)/1000;kp = Kp;ki = Ki * SampleTimeInSec;kd = Kd / SampleTimeInSec;if(controllerDirection ==REVERSE){kp = (0 - kp);ki = (0 - ki);kd = (0 - kd);}
}void SetSampleTime(int NewSampleTime)
{if (NewSampleTime > 0){double ratio  = (double)NewSampleTime/ (double)SampleTime;ki *= ratio;kd /= ratio;SampleTime = (unsigned long)NewSampleTime;}
}void SetOutputLimits(double Min, double Max)
{if(Min > Max) return;outMin = Min;outMax = Max;if(Output > outMax) Output = outMax;else if(Output < outMin) Output = outMin;if(ITerm > outMax) ITerm= outMax;else if(ITerm < outMin) ITerm= outMin;
}void SetMode(int Mode)
{bool newAuto = (Mode == AUTOMATIC);if(newAuto == !inAuto){  /*we just went from manual to auto*/Initialize();}inAuto = newAuto;
}void Initialize()
{lastInput = Input;initInput = Input;ITerm = Output;if(ITerm > outMax) ITerm= outMax;else if(ITerm < outMin) ITerm= outMin;
}void SetControllerDirection(int Direction)
{controllerDirection = Direction;
}

随着输入的变化,“测量比例”会提供越来越大的阻碍,但是如果没有参考系,我们的性能会有些不稳定。 如果第一次打开控制器时PID输入为10000,我们真的要开始以Kp * 10000进行抵抗吗? 否。我们希望将初始输入用作参考点(第108行),随着输入从此处更改(第38行),增加或减少阻力。
我们需要做的另一件事是允许用户选择是否要按比例进行误差或测量。 在上一篇文章之后,PonE似乎毫无用处,但重要的是要记住,对于许多循环来说,它运作良好。 因此,我们需要让用户选择他们想要的模式(第51和55行),然后在计算中采取相应的行动(第37和38行)。

Second Pass – On-the-fly tuning changes动态调整

尽管上面的代码确实可以工作,但是它有一个我们之前已经看到的问题。 在运行时更改调整参数时,会出现不希望出现的现象

为什么会这样呢?

我们上次看到此结果的原因是,积分是通过新的Ki重新缩放的。 这次是(Input – initInput)由Kp重新缩放。 我选择的解决方案类似于我对Ki所做的解决方案:我没有将Input – initInput视为一个整体单元乘以当前Kp的方法,而是将其分解为各个步骤,然后分别乘以Kp:

/*working variables*/
unsigned long lastTime;
double Input, Output, Setpoint;
double ITerm, lastInput;
double kp, ki, kd;
int SampleTime = 1000; //1 sec
double outMin, outMax;
bool inAuto = false;#define MANUAL 0
#define AUTOMATIC 1#define DIRECT 0
#define REVERSE 1
int controllerDirection = DIRECT;#define P_ON_M 0
#define P_ON_E 1
bool pOnE = true;
double PTerm;void Compute()
{if(!inAuto) return;unsigned long now = millis();int timeChange = (now - lastTime);if(timeChange>=SampleTime){/*Compute all the working error variables*/double error = Setpoint - Input;ITerm+= (ki * error);if(ITerm > outMax) ITerm= outMax;else if(ITerm < outMin) ITerm= outMin;double dInput = (Input - lastInput);/*Compute P-Term*/if(pOnE) Output = kp * error;else{PTerm -= kp * dInput;Output = PTerm;}/*Compute Rest of PID Output*/Output += ITerm - kd * dInput;if(Output > outMax) Output = outMax;else if(Output < outMin) Output = outMin;/*Remember some variables for next time*/lastInput = Input;lastTime = now;}
}void SetTunings(double Kp, double Ki, double Kd, int pOn)
{if (Kp<0 || Ki<0|| Kd<0) return;pOnE = pOn == P_ON_E;double SampleTimeInSec = ((double)SampleTime)/1000;kp = Kp;ki = Ki * SampleTimeInSec;kd = Kd / SampleTimeInSec;if(controllerDirection ==REVERSE){kp = (0 - kp);ki = (0 - ki);kd = (0 - kd);}
}void SetSampleTime(int NewSampleTime)
{if (NewSampleTime > 0){double ratio  = (double)NewSampleTime/ (double)SampleTime;ki *= ratio;kd /= ratio;SampleTime = (unsigned long)NewSampleTime;}
}void SetOutputLimits(double Min, double Max)
{if(Min > Max) return;outMin = Min;outMax = Max;if(Output > outMax) Output = outMax;else if(Output < outMin) Output = outMin;if(ITerm > outMax) ITerm= outMax;else if(ITerm < outMin) ITerm= outMin;
}void SetMode(int Mode)
{bool newAuto = (Mode == AUTOMATIC);if(newAuto == !inAuto){  /*we just went from manual to auto*/Initialize();}inAuto = newAuto;
}void Initialize()
{lastInput = Input;PTerm = 0;ITerm = Output;if(ITerm > outMax) ITerm= outMax;else if(ITerm < outMin) ITerm= outMin;
}void SetControllerDirection(int Direction)
{controllerDirection = Direction;
}

:20,39-43,114为改动行

现在,我们保留一个有效的总和PTerm,而不是将Input-initInput的全部乘以Kp。 在每一步中,我们仅将当前输入更改乘以Kp并从PTerm中减去(第41行。)在这里,我们可以看到更改的影响:

由于旧的Kps位于“银行”中,因此调整参数的变化只会影响我们向前迈进

Final Pass – Sum problems.

关于上述代码的错误,我不会详细介绍(流行趋势等)。很好,但是仍然存在重大问题。例如:

  1. 结束时间:虽然最终输出限制在outMin和outMax之间,但PTerm可能会在不希望的时候增加。它不会像完整的缠绕那样糟糕,但是仍然不能被接受
  2. 即时更改:如果用户在运行时从P_ON_M更改为P_ON_E,则一段时间后返回,则不会对PTerm进行属性初始化,这会导致输出颠簸

还有更多,但仅这些就足以了解真正的问题是什么。在创建ITerm之前,我们已经处理了所有这些问题。我决定不为PTerm重新执行相同的解决方案,而是决定使用一种更美观的解决方案。
通过将PTerm和ITerm合并到一个称为“ outputSum”的变量中,P_ON_M代码将从已存在的所有ITerm修复中受益,并且由于代码中没有两个和,因此没有不必要的冗余。

/*working variables*/
unsigned long lastTime;
double Input, Output, Setpoint;
double outputSum, lastInput;
double kp, ki, kd;
int SampleTime = 1000; //1 sec
double outMin, outMax;
bool inAuto = false;#define MANUAL 0
#define AUTOMATIC 1#define DIRECT 0
#define REVERSE 1
int controllerDirection = DIRECT;#define P_ON_M 0
#define P_ON_E 1
bool pOnE = true;void Compute()
{if(!inAuto) return;unsigned long now = millis();int timeChange = (now - lastTime);if(timeChange>=SampleTime){/*Compute all the working error variables*/double error = Setpoint - Input;double dInput = (Input - lastInput);outputSum+= (ki * error);/*Add Proportional on Measurement, if P_ON_M is specified*/if(!pOnE) outputSum-= kp * dInputif(outputSum > outMax) outputSum= outMax;else if(outputSum < outMin) outputSum= outMin;/*Add Proportional on Error, if P_ON_E is specified*/if(pOnE) Output = kp * error;else Output = 0;/*Compute Rest of PID Output*/Output += outputSum - kd * dInput;if(Output > outMax) Output = outMax;else if(Output < outMin) Output = outMin;/*Remember some variables for next time*/lastInput = Input;lastTime = now;}
}void SetTunings(double Kp, double Ki, double Kd, int pOn)
{if (Kp<0 || Ki<0|| Kd<0) return;pOnE = pOn == P_ON_E;double SampleTimeInSec = ((double)SampleTime)/1000;kp = Kp;ki = Ki * SampleTimeInSec;kd = Kd / SampleTimeInSec;if(controllerDirection ==REVERSE){kp = (0 - kp);ki = (0 - ki);kd = (0 - kd);}
}void SetSampleTime(int NewSampleTime)
{if (NewSampleTime > 0){double ratio  = (double)NewSampleTime/ (double)SampleTime;ki *= ratio;kd /= ratio;SampleTime = (unsigned long)NewSampleTime;}
}void SetOutputLimits(double Min, double Max)
{if(Min > Max) return;outMin = Min;outMax = Max;if(Output > outMax) Output = outMax;else if(Output < outMin) Output = outMin;if(outputSum > outMax) outputSum= outMax;else if(outputSum < outMin) outputSum= outMin;
}void SetMode(int Mode)
{bool newAuto = (Mode == AUTOMATIC);if(newAuto == !inAuto){  /*we just went from manual to auto*/Initialize();}inAuto = newAuto;
}void Initialize()
{lastInput = Input;outputSum = Output;if(outputSum > outMax) outputSum= outMax;else if(outputSum < outMin) outputSum= outMin;
}void SetControllerDirection(int Direction)
{controllerDirection = Direction;
}

那里有。 以上功能是Arduino PID v1.2.0中提供的功能。

But wait, there’s more: Setpoint Weighting.

我没有在Arduino库代码中添加以下内容,但这是一项功能,如果您想自己动手,可能会感兴趣。 设定点加权是其核心,是同时拥有PonE和PonM的一种方法。 通过指定0到1之间的比率,您可以分别具有100%PonM,100%PonE或两者之间的某个比率。 如果您的流程集成度不理想(例如回流炉),并且对此有所考虑,这可能会有所帮助。
最终,我决定此时不将其添加到库中,因为它最终成为要调整/解释的另一个参数,而且我认为由此带来的好处是不值得的。 无论如何,这是代码,如果您想修改代码以具有设定值权重,而不仅仅是选择PonM / PonE:

/*working variables*/
unsigned long lastTime;
double Input, Output, Setpoint;
double outputSum, lastInput;
double kp, ki, kd;
int SampleTime = 1000; //1 sec
double outMin, outMax;
bool inAuto = false;#define MANUAL 0
#define AUTOMATIC 1#define DIRECT 0
#define REVERSE 1
int controllerDirection = DIRECT;#define P_ON_M 0
#define P_ON_E 1
bool pOnE = true, pOnM = false;
double pOnEKp, pOnMKp;void Compute()
{if(!inAuto) return;unsigned long now = millis();int timeChange = (now - lastTime);if(timeChange>=SampleTime){/*Compute all the working error variables*/double error = Setpoint - Input;double dInput = (Input - lastInput);outputSum+= (ki * error);/*Add Proportional on Measurement, if P_ON_M is specified*/if(pOnM) outputSum-= pOnMKp * dInputif(outputSum > outMax) outputSum= outMax;else if(outputSum < outMin) outputSum= outMin;/*Add Proportional on Error, if P_ON_E is specified*/if(pOnE) Output = pOnEKp * error;else Output = 0;/*Compute Rest of PID Output*/Output += outputSum - kd * dInput;if(Output > outMax) Output = outMax;else if(Output < outMin) Output = outMin;/*Remember some variables for next time*/lastInput = Input;lastTime = now;}
}void SetTunings(double Kp, double Ki, double Kd, double pOn)
{if (Kp<0 || Ki<0|| Kd<0 || pOn<0 || pOn>1) return;pOnE = pOn>0; //some p on error is desired;pOnM = pOn<1; //some p on measurement is desired;double SampleTimeInSec = ((double)SampleTime)/1000;kp = Kp;ki = Ki * SampleTimeInSec;kd = Kd / SampleTimeInSec;if(controllerDirection ==REVERSE){kp = (0 - kp);ki = (0 - ki);kd = (0 - kd);}pOnEKp = pOn * kp;pOnMKp = (1 - pOn) * kp;
}void SetSampleTime(int NewSampleTime)
{if (NewSampleTime > 0){double ratio  = (double)NewSampleTime/ (double)SampleTime;ki *= ratio;kd /= ratio;SampleTime = (unsigned long)NewSampleTime;}
}void SetOutputLimits(double Min, double Max)
{if(Min > Max) return;outMin = Min;outMax = Max;if(Output > outMax) Output = outMax;else if(Output < outMin) Output = outMin;if(outputSum > outMax) outputSum= outMax;else if(outputSum < outMin) outputSum= outMin;
}void SetMode(int Mode)
{bool newAuto = (Mode == AUTOMATIC);if(newAuto == !inAuto){  /*we just went from manual to auto*/Initialize();}inAuto = newAuto;
}void Initialize()
{lastInput = Input;outputSum = Output;if(outputSum > outMax) outputSum= outMax;else if(outputSum < outMin) outputSum= outMin;
}void SetControllerDirection(int Direction)
{controllerDirection = Direction;
}

现在,pOn不再是将pOn设置为整数,而是以允许允许比率的双精度形式出现(第58行)。除了一些标志(第62和63行)以外,还在第77-78行计算了加权Kp项。 然后,在第37和43行上,将加权的PonM和PonE贡献添加到总体PID输出中。


http://www.ppmy.cn/news/546790.html

相关文章

ipad刷机的时候,突然中断了怎么处理好

对于经常玩手机、ipad用户来说&#xff0c;像刷机这个词&#xff0c;相信大家并不陌生&#xff0c;刷机通常是&#xff0c;手机或者ipad用得卡的时候&#xff0c;还有就是想越狱&#xff0c;当然&#xff0c;刷机的过程可能会有突然情况&#xff0c;就是中断了&#xff0c;那这…

刷机一直请求shsh_爱思助手刷机过程里提示请求SHSH失败的处理方法

Android版iPhone版PC版 当前很多网友会使用到爱思助手软件来刷机&#xff0c;而最近有网友在刷机过程里遇到了提示请求SHSH失败的情况&#xff0c;怎么办呢&#xff1f;请看下文爱思助手刷机过程里提示请求SHSH失败的处理教程。 问题&#xff1a;iPhoneXS Max手机系统想刷机到i…

刷机一直请求shsh_爱思助手刷机过程里提示请求SHSH失败的处理教程

当前很多网友会使用到爱思助手软件来刷机&#xff0c;而最近有网友在刷机过程里遇到了提示请求SHSH失败的情况&#xff0c;怎么办呢&#xff1f;接下来小编就带各位了解文爱思助手刷机过程里提示请求SHSH失败的处理教程吧。 问题&#xff1a;iPhoneXS Max手机系统想刷机到iOS12…

爱思服务器shsh文件类型,爱思助手SHSH怎么备份 爱思助手SHSH备份教程

爱思助手SHSH怎么备份&#xff1f;之前有很多用户提过这个问题&#xff0c;今天小编就通过这篇文章给大家讲讲操作教程&#xff0c;非常简单&#xff0c;一起来看吧&#xff01; 类别&#xff1a;手机工具 大小&#xff1a;123.18M 语言&#xff1a;简体中文 评分&#xf…

刷机一直请求shsh_爱思助手里刷机提示请求SHSH失败的处理教程

各位使用爱思助手的同学们&#xff0c;你们知道爱思助手刷机时提示请求SHSH失败如何处理呢?在这篇教程内小编就为各位呈现了爱思助手里刷机提示请求SHSH失败的处理教程。 爱思助手里刷机提示请求SHSH失败的处理教程 问题&#xff1a;iPhoneXS Max系统想刷机到iOS12.2正式版&am…

保姆级系统降级教程,适用于iPhone13

准备电脑一台&#xff0c;数据线&#xff0c;高于50%电量的iPhone 1&#xff0c;电脑端打开此网址&#xff0c;找到iPhone13pro那一列&#xff0c;下载固件15.6rc&#xff0c;下载过程中可以进行以下步骤&#xff0c;网址因为等级原因&#xff0c;酷安限制发布&#xff0c;欢迎…

爱思助手从苹果服务器shsh失败,爱思助手无法提取SHSH降级iOS6.1.2固件教程

iOS6.1.2降级方法&#xff1a; 1、你的手机在cydia如果有显示相应固件的SHSH&#xff0c;那用itools的SHSH保存功能&#xff0c;将SHSH保存到电脑。此时&#xff0c;itools虽然已经帮你保存SHSH到电脑&#xff0c;但是itools保存的SHSH爱思助手不认&#xff0c;如果之前你没用爱…

windows下插入u盘怎么使用qt进行读取_【技术篇】Windows系统使用爱思助手制作越狱 U 盘教程...

CheckRa1n 越狱工具需要在 macOS 系统上进行安装,借助电脑端爱思助手制作越狱 U 盘后,即可在 Windows 电脑上使用 CheckRa1n 进行越狱。 支持设备:iPhone 5S - iPhone X 兼容系统:iOS 12.3 及以上 U盘购买链接:$cwEp1zZR4ua$(也可自行从某宝购买) 爱思助手下载链接:http:…

爱思助手(i4助手) v5.21 官方版

爱思助手(i4助手) v5.21 官方版 软件大小&#xff1a;46MB 软件语言&#xff1a;简体中文 软件类别&#xff1a;手机工具 软件授权&#xff1a;官方版 更新时间&#xff1a;2015-02-14 应用平台&#xff1a;/Win8/Win7/Vista/WinXP 爱思助手(i4助手)是首款集一键查询shsh、一键…

打开ssh通道是什么意思_如何使用爱思助手打开SSH通道和关闭SSH通道?

爱思助手是全球首款集一键查询shsh、一键备份shsh、一键刷机功能于一身的全自动傻瓜式刷机软件,关于爱思助手教程小编也给大家分享了很多,下面小编要给大家分享的就是如何使用爱思助手"打开SSH通道"和"关闭SSH通道"?在介绍具体方法之前,我们先来了解一…

爱思助手(i4助手) v5.08 官方版​

爱思助手(i4助手) v5.08 官方版 软件大小&#xff1a;38.3MB 软件语言&#xff1a;简体中文 软件类别&#xff1a;手机工具 软件授权&#xff1a;官方版 更新时间&#xff1a;2015-01-29 应用平台&#xff1a;/Win8/Win7/Vista/WinXP 爱思助手(i4助手)是首款集一键查询shsh、一…

大数据企业开发全套流程

大数据企业开发基础流程 Linux命令 1 Hadoop(HDFSYarn)单机版环境搭建 Hadoop 是一个开源的分布式计算框架&#xff0c;由 HDFS&#xff08;Hadoop Distributed File System&#xff09;和 YARN&#xff08;Yet Another Resource Negotiator&#xff09;两个核心组件组成。HD…

itunes刷机一直正在恢复固件要多久_ios刷机报错故障汇总指南

ios刷机报错故障汇总指南 苹果刷机一般有两类刷机软件:一是苹果官方软件 iTunes,二是第三方软件,比如爱思助手。 iTunes整体操作比较麻烦 ,以下内容说一下爱思助手恢复模式刷机报错的几大故障码及解决方法(关于怎么进入恢复模式/DFU模式下一期做详细教程) 刷机卡60%报错未…

使用爱思助手刷机和越狱

转至&#xff1a;http://www.chinamac.com/iphoneipad/mac21376.html http://www.educity.cn/help/651014.html 感谢作者&#xff0c;支持原创 先说刷机&#xff1a; 2、在连接手机之前先将手机的密码取消&#xff0c;否则读不了信息&#xff0c;将手机与电脑连接显示下图&am…

支持断点续传的大文件传输协议

从1971年A.K.Bhushan提出第一个FTP协议版本&#xff08;RFC114&#xff09;到现在&#xff0c;人们对FTP的应用已经历了40余年的时间&#xff0c;同时&#xff0c;许多基于FTP协议的数据传输软件也应运而生。如Windows操作系统下经常使用的支持FTP协议的软件有&#xff1a;Cute…

java上传超大文件解决方案

用JAVA实现大文件上传及显示进度信息 ---解析HTTP MultiPart协议 (本文提供全部源码下载&#xff0c;请访问 https://github.com/1269085759/up6-jsp-mysql) 一. 大文件上传基础描述&#xff1a; 各种WEB框架中&#xff0c;对于浏览器上传文件的请求&#xff0c;都有自己的处理…

Netty学习之路一(大文件传输案例分析)

业务场景&#xff1a; 由于工作需要&#xff0c;需要在两台服务器的java服务之间通过netty建立链接&#xff0c;将大文件&#xff08;几百G到TB级别&#xff09;从机器A上的serverA发送到机器B上的serverB。 实现方法设计&#xff1a; 系统现有的实现方法&#xff1a;将业务方…

大文件传输(gofastdfs)

简介&#xff1a; 1、使用的是go-fast&#xff0c;支持大文件传输。 2、参考的资料&#xff1a;https://gitee.com/sjqzhang/go-fastdfs/blob/master/README-en.md 3、下载fileserver.exe 4、找到“断点续传示例”,点击“更多客户端请参考”&#xff0c;下载“tus-java-client”…

如何进行大文件传输?

本文首发微信公众号&#xff1a;码上观世界 网络文件传输的应用场景很多&#xff0c;如网络聊天的点对点传输、文件同步网盘的上传与下载、文件上传到分布式文件存储器等&#xff0c;其传输速度主要受限于网络带宽、存储器大小、CPU处理速度以及磁盘读写速度&#xff0c;尤其是…