PID c++算法学习和实现

原理
五种PID积分抗饱和(ANTI-Windup)方法
梯形积分PID控制器的实现
原理图:
在这里插入图片描述

u ( t ) = K p ∗ e ( t ) + K i ∗ ∫ 0 t e ( t ) d t + K d ∗ d e ( t ) d t u(t)=K_p*e(t)+K_i*\int^t_0e(t)dt+K_d*\dfrac{de(t)}{dt} u(t)=Kpe(t)+Ki0te(t)dt+Kddtde(t)
(1)位置式PID
位置式PID:
1:当前系统的实际位置,与你想要达到的预期位置的偏差, 2:进行PID控制,误差会一直累加,会使当前输出与过去的所有输入相关,输入uk出错,会导致系统大幅波动 3:位置式PID在积分项达到饱和时,误差仍然会在积分作用下继续累积,一旦误差开始反向变化,系统需要一定时间从饱和区退出,所以在u(k)达到最大和最小时,要停止积分作用,并且要有积分限幅和输出限幅, 4:用位置式PID时,一般我们直接使用PD控制,不使用积分项

实际应用中,用差分代替微分,连加代替积分,也就是离散型PID
令:
u ( t ) = u ( k ) ; u(t)=u(k); u(t)=u(k);
e ( t ) = e ( k ) ; e(t)=e(k); e(t)=e(k);
∫ 0 t e ( t ) d t = ∑ j = 0 k T ∗ e ( j ) ; \int_0^te(t)dt=\sum_{j=0}^kT*e(j); 0te(t)dt=j=0kTe(j);
d e ( t ) d t = e ( k ) − e ( k − 1 ) T \dfrac{de(t)}{dt}=\dfrac{e(k)-e(k-1)}{T} dtde(t)=Te(k)e(k1)
公式:
u ( k ) = K p ∗ e ( k ) + k i ∗ ∑ j = 0 k e ( j ) ∗ d t + K d ∗ ( e ( k ) − e ( k − 1 ) ) u(k)=K_p*e(k)+k_i*\sum_{j=0}^ke(j)*dt+K_d*(e(k)-e(k-1)) u(k)=Kpe(k)+kij=0ke(j)dt+Kd(e(k)e(k1))
(1)实现:位置模式PID

#include <math.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include "matplotlibcpp.h"
#include <vector>
#include <math.h>
#include <string>
#include<stdlib.h>
namespace plt = matplotlibcpp;


class pid_p
{
private:
    float ki;
    float kp;
    float kd;
    float ek;
    float ek_1;
    float actual;
    float de;
    float target;
    float yk;
public:
    pid_p();
    ~pid_p();
    pid_p(float p,float i,float d);
    void get_error();
    
    void get_value(float act,float tar);
    float update();
};

pid_p::pid_p():kp(0),ki(0),kd(0),ek(0),ek_1(0),de(0),actual(0),yk(0)
{
}
pid_p::pid_p(float p,float i,float d):ek(0),ek_1(0),de(0),actual(0),yk(0)
{
    kp=p;
    ki=i;
    kd=d;
}

pid_p::~pid_p()
{
}
void pid_p::get_value(float act,float tar)
{
    actual=act;
    target=tar;
    get_error();
    printf("actual:%f,target%f",actual,target);
}
void pid_p::get_error()
{
    ek=target-actual;
}
float pid_p::update()
{
    de+=ek;
    yk=kp*ek+ki*de+kd*(ek-ek_1);
    printf("p:%f,i:%f,d:%f,act:%f,yk:%f,ek:%f\r\n",kp,ki,kd,actual,yk,ek);
    ek_1=ek;
    return yk;

}

//输入三个参数kp,ki,kd
int main(int argc,char ** argv)
{   float target=1000;
    std::string str_p=argv[1];
    std::string str_i=argv[2];
    std::string str_d=argv[3];
    // std::string str_p="0.35";
    // std::string str_i="0.0001";
    // std::string str_d="0.0001";
    float act=0;int N=100;
   
    float kp=atof(str_p.c_str());
    float ki=atof(str_i.c_str());
    float kd=atof(str_d.c_str());
    
    pid_p a(kp,ki,kd);
    std::vector<float> x,y;
    for (int i=0;i<N;i++)
    {
        x.push_back(i);
        y.push_back(act);
        a.get_value(act,target);
        act+=a.update();
        a.pid_printf();
        //if(act>target)break;
    }
    plt::plot(x,y);
    plt::show();
}


(2)增量式PID
原理:使控制器输出为增量,尽量使每次数据均与过去数据无关,没有积分项。
公式:

e ( t ) = e ( k ) ; e(t)=e(k); e(t)=e(k);
Δ u = u ( k ) − u ( k − 1 ) = K p ∗ ( e k − e k − 1 ) + k i ∗ ( e k − 2 ∗ e k − 1 + e k − 2 ) \Delta{u}=u(k)-u(k-1) =K_p*(e_k-e_{k-1})+k_i*(e_k-2*e_{k-1}+e_{k-2}) Δu=u(k)u(k1)=Kp(ekek1)+ki(ek2ek1+ek2)
实现

	class pid_add
{
    private:
        float kp,ki,kd,ek,ek_1,ek_2,uk,yk,delta_u;
    public:
        pid_add();
        pid_add(float p,float i,float d);
        void get_value(float act,float tar);
        void update_error();
        float update();

};
pid_add::pid_add():kp(0),ki(0),kd(0),uk(0),ek(0),ek_1(0),yk(0)
{

};

pid_add::pid_add(float p,float i,float d):uk(0),ek(0),ek_1(0),yk(0)
{
    kp=p;
    ki=i;
    kd=d;
};

void pid_add::get_value(float act,float tar)
{
    uk=act;
    yk=tar;
}
void pid_add::update_error()
{
    ek_1=ek;
    ek=yk-uk;
}

float pid_add::update()
{
    update_error();
    delta_u=kp*(ek-ek_1)+ki*ek+kd*(ek-2*ek_1+ek_2);
    return delta_u;
}

(3) 积分分离式PID

原理:在系统误差较大时,取消积分环节;当误差较小时,引入积分环节。这样既不影响控制器的动态性能,又可以提高控制器的稳态精
实现:在位置式/增量式PID加入积分环节一个阈值,实现略

(4) 抗饱和积分式PID
原理:在计算U(k)的时候,先判断上一时刻的控制量U(k-1)是否已经超出了限制范围。若U(k-1)>Umax,则只累加负偏差;若U(k-1)<Umin,则只累加正偏差。从而避免控制量长时间停留在饱和区。
实现:

class pid_antisaturation
{
    private:
        float kp,ki,kd,uk,uk_1,yk,ek,ek_1,ek_2;
        const float max_uk_1=500,min_uk_1=-500;
    public:
        pid_antisaturation():kp(0),ki(0),kd(0),uk(0),yk(0),ek(0),ek_1(0),ek_2(0),uk_1(0)
        {};
        pid_antisaturation(float p,float i,float d):kp(p),ki(i),kd(d),uk(0),yk(0),ek(0),ek_1(0),ek_2(0),uk_1(0)
        {};
        void get_value(float act,float target);
        void update_error();
        float update();
};
void pid_antisaturation::get_value(float act,float target)
{
    uk_1=uk;
    uk=act;
    yk=target;
}
void pid_antisaturation::update_error()
{
    ek_2=ek_1;
    ek_1=ek;
    ek=yk-uk;
}
float pid_antisaturation::update()
{
    float increase;
    update_error();
    if((uk_1>max_uk_1)&(ek>0))
    {
        ek=0;
    }
    if((uk_1<min_uk_1)&(ek<0))
    {
        ek=0;
    }
    increase=kp*(ek-ek_1)+ki*ek+kd*(ek-2*ek_1+ek_2);
    printf("p:%f,i:%f,d:%f,act:%f,yk:%f,ek:%f\r\n",kp,ki,kd,uk,yk,ek);
    return increase;
}
  • 0
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BP神经网络(Back Propagation Neural Network)是一种常用的人工神经网络模型,主要用于模式识别和函数逼近等任务。PID智能控制是一种经典的自适应控制算法,可以用于实现对系统的自动调节和控制。本文将介绍如何使用C语言实现BP神经网络和PID智能控制。 首先,我们来介绍BP神经网络的实现。BP神经网络由输入层、隐藏层和输出层组成,其中隐藏层可以有多个。在C语言中,我们可以使用多维数组来表示神经网络的权值和偏置,使用循环来进行神经网络的前向传播和反向传播的计算。具体步骤如下: 1. 初始化神经网络的权值和偏置; 2. 输入样本数据,通过前向传播计算网络的输出值; 3. 计算网络误差,并通过反向传播调整网络的权值和偏置; 4. 重复步骤2和3,直到网络达到收敛。 接下来,我们来介绍PID智能控制的实现PID控制器由比例控制、积分控制和微分控制三个部分组成。在C语言中,我们可以使用变量和循环来实现PID控制。具体步骤如下: 1. 初始化PID控制器的参数; 2. 获取当前系统的反馈值(例如温度、速度等); 3. 根据比例控制、积分控制和微分控制计算出控制信号; 4. 通过控制信号对系统进行控制; 5. 重复步骤2到4,直到系统达到期望状态或者满足停止条件。 综上所述,使用C语言可以分别实现BP神经网络和PID智能控制。在实际工程中,我们可以将这两种方法结合起来,使用BP神经网络进行模型学习和参数自动调节,并将学习到的控制模型应用于PID控制中,以实现对复杂系统的智能控制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值