数字货币微分 【基础知识】PID(比例微分)控制

入门知识 2周前 (08-06) 12次浏览 0个评论

输出必须是影响输入的量。设计时,输出和输入不应完全独立。听上去很废话,但是很多人都在这里搞糊涂了

比例(P)

我们在这里想象一个简单的模型来理解

读者应该学过物理,我们想象一个很简单的场景

在地上,水平放置一个弹簧和一个滑块数字货币微分,弹簧原长在红线处,松开滑块(如下图1)

由于摩擦,滑块不会停留在原来的长度,它可能会停在静摩擦(干涉)和弹簧力平衡的点

在这个例子中数字货币微分,弹簧是我们的控制系统,它产生的力的大小与距离成正比,方向总是指向原来长度的位置

原始长度(红线)为设定的目标值,即比例控制

比例控制是将目标值与当前值(正或负,正表示当前值小于目标值)的差值(误差)乘以一个系数作为控制的一部分

p>

比例控制在自然现象中很常见,本质上是赋予被控对象达到目标值的动力

目标值与当前值的距离越远,给予的功率越大,使其更快达到目标值(这就是为什么不给予恒定功率的原因)

方向很重要,动力的方向一定要始终指向目标值

这里的功率在不同的控制下是不同的,在例子中是力,在温度控制中是加热和冷却(不一定是物理力)

单独使用比例控制往往达不到目标值

这是因为真实环境中存在干扰。当这个扰动等于某一点的功率时,当前值不会更接近目标值(这种误差称为稳态误差)

积分(一)

我们如何改变我们的控制系统(如上例中的弹簧),使当前值等于误差范围内的目标值?

我们可以引入错误的累积。如果由于干扰,比例控制在目标值之外的某个点停止,误差的累积会一直增加(绝对值增加)

那么这种累积会进一步推动当前值更接近目标值作为比例控制以外的驱动力

该部分的方向与刻度部分的方向相同,无需特殊设置

回到上面提到的例子

这次的弹簧变成了可以自由改变弹力的装置(也用弹簧来表示)

在这个误差累积的推动下,滑块可以来到原来的位置(目标点)

所以我们的算法迭代到第二个版本

首先是比例控制部分,每时每刻根据当前值与目标值的偏差提供动力

二阶为积分控制部分,作为二次幂,根据系统运行到现在每一刻的误差累积来补偿稳态误差

因为真实系统是一个连续状态

每一刻的累加(求和)就是积分

所以积分部分就变成了误差函数从0到此刻的定积分,即

e(t)为误差函数,表示t时刻的误差

PID算法来了,已经可以达到控制目标(将当前值稳定在设定的范围内)

导数(D)

微分部分用于优化整个控制算法

之前的算法有什么问题?

如果比例和微分控制部分的系数很大,就会出现误差过冲(即每次通过目标值都会出现很大的偏差),这两个系数不能太小

下图中横轴为时间,纵轴为当前值,虚线为目标值

微分部分用来改善这种现象

微分也叫求导,得到的值就是函数在这一点的变化率(高数)

现在问题很简单,只需将当前误差函数 e(t) 推导并乘以一个系数作为控制的一部分

总是阻断误差的趋势,即减少误差的超调

分析如下图,在初始阶段,误差(目标值-当前值)从正值迅速减小,微分项为-(积分项和比例项均为+)以降低改变,减少超调,等等。

添加微分部分会让它更丝滑(更少抖动)

PID公式

经过上面的介绍,我们了解了PID控制的原理,现在就需要实现PID了

连续状态公式

u(t)—>输出函数,PID计算后的输出给输出机构

—–>系数

e(t)—->误差值(目标值-当前值)

这个公式用于模拟电路

离散状态公式

数字电路只能处理离散数据,所以连续状态公式需要离散化

位置

知识渊博的人应该知道,整合是积累和总结,分化是找到当前的变化率

根据时间离散化,即每隔一段时间(几毫秒)进行一次PID运算,得到u(t)的值

比例部分自然不需要改变(只与位置有关,与时间无关)

积分部分,即系统运行时所有误差的总和(每隔几毫秒计算一次误差)

导数部分,本次运算与前次运算的直线(e-t相)的斜率

同时

在这个操作中是一个常数,定期调用,所以可以和系数相加

所以离散位置PID的公式如上

u(k)直接输出到执行器

e(k)是k次调用的误差(目标值-当前值)

增量

位置看起来很完美(确实如此),但还有一个问题:

如果环境发生剧烈变化或需要长期稳定运行,错误的累积值会一直增加,而电脑中的数字是有上限(范围)的,所以可能会出现一些问题

我该怎么办?

我们可以计算

,

将前面计算的结果放入输出值,

可以使用

计算输出机制的价值

这样,只有需要存储的数据不会增加到超出范围的地步

PID实现定位

先分析一波

对于函数来说,需要保存的数据(全局变量)是

(积分值)

每次调用PID函数需要传入的值(局部变量)有,Target(目标值),Current(当前值)

我们先创建一个结构体,把需要的全局变量放进去,然后创建这个结构体的全局变量

typedef struct __PID_Position_Struct
{
  float Kp, Ki, Kd;     //系数
  float Integral;    //积分(累积)
  float Error_Last1; //上次误差
} PID_Position_Struct;

PID_Position_Struct PID = {0};

每隔固定时间进行一次PID计算

/**
 * @brief 位置式PID计算
 * @param PID:PID结构体
 * @param Current:当前值
 * @param Target:目标值
 * @return 输出
 * @author HZ12138
 * @date 2022-08-05 13:07:23
 */
float PID_Position(PID_Position_Struct *PID, float Current, float Target)
{
  float err,                                                                                                        //误差
      out,                                                                                                          //输出
      differential;                                                                                                 //微分
  err = (float)Target - (float)Current;                                                                             //计算误差
  PID->Integral += err;                                                                                             //更新积分
  differential = (float)err - (float)PID->Error_Last1;                                                              //计算微分
  out = (float)PID->Kp * (float)err + (float)PID->Ki * (float)PID->Integral + (float)PID->Kd * (float)differential; //计算PID
  PID->Error_Last1 = err;                                                                                           //更新误差
  return out;
}

增量

还是先分析一波

对于函数来说,需要保存的数据(全局变量)是

(最后输出)

每次调用PID函数需要传入的值(局部变量)有,Target(目标值),Current(当前值)

我们先创建一个结构体,把需要的全局变量放进去,然后创建这个结构体的全局变量

typedef struct __PID_Increment_Struct
{
  float Kp, Ki, Kd;  //系数
  float Error_Last1; //上次误差
  float Error_Last2; //上次误差
  float Out_Last;    //上次输出
} PID_Increment_Struct;

PID_Increment_Struct PID = {0};

每隔固定时间进行一次PID计算

/**
 * @brief 增量式PID计算
 * @param PID:PID结构体
 * @param Current:当前值
 * @param Target:目标值
 * @return 输出
 * @author HZ12138
 * @date 2022-08-05 13:20:36
 */
float PID_Increment(PID_Increment_Struct *PID, float Current, float Target)
{
  float err,                                                                                                       //误差
      out,                                                                                                         //输出
      proportion,                                                                                                  //比例
      differential;                                                                                                //微分
  err = (float)Target - (float)Current;                                                                            //计算误差
  proportion = (float)err - (float)PID->Error_Last1;                                                               //计算比例项
  differential = (float)err - 2 * (float)PID->Error_Last1 + (float)PID->Error_Last2;                               //计算微分项
  out = (float)PID->Out_Last + (float)PID->Kp * proportion + (float)PID->Ki * err + (float)PID->Kd * differential; //计算PID
  PID->Error_Last2 = PID->Error_Last1;                                                                             //更新上上次误差
  PID->Error_Last1 = err;                                                                                          //更新误差
  PID->Out_Last = out;                                                                                             //更新上此输出
  return out;
}

PID级联

级联通常有两个目的

1.预期控制内容有多种(如热水器示例、水位保持位置、温度保持)

2.提高控制收敛速度(如在电机控制中加入电流PID)

串级PID

上一级的输出作为下一级的输入

例如使用角度环和速度环来控制电机位置的系统

并行PID

有多个期望输入,输出同时叠加

挖矿网Ethos中文站简单易用的挖矿系统,为挖矿产业提供教程软件以及矿机测评交易信息等,挖矿网各种数字货币挖矿收益对比计算,挖矿网介绍挖矿的工具,以及矿场的最新消息等。http://www.ethospool.com/

喜欢 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址