STM32单片机开发(6).离散PID公式:位置式PID 增量式PID

ops/2025/2/24 5:18:43/

连续形式的 PID 公式里有连续形式的积分和微分操作——这些操作直接放在单片机里,是不太好写程序的,连续形式 PID 需要用模拟电路来实现——模拟电路时时刻刻都在工作着。而对于单片机而言,我们只能使用离散形式 PID ,离散后,单片机需要设定一个调控周期 T 单片机每隔 T 时间才会进行一次 PID 计算和调控,单片机里的 PID 调控是间隔进行的,因此本节的内容就是,将这个连续形式的 PID 离散化,然后用单片机的 C 语言来实现离散 PID 算法程序的设计。

一、相关背景

在讲之前,不知道大家有没有这样的疑惑呀,就是既然我们最终需要离散形式的 PID ,那为啥还要先学习连续形式的 PID 公式呢(见:STM32单片机开发(5).PID公式:比例项P、积分项I、微分项D_pid正反作用的偏差计算-CSDN博客)?直接把离散形式 PID 当做基本公式讲不是更直接吗?何必绕弯子先学习连续PID公式再讲离散PID公式,最后只用离散呢?

其实都是按照历史发展来学习的,我们知道最开始电子世界是模拟电路的天下,这个 PID 公式设计的时候,身边也都是模拟电路,自然设计者在设计 PID 的时候,就只能用模拟电路的方式来设计。

使用模拟电路来进行加、减、乘、除等运算时,基本都会用到运算放大器这样一个软件,简称运放,而运放的基本运算电路就加法器(含减法器)、乘法器(即放大器,含除法器)、反向器(取负)、积分器和微分器——因此,为什么 PID 公式里会用到积分和微分操作呢,可能就是因为积分和微分都是一种基本的运放电路,身边的素材就只有这么多,设计的时候必然会用到这些素材对吧,因此 PID 公式里包含积分和微分的操作,就不奇怪了,而且为什么 PID 基本公式都是连续形式的,我们也不奇怪了,因为 PID 诞生在一个模拟世界,模拟电路也是可以实现 PID 公式的,这个叫做模拟 PID ,大家感兴趣的话可以研究一下。

然而,随着数字电路的发展,尤其是可编程的单片机等微控制器的出现,电路设计的便捷性和灵活性被极大提高了,我们的电子世界也正式进入数字时代,为了完成这样的转变,许多之前模拟电路的公式都要进行离散化,以转移到数字电路和单片机中,所以这也对应了PID的学习流程,先学模拟,再离散化,再在单片机写程序实现,为什么是这个流程,因为历史发展是这样的,至于用模拟电路的方法实现 PID ,那已经被历史淘汰了,现在很少见到,其实我们学习其他知识点的时候,比如傅立叶变换,比如数字滤波器,也都是先学连续的模拟形式,再学离散化的数字形式,为什么课程这么安排,实际上也和历史发展有关,好,历史背景就这些,大概给大家介绍一下,接下来我们正式进入本节课程。

二、离散PID公式理解

首先我们的基本公式是连续形式PID:

这个公式就是之前介绍的 PID 基本公式,而且这个公式是连续形式,接下来我们把这个公式进行离散化,得到下面这个离散形式:

这是离散形式 PID 的基本公式,其中out(k)的 k 取值是:0、1、2……,表示的是第几次调控,out(k)就表示第k次调控时的输出值,error(k) 就表示第k次调控时当时的误差值,T 是离散后的调控周期,也就是系统每隔 T 时间执行一次PID调控算法,根据新的误差输出一次新的驱动。error(j)是每一次的误差,j 从零变到 k,意思就是把第 k 次调控之前所有的 error 累加起来,这里error(j)中的 j 是累加时候的临时变量,类似于 C 语言 FOR 循环for(i=0;i<k;i++) 里的 i 这个意思,为了避免和微分的 i 冲突,这里取名就叫做 j。最后 error(k) 是本次误差,error(k-1) 表示的是上一次的误差,那么为什么离散化之后是这个公式呢 这里 I 项和 D 项,为什么多了一个调控周期 T 呢,而又为什么I项是乘 T,D 项是除 T 呢?下面来逐项分析。

2.1 离散 P项

首先, P 项进行离散,这个最简单,原来 P 项是 Kp 乘 error(t) ,表示 Kp 乘 t 时刻的误差,现在是 Kp 乘 error(k) ,表示 Kp 乘第 k 次调控时刻的误差,这个转变很简单。

2.2 离散 I项

然后是 I 项,原来是 Ki 乘 error(t) 从零到 t 时刻的积分,现在是这么一串,为什么?我们看下面这个图,这是积分和微分离散化示意图啊,左边是积分示意图,横轴表示时间,纵轴表示误差的变化,绿色线是连续形式的误差曲线,然后横轴每个数字表示 第k次调控,其中每次调控的时间间隔都是 T,T 就是调控周期。

我们举个例子来理解积分累加的关系,比如说我要求 0 到 k=8 这一段时间的积分,对于连续形式就是绿色曲线这样,然后积分就曲线下的面积,所以 T 从0~8T 这段时间的面积就是这一部分,那么对应在离散形式下这一片区域的面积,它就约等于所有蓝色矩形的面积之和,且 T 越小时,这个约等于就越精确,那么所有蓝色矩形的面积之和,就下面这个计算公式,这就类似于我们 C 语言的遍历:

 for(j=0;j<k;j++) sum += error(j); 

因为 T 是一个定值啊,所以 T 写在求和符号里面还是外面都一样,好,经过这么一分析,这里 error(t) 从零到 t 的积分,离散之后是 t 倍的errorr(j) ,j从0到k的累加,为什么是这样的,以及为什么多个乘 T ,大家应该理解了吧。

2.3 离散 D项

最后看 D 项,为什么离散后是这个公式,我们继续看上面的示意图,某点的微分表示的就是求该点切线的斜率。 

我们再举两个例子分析一下,首先, derror(5T)/dt表示求 5T 时刻的微分,也就是求上图 k 等于5时刻绿线切线的斜率,此时绿线的斜率在离散的情况下,我们要借助上一次的误差值近似求——向后差商法(数值计算分析、多项式插值……等场景中会用到),也就是在 error(5)和 error(4)之间连一条线,这里用红线表示啊,这个红线近似为 k=5 时刻的切线,且 T 越小时这个近似越精确,由此下面写的约等于红色线的斜率,那最后一步就求红色线的斜率,斜率怎么求,就是 dy/dx 对吧,这里画了两条蓝线 dy 就是 error(5)-error(4),dx 就是 T ,因此斜率是 error(5)-error(4)/T,error(5)比 error(4)大,此时斜率是正值,对应图示红线也没问题。

最后再举个例子,求 8T 时刻的微分,同样的步骤,在 error(8)和 error(7)之间连线,用紫线表示,这个微分约等于紫色线的斜率,斜率也是 dy 除 dx ,最终等于 error(8)-error(7)/T,error(8)比 error(7)小,此时斜率是负值,对应图示紫线也没问题。

好,理解了这两个例子,再看这个微分离散的公式,derror(t)/dt 离散后是 error(k)-error(k-1) ,即本次误差-上次误差,再除 T ,为什么是这样的,以及为什么要多除个 T ,大家应该能理解吧,那到这里,我们这个连续形式PID离散化的任务就完成了。

2.4 参数整定

其中注意积分和微分离散后多带了个 T ,意思就是积分和微分离散后,和这个调控时间 T 是有关的,最后 T 其实是个常数,Ki和 Kd 也是常数,为了让公式更简洁,我们可以把 T 合并到 Ki 和 Kd 中,这样就得到了最后一个公式:

最终我们程序会用这个更简洁的公式来写,但是别忘了,这个新的 Ki其中包含了个乘 T ,新的 Kd 其中包含了个除 T ,这个意思是,当我们在修改调控周期 T 时,Ki 和 Kd 参数也要对应更改,如果你想实现修改 T 而 Ki 和 Kd 不变的效果,那么你也可以用之前那个公式,在公式中表达出调控周期 T ,这样在修改调控周期时,程序只需要修改 T ,而不必再修改 Ki 和 Kd 了,这也是一个不错的方案,至于使用哪个方案好,那就看你的喜好和经验。

方案1显示调控周期T,更稳定;

方案2隐藏调控周期T,更简洁——推荐使用方案1。

三、位置式PID & 增量式PID

3.1 位置式PID

可以看出来,这个位置PID公式和前面参数整定后的公式完全一样,为什么呢?因为将连续形式 PID 公式直接离散后,得到的公式就叫做位置式 PID

位置式 PID 的名称,是相对于增量式 PID 而言的,另外,大家千万不要被这个位置式的位置给迷惑了,有的人可能会想,位置式 PID 是不是只能控制位置啊,增量式 PID 是不是只能控制速度啊,这种想法是完全错误的,我们看了位置式 PID 就是连续 PID 直接离散得到的最直接、最通用的公式,可以说离散形式的 PID 基本公式就是位置式 PID ,这个位置式PID ,可以覆盖几乎所有需要用到 PID 的场景:速度控制、位置控制、温度控制、姿态控制……等等都可以,所以说不要纠结什么时候用位置式 PID ,什么时候用增量式 PID ,其实大多数场景用哪种形式 PID 都行,有时候觉得这里取名叫做位置式 PID ,感觉不太合适,不如叫做直接式 PID,或者叫做全量式 PID 这样理解起来不会引起误会。

位置式PID:在不同时间位置上计算,这种离散方式得到的离散PID公式(将连续的时间离散成一段一段的时间)叫作位置式PID公式(在不同时间位置上,得到PID调控的输出)。

3.2 增量式PID

然后现在的离散 PID ,还有一种比较流行的公式,就是增量式 PID ,增量式 PID 是由位置式 PID 变换而来的,我们看增量式 PID 公式是如何得到的呢——第k次输出值out(k)相比于上一次输出值的增量△out(k) :

然后观察这个公式,我们发现增量式的 P 项和位置式的 D 项很类似,增量式的 I 项和位置式的 P 项很类似,千万别把这两个公式里的每一项搞混了,之后再观察增量式 PID 公式,我们发现等号右边没有任何的求和符号,全是最基础的加减乘除,计算过程非常清晰,并且其中只出现了error(k)、error(k-1)和error(k-2),这三个输入变量,因此我们可以有一个结论——增量式 PID 输出的结果△out(k),只是本次、上次、上上次这三次 error 的线性组合

如果把增量式 PID 看成一个模块,那么给这个模块输入本次、上次、上上次的 error ,模块直接就能给出 PID 输出值的增量,且模块内不需要有任何记忆或者累加单元(即不需要新定义变量),好,这是我们观察公式初步看出来的内容,那么为什么要有增量式 PAD 呢?增量式 PID 和位置式 PID 有什么区别呢?

3.3 增量式PID 与 位置式PID的区别

这里这是位置式 PID 与增量式 PID 的比较。

我们刚才推导公式的时候已经明白了 连续 PID ,离散之后是离散 PID,而这个直接得到的离散 PID 就是位置式 PID,或者叫做全量式 PID ,因为它每次计算得到的 是全量的输出值,这个全量输出值可以作为调控力度,直接作用给大多数的执行机构,比如说直流电机 PWM 的占空比、加热装置的加热功率……等等,因此位置式 PID 就是离散 PID 的基本格式,位置式 PID 适合大部分场景,只要你需要用单片机实现 PID ,那么你优先考虑 位置是 PID 就可以了,不必纠结,速度控制、温度控制、位置控制、姿态控制……等等,位置式 PID 都可以完成。

两位置式PID公式做减,就得到了增量式 PID 公式,所以增量式 PAD 的输出值是△OUT ,也就是这里说的是输出值的增量,这个值一般情况下不能直接赋值给被控对象的执行机构啊,除非这个被控对象内部自带有积分的功能,比如说驱动步进电机或者阀门控制,这些场景可以使用增量式 PID 举个例子,比如说阀门控制-位置式 PID ,说阀门状态30%、阀门状态50%,阀门状态40%等等, 位置式 PAD 每次告诉完整的阀门状态,而增量式 PID 说阀门加大5%、阀门加大8%,阀门减小2%等等,增量式 PID 每次告诉阀门状态改变的大小,这个改变值是基于上一次的阀门状态进行的,因此如果被控制对象不能记住上一次的状态,或者不能实现在上次状态的基础之上加减调节的功能,那么这个被控对象就不能直接接收输出值的增量

3.4 增量式PID 与 位置式PID的联系

当然其实位置式 PID 也可以实现输出增量的效果,就是我直接把上一次输入值存起来,然后本次计算后,用本次的输入值减上次输入值,是不是结果也是输出值的增量啊,只不过是这样的话,位置式 PID 就多走了一些冤枉路,因为在公式上可以直接作差进行简化,而不必计算出两个全量的数值,再作差得到增量,因此当执行机构需要增量时,我们可以直接使用增量式 PID ,这样计算会得到简化。增量式 PID ,计算结果是输出值的增量,一般不能直接复制给执行机构,但是我们可以在控制器内进积分,然后输出积分后的结果,意思就是在控制器里定义一个变量 OUT ,每次计算得到增量后,执行 OUT +=△ OUT 这样就能实现全量输出,之后 out 变量就可以作为调控力度输出给执行机构,当我们这样处理后,此时增量式 PID 与位置是 PID 整体功能没有区别,因为现在他们输出的都是全量的输出值,整体功能没有区别啊,使用哪个都一样,所以有些增量式 PID ,输出值确实只有增量,这样的增量式 PID ,你要确认一下被控对象是否能接收增量;但有些增量式 PID 它在内部进行了积分,这时候它输出的其实是全量,内部积分的增量式 PID 和位置式 PID 整体功能没有区别,这两种增量式 PID 大家要注意区分一下。 当然整体功能虽然没有区别,但是它们内部的计算细节还是不太一样的。

最后一条这里不同的特性有很多啊,比如说位置式 PID 的中间变量是误差积分,可以单独进行积分限幅,防止积分饱和,内部积分的增量式 PID 没法单独对积分环节限幅啊,换一种说法就是内部积分的增量式 PID 只需要对输出值限幅就可以同时实现积分限幅和输出线幅两个功能,而位置是 PID 输出限幅和积分限幅得分开进行, 另外位置式 PID 每次计算得到的是全量输出值,如果信号有噪声干扰,不同输入值可能会相差很大,这会导致执行机构大幅度变化,而增量式 PID 或者内部积分的增量式 PID ,都可以单独对输出值增量(△out)进行限幅,防止执行机构大幅度变化 …… 关于这两种 PID 更多的区别啊,大家可以在实践中慢慢感受,这里我就不再多说了,接下来我们就正式进入 C 语言代码部分,离散的公式我们已经有了,之后就来看一下代码实现的思路啊。


http://www.ppmy.cn/ops/160889.html

相关文章

【C语言】指针(6)

前言&#xff1a;上期我们介绍了回调函数的实现&#xff0c;qsort快速排序函数的使用以及qsort的模拟实现。这期我们主要通过一些小练习和面试题来巩固之前所学的知识。 往期文章&#xff1a; 指针1 指针2 指针3 指针4 指针5 文章目录 一&#xff0c;sizeof和strlen的辨析…

三星Galaxy S24系列手机被曝3月推送One UI 7稳定版更新

在智能手机的激烈竞争中&#xff0c;系统更新往往是决定用户体验的关键因素之一。2025年2月22日&#xff0c;一则振奋人心的消息在科技圈传开&#xff1a;三星计划于今年3月&#xff0c;向Galaxy Z Fold6、Galaxy Z Flip6折叠手机以及Galaxy S24系列推送One UI 7稳定版更新。这…

DeepSeek 助力 Vue 开发:打造丝滑的导航栏(Navbar)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

Windows辉煌的发展历程

2021年6月24号晚上11点&#xff0c;微软正式发布了下一代Windows操作系统——Windows 11。但在惊叹于Windows 11的精妙之余&#xff0c;我们不妨先先回顾一下windows的发展进程&#xff0c;看看在全新的Windows 11中&#xff0c;我们能看到那些熟悉的影子。 一切的开端&#x…

k8s学习记录(二):Pod基础篇

一、前言 上一篇文章中&#xff0c;我们对于k8s有了初步的认识&#xff0c;学习了k8s的架构&#xff08;Master-Worker&#xff09;&#xff0c;同时也简单的了解了k8s中比较重要的的几个组件&#xff0c;Pod、ReplicaSet、Deployment、Service等等&#xff08;当然了还有更多…

Java多线程三:补充知识

精心整理了最新的面试资料&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 Lambda表达式 简介&#xff1a; 希腊字母表中排序第十一位的字母&#xff0c;英语名称为Lambda避免匿名内部类定义过多其实质属于函数式编程的概念 为什么要使用lam…

51单片机学习之旅——在LCD1602上显示时钟

新建工程 打开软件 LCD1602模块代码添加 因为我们在LCD1602上显示时钟&#xff0c;因此我们需要添加LCD1602的模块代码 跳转到这条博客51单片机学习之旅——模块化编程集_51单片机ruminant-CSDN博客&#xff0c;复制相关代码跳转到这条博客51单片机学习之旅——模块化编程集…

web的分离不分离:前后端分离与不分离全面分析

让我们一起走向未来 &#x1f393;作者简介&#xff1a;全栈领域优质创作者 &#x1f310;个人主页&#xff1a;百锦再新空间代码工作室 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[1504566…