PIDアルゴリズムの詳細な紹介

1 はじめに

1.1 概要

比例(比例)積分(積分)微分(差動)コントローラ(PIDコントローラまたは3項コントローラ)は、フィードバックを使用した制御ループ機構であり、産業用制御システムや連続変調制御を必要とするその他のさまざまなアプリケーションで広く使用されています。PID コントローラーはエラー値e ( t ) e(t)を継続的に計算します。e ( t )を所望の設定値 (SP) と測定されたプロセス変数 (PV) の差として計算し、比例項、積分項、および微分項 (それぞれ P、I、および D で示す) に基づいて補正を適用すると、次のようになります。名前。
ここに画像の説明を挿入
ここに画像の説明を挿入
r ( t ) r(t)r ( t )は、所望のプロセス値または設定値(SP) (SP)( SP )y ( t ) y(t)y ( t )は測定されたプロセス値(PV) (PV)( PV ) _

1.2 歴史的発展

  • 1911 年に、最初の PID コントローラーが Elmer Sperry によって開発されました。
  • 1922 年になって初めて、ロシア系アメリカ人の技術者ニコラス マイナースキーが初めて理論分析を使用して、現在 PID (3 項制御) と呼ばれているものの正式な制御法則を定式化しました。マイナースキー氏は当時、アメリカ海軍の自動操船システムの研究と設計を行っており、彼の分析は操舵手の観察に基づいていました。彼は、操舵手が現在の機首方位の誤差だけでなく、過去の誤差と現在の変化率にも基づいて船を操縦したことを指摘し、マイナースキー氏はこれを計算しました。彼の目標は全体的な制御ではなく安定性であり、それが問題を大幅に単純化します。
  • 1933 年、TIC (Taylor Instrument Company) は、完全に調整可能なフロント空気圧コントローラーを実装しました。数年後、制御エンジニアは、誤差がゼロ以外になるまで終端を何らかの偽の値に戻すことで、比例コントローラーに見られる定常状態誤差を排除しました。この戻りには誤差が含まれており、これを比例積分制御器と呼びます。
  • 1940 年に、オーバーシュートの問題を軽減するための微分動作を備えた最初の空気圧 PID コントローラーが開発されました。
  • 1942 年に、ジーグラーとニコルズは、エンジニアが PID コントローラーに適したパラメーターを見つけて設定する調整ルールを導入しました。
  • 1950 年代半ばには、自動 PID コントローラーが産業界で広く使用されました。業界における最新の PID 制御のほとんどは、DCS、PLC、またはマイクロコントローラー プログラムとして実装されています。

1.3 アプリケーション

  • ロケットの姿勢制御
  • UAVホバリング制御など
  • カメラスタビライザー、カメラジンバル
  • バランスカー
  • 自動車のクルーズコントロール、ステアリングコントロール
  • エンジン速度制御
  • 3Dプリンターの温度コントローラー
  • 産業オートメーションの分野では、閉ループ操作の約 95% で PID コントローラーが使用されています。

1.4 メリットとデメリット

1.5 ON/OFF型コントローラとの比較

PID コントローラーなどの閉ループ システムには、フィードバック制御システムが含まれています。システムは固定小数点を使用してフィードバック変数を評価し、エラー信号を生成します。これに基づいてシステム出力を変更します。このプロセスは、誤差がゼロに達するまで継続されます。ゼロに達しない場合、フィードバック変数の値は固定小数点に等しくなります。

ON/OFFタイプのコントローラーに比べて良好な結果が得られます。オン/オフ型コントローラーでは、システムを管理するために必要な条件は 2 つだけです。ほとんどの HVAC システムや冷蔵庫はこの方法を使用しています。たとえば、冷蔵庫では、希望の温度に達するまで内部を冷却し、その後、希望の温度を超える設定値に達するまでクーラーをオフにします。プロセス値が固定点を下回るとオンになります。同様に、値が一定値を超えるとオフになります。このコントローラーの出力は不安定で、固定小数点の領域で頻繁に発振します。ただし、PID コントローラは ON/OFF タイプのコントローラと比較して、より安定しており、正確です。
ここに画像の説明を挿入

1.6 応答の種類

PID の概要

PID コントローラーによって駆動されるシステムには通常、過小減衰、過減衰、臨界減衰という 3 種類の応答があります。
ここに画像の説明を挿入
ここに画像の説明を挿入

  • 過小減衰応答は、安定する前に基準値付近で振動します。
  • 過減衰応答はゆっくりと立ち上がり、基準値を超えることはありません。
  • 臨界減衰応答は、基準値を超えずに最速の立ち上がり時間を持ちます。

2 公式

2.1 PID システムの定義と計算式

ここに画像の説明を挿入

r ( t ) r(t)r ( t )設定値、基準は、望ましいプロセス値または設定値( SP ) (SP)( SP )
y ( t ) y(t)y ( t )出力、プロセス変数は、測定されたプロセス値、出力値( PV ) (PV)( P V )
e ( t ) e(t)e ( t )誤差、偏差です;
u ( t ) u(t)u ( t )制御努力、制御量です。

PIDコントローラの大きな特徴は、比例、積分、微分の3つの制御項目がコントローラの出力に与える影響を利用して、精密で最適な制御を行えることです。

PID コントローラー、エラー値e ( t ) e(t)を継続的に計算します。e ( t )は、所望の設定値間の差としてSP = r(t) SP=r(t)SP=r ( t )と測定されたプロセス変数PV = y ( t ) : e ( t ) = r ( t ) − y ( t ) PV=y(t):e(t)=r(t)-y(t )PV _=y ( t ):e ( t )=r ( t )y ( t )を計算し、比例項、積分項、および微分項に基づいて補正を適用します。コントローラーは、制御変数を調整することで時間変化誤差u ( t ) u(t)u ( t )。操作変数 (MV)。

u ( t ) = MV ( t ) = K pe ( t ) + K i ∫ 0 te ( τ ) d τ + K dde ​​( t ) dt (1-1) u(t)=MV(t)=K_pe( t)+K_i \int_{0}^{t}e(τ)dτ+K_d \dfrac{de(t)}{dt}\tag{1-1}u ( t )=MV ( t ) _=Kpe ( t )+K私は0および( τ ) d τ+Kddt _( t ) _( 1-1 )
u ( t ) = P ∗ 偏差 + I ∗ 面積 + D ∗ 傾き u(t)=P*偏差 + I*面積 + D*傾きu ( t )=P偏差+エリア+Dスロープ

K p K_pKpは比例ゲインであり、このパラメータは大きな変化を引き起こし、システムの応答を高速化し、システムのオーバーシュートや発振を引き起こす可能性があります。K
i K_iK私はは積分ゲインであり、設定点に向けてプロセスの動きを加速します;
K d K_dKdはシステムのオーバーシュート振幅と発振時間を減らすのに役立つ微分ゲインです;
t は時間または瞬間時間 (現在);
τ \tauτは整数変数です(時刻0から現在tまでの値をとります)。

K p K_pKpK i K_iK私はK d K_dKdは、それぞれ比例項、積分項、および微分項の係数 (P、I、および D で示されることもあります) を表す非負の数です。

PID コントローラーから:理論、設計、調整

u ( t ) = MV ( t ) = K p ( e ( t ) + 1 Ti ∫ 0 te ( τ ) d τ + T dde ​​( t ) dt ) (1-2) u(t)=MV(t )=K_p \Bigl(e(t)+ \dfrac1{T_i} \int_{0}^{t}e(τ)dτ + T_d \dfrac{de(t)}{dt} \Bigl) \tag{1 -2}u ( t )=MV ( t ) _=Kp( e ( t )+T私は10および( τ ) d τ+Tddt _( t ) _( 1-2 )

方程式の標準形式では、K i K_iK私はそしてK d K_dKdそれぞれK p T i \dfrac{K_p}{T_i}に置き換えられます。T私はKpK p T d K_pT_dKpTd;この利点は、T i , T d T_i, T_dであることです。T私はTdそれらはそれぞれ積分時間と微分時間を表すため、ある程度の理解可能な物理的意味を持ちます。K p T i \dfrac{K_p}{T_i}T私はKpコントローラーが出力が設定値を上回るか下回る状態にどれだけ耐えられるかを決定します。K p T d K_pT_dKpTdは、コントローラーが設定値に近づこうとする時間定数です。

2.2 PIDデジタル式

コンピュータ制御はサンプリング制御の一種であるため、サンプリング時刻の偏差に応じて制御量を計算することしかできず、連続制御を行うアナログ制御のように制御量を連続的に出力することはできません。この特徴により、(式 1-1) の積分項と微分項は直接使用できず、離散化する必要があります。
離散化の方法は次のとおりです。τ \tauを使用します。τはサンプリング周期、k はサンプリング数、離散サンプリング時間k τ k\tauk τ は連続時間 t に相当し、積分は方形法、微分は後方一次差分で近似し、以下の近似変換が可能です:
t ≈ k τ ( k = 0 , 1 , 2 , . . . ) u ( t ) ≈ u ( k ) e ( t ) ≈ e ( k ) ∫ 0 te ( τ ) d τ ≈ τ ∑ j = 0 ke ( j τ ) = τ ∑ j = 0 ケジデ( t ) dt ≈ e ( k τ ) − e ( ( k − 1 ) τ ) τ = ek − ek − 1 τ \begin{align} t & ≈ k \tau (k=0,1,2,.. .) \notag\\ u(t) & ≈ u(k) \notag\\ e(t) & ≈ e(k) \notag\\ \int_{0}^{t}e(τ)dτ & ≈ \tau \sum_{j =0}^ke(j \tau) = \tau \sum_{j=0}^k e_j \tag{2-1}\\ \dfrac{de(t)}{dt} & ≈ \dfrac{e( k\tau)-e((k-1)\tau)}{\tau} = \dfrac{e_k-e_{k-1}}{\tau} \notag \end{align}tu ( t )e ( t )0および( τ ) d τdt _( t ) _k τ ( k=0 1 2 ... )u ( k )e ( k )tj = 0e ( ) _=tj = 0ejte ( k t )e (( k1 ) t )=teek 1( 2-1 )

偏差值 e ( i τ ) e(i\tau) e(iτ),简记为 e ( i ) e(i) e(i) 或者 e i e_i e私は;
控制量 u ( i τ ) u(i\tau) u(iτ),简记为 u ( i ) u(i) u(i) 或者 u i u_i あなた私は;

2.3 位置 PID アルゴリズム

(式 1-1) に (式 2-1) を代入すると、離散 PID 式は
u ( k ) = K pe ( k ) + K i τ ∑ j = 0 ke ( j ) + K de ( k ) − e ( k − 1 ) τ (2-2) u(k)=K_pe(k)+K_i \tau \sum_{j=0}^{k}e(j) + K_d \dfrac{e( k )-e(k-1)}{\tau}\tag{2-2}u ( k )=Kpe ( k )+K私はtj = 0e ( j )+Kdte ( k )e ( k1 )( 2-2 )
(式 2-1) を (式 1-2) に代入すると、離散 PID 式は
u ( k ) = K p ( e ( k ) + τ Ti ∑ j = 0 ke ( j ) + T de ( k ) − e ( k − 1 ) τ ) (2-3) u(k)=K_p \Bigl(e(k)+ \dfrac{\tau}{T_i} \sum_{j = 0}^{k}e(j) + T_d \dfrac{e(k)-e(k-1)}{\tau} \Bigl) \tag{2-3}u ( k )=Kp( e ( k )+T私はtj = 0e ( j )+Tdte ( k )e ( k1 )( 2-3 )

積分係数と微分係数は次のように置き換えられます。注: τ \tau
を使用する必要があります。τは固定値、または変化が無視できるほど小さいため、P、I、D は固定定数となり、調整可能です。
K i = K p T i K d = K p T d KI = K p ∗ τ T i = K i τ KD = K p T d τ = KD τ \begin{align} K_i & = \dfrac{K_p}{T_i} \notag\\ K_d & = K_pT_d \notag\\ K_I & = \dfrac{K_p*\tau }{T_i} = K_i \tau \tag{2-4}\\ K_D & = K_p\dfrac{T_d}{\tau} = \dfrac{K_D}{\tau} \notag \\ \end{整列}K私はKdKKD=T私はKp=KpTd=T私はKpt=K私はt=KptTd=tKD( 2-4 )

この利点は、T i 、T d T_i、T_dであることです。T私はTd理解できる物理的な意味があります。
ティティT私は積分時定数を表します;
T d T_dTd微分時定数を表します。

キキKそしてKD K_DKD(式 2-2) に代入すると、
u ( k ) = K pe ( k ) + KI ∑ j = 0 ke ( j ) + KD [ e ( k ) − e ( k − 1 ) ] (2-5) u(k) = K_pe(k)+K_I \sum_{j=0}^{k}e(j) + K_D [e(k)-e(k-1)]\tag {2-5}u ( k )=Kpe ( k )+Kj = 0e ( j )+KD[ e ( k )e ( k1 )]( 2-5 )

kkkはサンプリング シーケンス番号、k = 0、1、2、... k=0、1、2、...k=0 1 2 ...
うーん、u_kあなたk 番目のサンプリング時点でのコンピュータ出力値です;
ek e_kek 番目のサンプリング時間に入力されたバイアス値;
ek − 1 e_{k-1}ek 1k-1 番目のサンプリング時間に入力されたバイアス値。

2.4 インクリメンタル PID アルゴリズム

いわゆるインクリメンタル PID は、デジタル コントローラーの出力が制御量Δ uk \Delta u_kの増分のみであることを意味します。あなた_アクチュエータに要求される制御量が位置量の絶対値ではなくインクリメンタルである場合には、インクリメンタルPID制御アルゴリズムを使用して制御することができます。

インクリメンタル PID 制御アルゴリズムは (2-2) によって推定できます。(式 2-2) より、k-1 回目のサンプリング時点におけるコントローラーの出力値は次のように求められます。

u ( k − 1 ) = K pe ( k − 1 ) + K i τ ∑ j = 0 k − 1 e ( j ) + K de ( k − 1 ) − e ( k − 2 ) τ (2-6) u(k-1) = K_pe(k-1)+K_i \tau \sum_{j=0}^{k-1}e(j) + K_d \dfrac{e(k-1)-e(k- 2)}{\年}\tag{2-6}( k1 )=Kpe ( k1 )+K私はtj = 0k 1e ( j )+Kdte ( k1 )e ( k2 )( 2-6 )

(式 2-3) より、k-1 番目のサンプリング瞬間におけるコントローラーの出力値が得られます。
u ( k − 1 ) = K p ( e ( k − 1 ) + τ Ti i ∑ j = 0 k − 1 e ( j ) + T de ( k − 1 ) − e ( k − 2 ) τ ) (2-7) u(k-1)=K_p \Bigl(e(k-1)+ \dfrac{\タウ }{T_i} \sum_{j=0}^{k-1}e(j) + T_d \dfrac{e(k-1)-e(k-2)}{\tau} \Bigl) \tag {2-7}( k1 )=Kp( e ( k1 )+T私はtj = 0k 1e ( j )+Tdte ( k1 )e ( k2 )( 2-7 )

図 (図 2-3) および (図 2-7) は、PID サイクルの逆サイクルを示しています。
Δ uk = u ( k ) − u ( k − 1 ) = K p ( e ( k ) − e ( k − 1 ) + τ Tie ( k ) + T de ( k ) − 2 e ( k − 1 ) + e ( k − 2 ) τ ) = K p ( 1 + τ T i + T d τ ) e ( k ) − K p ( 1 + 2 T d τ ) e ( k − 1 ) + K p T d τ e ( k − 2 ) = A e ( k ) + B e ( k − ) + C e ( k − 2 ) \begin{align} \Delta u_k = u(k) - u(k-1) & = K_p \Bigl(e(k)-e(k-1)+ \dfrac{ \tau}{T_i} e (k) + T_d \dfrac{e(k)-2e(k-1)+e(k-2)}{\tau}\Bigl) \notag \\ & = K_p(1 +\dfraction{\tau} {T_i}+\dfraction{T_d}{\tau})e(k) - K_p(1+\dfraction{2T_d}{\tau})e(k-1)+K_p\dfraction {T_d}{\tau} e(k-2) \tag{2-8} \\ & = Ae(k) + Be(k-1) + Ce(k-2) \notag \end{align}あなた_=u ( k )( k1 )=Kp( e ( k )e ( k1 )+T私はte ( k )+Tdte ( k )2e ( k _1 )+e ( k2 )=Kp( 1+T私はt+tTd) e ( k )Kp( 1+t2T_ _d) e ( k1 )+KptTde ( k2 )=A e ( k )+B e ( k1 )+C e ( k2 )( 2-8 )

(式 2-8) からわかるように、コンピュータ制御システムが一定のサンプリング周期τ \tauを採用している場合、τは、A、B、C が決まれば、前後 3 回の測定値の偏差値を使えば、(式 2-8) で制御量を求めることができます。

インクリメンタル PID 制御アルゴリズムは、位置 PID アルゴリズム (式 2-3) と比較して、現時点から 3 瞬間前の偏差値を保持するだけでよく、累積誤差が小さく、計算量が大幅に少ないため、実際には広く使われています。

位置 PID 制御アルゴリズムは、インクリメンタル制御アルゴリズムを通じて再帰的計算式を導出することもできます。
u ( k ) = u ( k − 1 ) + Δ u ( k ) (2-9) u(k) = u(k -1) ) + \Delta u(k) \tag{2-9}u ( k )=( k1 )+Δu ( k ) _( 2-9 )
(式 2-9) は、現在コンピュータ制御で広く使われているデジタル再帰型 PID 制御アルゴリズムです。

3 デバッグのヒント

ここに画像の説明を挿入

4 コードの実装

パイソン

Pythonシミュレーションを使用したPID制御アルゴリズムの実装より

import numpy as np
import matplotlib.pyplot as plt

class PositionPID(object):
    """位置式PID算法实现"""

    def __init__(self, target, cur_val, dt, max, min, p, i, d) -> None:
        self.dt = dt  # 循环时间间隔
        self._max = max  # 最大输出限制,规避过冲
        self._min = min  # 最小输出限制
        self.k_p = p  # 比例系数
        self.k_i = i  # 积分系数
        self.k_d = d  # 微分系数

        self.target = target  # 目标值
        self.cur_val = cur_val  # 算法当前PID位置值,第一次为设定的初始位置
        self._pre_error = 0  # t-1 时刻误差值
        self._integral = 0  # 误差积分值


    def calculate(self):
        """
        计算t时刻PID输出值cur_val
        """
        error = self.target - self.cur_val  # 计算当前误差
        # 比例项
        p_out = self.k_p * error  
        # 积分项
        self._integral += (error * self.dt)
        i_out = self.k_i * self._integral
        # 微分项
        derivative = (error - self._pre_error) / self.dt
        d_out = self.k_d * derivative

        # t 时刻pid输出
        output = p_out + i_out + d_out

        # 限制输出值
        if output > self._max:
            output = self._max
        elif output < self._min:
            output = self._min
        
        self._pre_error = error
        self.cur_val = output
        return self.cur_val

    def fit_and_plot(self, count = 200):
        """
        使用PID拟合setPoint
        """
        counts = np.arange(count)
        outputs = []

        for i in counts:
            outputs.append(self.calculate())
            print('Count %3d: output: %f' % (i, outputs[-1]))

        print('Done')
        # print(outputs)
        
        plt.figure()
        plt.axhline(self.target, c='red')
        plt.plot(counts, np.array(outputs), 'b.')
        plt.ylim(min(outputs) - 0.1 * min(outputs), max(outputs) + 0.1 * max(outputs))
        plt.plot(outputs)
        plt.show()

pid = PositionPID(10, -5, 0.5, 100, -100, 0.2, 0.1, 0.01)
pid.fit_and_plot(150)

ここに画像の説明を挿入

c/c++

PID超詳細チュートリアルより- PID原理 + カスケードPID + Cコード + オンラインシミュレーションパラメータ調整

//首先定义PID结构体用于存放一个PID的数据
typedef struct
{
   	float kp,ki,kd;//三个系数
    float error,lastError;//误差、上次误差
    float integral,maxIntegral;//积分、积分限幅
    float output,maxOutput;//输出、输出限幅
}PID;
 
//用于初始化pid参数的函数
void PID_Init(PID *pid,float p,float i,float d,float maxI,float maxOut)
{
    pid->kp=p;
    pid->ki=i;
    pid->kd=d;
    pid->maxIntegral=maxI;
    pid->maxOutput=maxOut;
}
 
//进行一次pid计算
//参数为(pid结构体,目标值,反馈值),计算结果放在pid结构体的output成员中
void PID_Calc(PID *pid,float reference,float feedback)
{
 	//更新数据
    pid->lastError=pid->error;//将旧error存起来
    pid->error=reference-feedback;//计算新error
    //计算微分
    float dout=(pid->error-pid->lastError)*pid->kd;
    //计算比例
    float pout=pid->error*pid->kp;
    //计算积分
    pid->integral+=pid->error*pid->ki;
    //积分限幅
    if(pid->integral > pid->maxIntegral) pid->integral=pid->maxIntegral;
    else if(pid->integral < -pid->maxIntegral) pid->integral=-pid->maxIntegral;
    //计算输出
    pid->output=pout+dout+pid->integral;
    //输出限幅
    if(pid->output > pid->maxOutput) pid->output=pid->maxOutput;
    else if(pid->output < -pid->maxOutput) pid->output=-pid->maxOutput;
}
 
PID mypid;//创建一个PID结构体变量
 
int main()
{
    //...这里有些其他初始化代码
    PID_Init(&mypid,10,1,5,800,1000);//初始化PID参数
    while(1)//进入循环运行
    {
        float feedbackValue=...;//这里获取到被控对象的反馈值
        float targetValue=...;//这里获取到目标值
        PID_Calc(&mypid,targetValue,feedbackValue);//进行PID计算,结果在output成员变量中
        设定执行器输出大小(mypid.output);
        delay(10);//等待一定时间再开始下一次循环
    }
}

シングルリングエフェクト
ここに画像の説明を挿入
ここに画像の説明を挿入

カスケードPIDのC言語コード

//此处需要插入上面的单级PID相关代码
 
//串级PID的结构体,包含两个单级PID
typedef struct
{
    
    
    PID inner;//内环
    PID outer;//外环
    float output;//串级输出,等于inner.output
}CascadePID;
 
//串级PID的计算函数
//参数(PID结构体,外环目标值,外环反馈值,内环反馈值)
void PID_CascadeCalc(CascadePID *pid,float outerRef,float outerFdb,float innerFdb)
{
    
    
    PID_Calc(&pid->outer,outerRef,outerFdb);//计算外环
    PID_Calc(&pid->inner,pid->outer.output,innerFdb);//计算内环
    pid->output=pid->inner.output;//内环输出就是串级PID的输出
}
 
CascadePID mypid;//创建串级PID结构体变量
 
int main()
{
    
    
    //...其他初始化代码
    PID_Init(&mypid.inner,10,0,0,0,1000);//初始化内环参数
    PID_Init(&mypid.outer,5,0,5,0,100);//初始化外环参数
    while(1)//进入循环运行
    {
    
    
        float outerTarget=...;//获取外环目标值
        float outerFeedback=...;//获取外环反馈值
        float innerFeedback=...;//获取内环反馈值
        PID_CascadeCalc(&mypid,outerTarget,outerFeedback,innerFeedback);//进行PID计算
        设定执行机构输出大小(mypid.output);
        delay(10);//延时一段时间
    }
}

ダブルリング効果
ここに画像の説明を挿入

5 二重ループ制御

シリーズで

組み込みソフトウェア アルゴリズムの PID 閉ループ制御原理より

モーター制御が速度と位置の両方を制御する必要がある場合、速度と位置は関連しているため、直列に接続する必要があります。
ここに画像の説明を挿入

並行して

組み込みソフトウェア アルゴリズムの PID 閉ループ制御原理より

姿勢角と速度には相関がなく、それぞれ一方向制御としてカウントされます。
ここに画像の説明を挿入

6つの例

追跡車

PID 制御アルゴリズムを 1 つの記事で理解する

車の追従効果が確認できます。

山火事時のステッピングモーターの位置と速度の二重ループ制御

11. ステッピングモーターの位置と速度の二重ループ制御の実現

9. ステッピングモーターの速度ループ制御の実現10. ステッピングモーターの位置ループ制御の実現シングルループ制御がモーターの性能を大幅に向上できることを紹介しましたが、それでも限界があります。

速度ループはモーターの速度を正確に制御するために使用されますが、停止位置を正確に制御するのは困難です。位置ループはモーターの角度を正確に制御するために使用されますが、
失速を防ぐために速度を人為的に制限する必要があります。 。

位置ループと速度ループの二重ループ制御により、精密な位置調整と速度の自動制御を実現します。
ここに画像の説明を挿入

この制御では、エンコーダはフィードバック位置の役割だけでなく、フィードバック速度の役割も果たします。

パラメータチューニングスキル:PIDパラメータを設定するときは、最初に内側ループ、次に外側ループの方法を採用します。つまり、最初に速度ループのみを使用して制御し、満足のいくパラメータを取得した後、位置ループを外側に配置し、パラメータを設定します。位置ループ、そして最後に全体的なエフェクトに従って速度ループのパラメーターを微調整します。

bsp_pid.h

/*pid*/
typedef struct
{
    
    
  float target_val;     //目标值
  float actual_val;     //实际值
  float err;            //定义当前偏差值
  float err_next;       //定义下一个偏差值
  float err_last;       //定义上一个偏差值
  float Kp, Ki, Kd;     //定义比例、积分、微分系数
}_pid;

bsp_stepper_ctrl.h

/*宏定义*/
/*******************************************************/
#define TIM_STEP_FREQ     (SystemCoreClock/TIM_PRESCALER) // 频率ft值

/*电机单圈参数*/
#define STEP_ANGLE                          1.8f                 //步进电机的步距角 单位:度
#define FSPR              (360.0f/STEP_ANGLE)  //步进电机的一圈所需脉冲数

#define MICRO_STEP        32                                         //细分器细分数
#define SPR               (FSPR*MICRO_STEP)    //细分后一圈所需脉冲数

#define PULSE_RATIO       (float)(SPR/ENCODER_TOTAL_RESOLUTION)//步进电机单圈脉冲数与编码器单圈脉冲的比值
#define SAMPLING_PERIOD   50                   //PID采样频率,单位Hz

#define MOVE_CTRL         0.1f                   //启用速度环控制量
#define TARGET_DISP       20                   //步进电机运动时的目标圈数,单位:转
#define TARGET_SPEED_MAX  800                 // 目标速度的最大值

typedef struct {
    
    
  unsigned char stepper_dir : 1;               //步进电机方向
  unsigned char stepper_running : 1;           //步进电机运行状态
  unsigned char MSD_ENA : 1;                   //驱动器使能状态
}__SYS_STATUS;

bsp_stepper_ctrl.c-インクリメンタル PID アルゴリズムの実装-インクリメンタル PID

/**
   * @brief  增量式PID算法实现
   * @param  val:当前实际值
   * @note   无
   * @retval 通过PID计算后的输出
   */
 float PID_realize(_pid *pid, float temp_val)
 {
    
    
   /*传入实际值*/
   pid->actual_val = temp_val;
   /*计算目标值与实际值的误差*/
   pid->err=pid->target_val-pid->actual_val;

   /*PID算法实现*/
   float increment_val = pid->Kp*(pid->err - pid->err_next) + pid->Ki*pid->err + pid->Kd*(pid->err - 2 * pid->err_next + pid->err_last);
   /*传递误差*/
   pid->err_last = pid->err_next;
   pid->err_next = pid->err;
   /*返回增量值*/
   return increment_val;
 }

bsp_stepper_ctrl.c - ステッピング モーターの位置と速度の二重閉ループ制御

 /**
   * @brief  步进电机位置速度双闭环控制
   * @retval 无
   * @note   基本定时器中断内调用
   */
 void Stepper_Ctrl(void)
 {
    
    
   /* 编码器相关变量 */
   static __IO float last_count = 0;
   __IO float capture_count = 0;
   __IO float capture_per_unit = 0;
   /* 经过pid计算后的期望值 */
   static __IO float speed_cont_val = 0.0f;
   static __IO float move_cont_val = 0.0f;
   static int cont_val = 0;

   /* 当电机运动时才启动pid计算 */
   if((sys_status.MSD_ENA == 1) && (sys_status.stepper_running == 1))
   {
    
    
     /* 计算编码器脉冲数 */
     capture_count = (int)__HAL_TIM_GET_COUNTER(&TIM_EncoderHandle) + (encoder_overflow_count * ENCODER_TIM_PERIOD);
     /* 计算速度环的传入值 */
     capture_per_unit = capture_count - last_count;
     last_count = capture_count;

     /* 编码器脉冲累计值作为实际值传入位置环pid控制器 */
     move_cont_val += PID_realize_move(&move_pid, (float)capture_count);// 进行 PID 计算
     /* 判断运动方向 */
     move_cont_val > 0 ? (MOTOR_DIR(CW)) : (MOTOR_DIR(CCW));
     /* 判断是否启用速度环 */
     if (fabsf(move_cont_val) >= MOVE_CTRL)
     {
    
    
       /* 传递位置环计算值,便于计算*/
       cont_val = move_cont_val;

       /* 目标速度上限处理 */
       if (cont_val > TARGET_SPEED_MAX)
       {
    
    
         cont_val = TARGET_SPEED_MAX;
       }
       else if (cont_val < -TARGET_SPEED_MAX)
       {
    
    
         cont_val = -TARGET_SPEED_MAX;
       }

 #if defined(PID_ASSISTANT_EN)
       int32_t temp = cont_val;
       set_computer_value(SEED_TARGET_CMD, CURVES_CH2, &temp, 1);     // 给通道 2 发送目标值
 #endif
       /* 设定速度的目标值 */
       set_pid_target(&speed_pid, cont_val);
       /* 单位时间内的编码器脉冲数作为实际值传入速度环pid控制器 */
       speed_cont_val += PID_realize_speed(&speed_pid, (float)capture_per_unit);// 进行 PID 计算
       /* 由于OC_Pulse_num为uint16_t变量,取速度环输出值的绝对值进行后续计算*/
       cont_val = fabsf(speed_cont_val);
       /* 计算比较计数器的值 */
       OC_Pulse_num = ((uint16_t)(TIM_STEP_FREQ / (cont_val * PULSE_RATIO * SAMPLING_PERIOD))) >> 1;
     }
     else
     {
    
    
       /* 计算比较计数器的值 */
       OC_Pulse_num = ((uint16_t)(TIM_STEP_FREQ / ((float)move_cont_val * PULSE_RATIO))) >> 1;
     }
 #if PID_ASSISTANT_EN
     int Temp_ch2 = capture_per_unit;    // 上位机需要整数参数,转换一下
     int Temp_ch1 = capture_count;
     set_computer_value(SEED_FACT_CMD, CURVES_CH2, &Temp_ch2, 1);  // 给通道 1 发送实际值     // 给通道 2 发送实际值
     set_computer_value(SEED_FACT_CMD, CURVES_CH1, &Temp_ch1, 1);     // 给通道 1 发送实际值

 #else
     printf("实际值:%d,目标值:%.0f\r\n", capture_per_unit, pid.target_val);// 打印实际值和目标值
 #endif
   }
   else
   {
    
    
     /*停机状态所有参数清零*/
     last_count = 0;
     speed_cont_val = 0;
     move_cont_val = 0;
     speed_pid.actual_val = 0;
     speed_pid.err = 0;
     speed_pid.err_last = 0;
     speed_pid.err_next = 0;
     move_pid.actual_val = 0;
     move_pid.err = 0;
     move_pid.err_last = 0;
     move_pid.err_next = 0;
   }
 }

主要

 /**
   * @brief  主函数
   * @param  无
   * @retval 无
   */
 int main(void)
 {
    
    
   /* 初始化系统时钟为168MHz */
   SystemClock_Config();
   /*初始化USART 配置模式为 115200 8-N-1,中断接收*/
   DEBUG_USART_Config();
   printf("欢迎使用野火 电机开发板 步进电机位置速度双环控制 例程\r\n");
   printf("按下按键3启动和停止电机\r\n");
   /* 初始化时间戳 */
   HAL_InitTick(5);
   /*按键中断初始化*/
   Key_GPIO_Config();
   /*led初始化*/
   LED_GPIO_Config();
   /* 初始化基本定时器定时,20ms产生一次中断 */
   TIMx_Configuration();
   /* 编码器接口初始化 */
   Encoder_Init();
   /*步进电机初始化*/
   stepper_Init();
   /* 上电默认停止电机 */
   Set_Stepper_Stop();
   /* PID算法参数初始化 */
   PID_param_init();
 //  MOTOR_DIR(CW);

   /* 目标位置转换为编码器的脉冲数作为pid目标值 */
   move_pid.target_val = TARGET_DISP * ENCODER_TOTAL_RESOLUTION;
   int32_t Temp = TARGET_DISP * ENCODER_TOTAL_RESOLUTION;
 #if PID_ASSISTANT_EN
   set_computer_value(SEED_STOP_CMD, CURVES_CH1, NULL, 0);    // 同步上位机的启动按钮状态
   set_computer_value(SEED_TARGET_CMD, CURVES_CH1, &Temp, 1);// 给通道 1 发送目标值
 #endif

   while(1)
   {
    
    
     /* 扫描KEY1,启动电机 */
     if( Key_Scan(KEY1_GPIO_PORT,KEY1_PIN) == KEY_ON  )
     {
    
    
     #if PID_ASSISTANT_EN
       Set_Stepper_Start();
       set_computer_value(SEED_START_CMD, CURVES_CH1, NULL, 0);// 同步上位机的启动按钮状态
     #else
       Set_Stepper_Start();
     #endif
     }
     /* 扫描KEY2,停止电机 */
     if( Key_Scan(KEY2_GPIO_PORT,KEY2_PIN) == KEY_ON  )
     {
    
    
     #if PID_ASSISTANT_EN
       Set_Stepper_Stop();
       set_computer_value(SEED_STOP_CMD, CURVES_CH1, NULL, 0);// 同步上位机的启动按钮状态
     #else
       Set_Stepper_Stop();
     #endif
     }
     /* 扫描KEY3,增大目标位置*/
     if( Key_Scan(KEY3_GPIO_PORT,KEY3_PIN) == KEY_ON  )
     {
    
    
       /* 目标位置增加48000,对应电机位置增加20圈 */
       move_pid.target_val += 48000;

     #if PID_ASSISTANT_EN
       int temp = move_pid.target_val;
       set_computer_value(SEED_TARGET_CMD, CURVES_CH1, &temp, 1);// 给通道 1 发送目标值
     #endif
     }
     /* 扫描KEY4,减小目标位置 */
     if( Key_Scan(KEY4_GPIO_PORT,KEY4_PIN) == KEY_ON  )
     {
    
    
       /* 目标位置减小48000,对应电机位置减少20圈 */
       move_pid.target_val -= 48000;

     #if PID_ASSISTANT_EN
       int temp = move_pid.target_val;
       set_computer_value(SEED_TARGET_CMD, CURVES_CH1, &temp, 1);// 给通道 1 发送目标值
     #endif
     }
   }
 }

他の

補足的な知識のポイント

アナログデジタル化

実際のデジタル アプリケーションでは、PID システムの積分項と微分項を離散化する必要があります。同様の典型的なアプリケーションはデジタル オシロスコープです。デジタル オシロスコープの場合、アナログ信号を直接定量化することはできません。代わりに、周期的なサンプリングを継続し、取得した一連のサンプリング ポイントを表示します。サンプリング レートが高いほど、表示される画像はより現実的になります。これが数学の限界です。差別的思考で。
ここに画像の説明を挿入

シャノンのサンプリングの法則

  • サンプリング
    連続変数関数 f(x) はコンピュータで直接処理できません。f ( x ) f(x)を選択してください。離散点 f ( xn ) におけるf ( x )の値f(x_n)f ( x)、このプロセスはサンプリングと呼ばれます。
  • 定理の内容
    シャノンのサンプリング定理は、有限帯域幅関数用です。
    アナログ信号を歪みなく復元するには、サンプリング周波数がアナログ信号スペクトルの最高周波数の 2 倍以上である必要があります。

参考

1. wiki – PID コントローラー
2. PID コントローラーとは : 仕組みとその応用
3. PID コントローラー: 理論、設計、チューニング
4. 11. ステッピングモーターの位置と速度の二重ループ制御の実現
5. PID 閉ループ組み込みソフトウェア アルゴリズムの制御 原理
6. PID 制御アルゴリズムの詳細な説明
7. Xiaoyuan Gungun-PID アルゴリズムの数学的導出
8. PID とは何ですか? ストーリー+アニメーションでわかりやすい!素晴らしい説明ですね!
9. PID 制御
10. PID アルゴリズムの導出と解析
11. PID 制御
12.比例積分微分法 (PID)
13.講義: 制御理論: PID 制御 (古典制御理論)
14. PID 制御アルゴリズム
https://blog.csdn 。 net/kilotwo/article/details/79828201
https://blog.csdn.net/kilotwo/article/details/79829669
https://blog.csdn.net/kilotwo/article/details/79952530
15. PID入門
16. PID制御原理とインクリメンタルPIDアルゴリズム
17. PID超詳細チュートリアル - PID原理 + カスケードPID + Cコード + オンラインシミュレーションチューニング
18、PID をまっすぐに進める
19、Python 2019 で PID コントローラーを構築する
20、PID アルゴリズムの原理、PID の 3 つのパラメーターを理解するための 1 つの図
21、PID 制御アルゴリズムを理解するための 1 つの記事
22、PID 制御 (4 つ) (ループとダブルループPID)
23. PID制御原理、最初を読めば最後がわかります!
24. PID アルゴリズムとロボット アーム アプリケーション (簡単な Python 実装による)
25. PID 式の一般的な理解
26. Python シミュレーションを使用した PID 制御アルゴリズムの実現
27. Python での PID コントローラーの実装
28.物理システムの制御
29. PID 制御

おすすめ

転載: blog.csdn.net/qq_38880380/article/details/131585006