Jetbotmini での PID 駆動アルゴリズム制御とコード実装

PID アルゴリズムは産業分野で広く使用されており、自動車、航空機、ミサイル、機器の温度制御などに使用されています.PID の 3 文字は、比例積分積分微分微分の頭文字であり、PID 制御です。アルゴリズムは、比例、積分、微分の 3 つのリンクを組み合わせた制御アルゴリズムです。PID 制御の本質は、入力偏差値に応じて比例、積分、微分の関数関係に従って動作し、計算結果を使用して出力を制御することであり、これら 3 つのアルゴリズムの組み合わせにより、制御された偏差を効果的に修正できます。安定した状態に到達できるようにします。

詳細については、百度百科事典の説明を確認できます: PID アルゴリズム

自動運転などの簡単で一般的な例を挙げると、一般的な考え方によれば、30 ヤードの速度を維持する場合、速度が 30 ヤード未満の場合はアクセルを踏んで加速し、速度が 30 ヤード未満の場合はブレーキを踏んで減速します。速度が 30 ヤードを超えると、車は非常に不安定になりますはい、速いときもあれば遅いときもあり、座り心地が悪いだけでなく、安全性も問題になるため、このときは PID 制御を使用します。車は非常に安定しているように見えます。もちろん、この例は P の比例調整を示しているだけのようです. 明らかに、それは良い効果をもたらしません. 微分と統合も必要です, つまり、それらを微調整することを意味します, 良い安定した出力を達成できるようにするため.

1. PID 式と説明

次のように、まず PID の式に慣れましょう。

Kp:比例ゲイン、Kpは比例と反比例の関係

Tt:積分時定数

TD:微分時定数

u(t): PID コントローラーの出力信号

e(t): 与えられた値 r(t) と測定値の差、または 2 つの誤差の差

PID コントローラーを図に示します。

3 つのアルゴリズムを追加した後、安定した出力が得られ、制御対象に適用されていることが図からわかります。

3 つの PID アルゴリズム (3 つのパラメーターを調整すると見なすこともできます) の説明は次のとおりです。

比例:アナログ PID コントローラでは、比例リンクの機能は偏差に瞬時に応答することです。偏差が発生すると、コントローラーはすぐに偏差を減らす方向に制御量を変化させる制御効果を生み出します。制御動作の強さは比例係数に依存し、比例係数が大きいほど制御動作が強くなり、移行プロセスが速くなり、制御プロセスの静的偏差が小さくなり、大きくなると制御が容易になります。振動を発生させ、システムの安定性を破壊することです。したがって、遷移時間を短縮し、静的な差を小さくし、効果を安定させるには、比例係数の選択が適切でなければなりません。

積分:積分部分の数式から、偏差がある限り、その制御効果は増加し続けることがわかります; 偏差がある場合にのみ、その積分は一定であり、制御効果は増加しません。一定です。一体型部品がシステムの偏差を解消できることがわかります。e(t) = 0 インテグラル リンクの調整機能は静的エラーを解消しますが、システムの応答速度を低下させ、システムのオーバーシュートを増加させます。積分定数が大きいほど、積分の累積効果が弱くなり、システムは移行中に振動しなくなりますが、積分定数を大きくすると、静的エラーを除去するプロセスが遅くなり、偏差を除去するのに必要な時間も長くなります、オーバーシュートを減らすことができますが、システムの安定性を向上させます。Ti が小さいと積分の効果が強くなり、このとき系の遷移時間に振動が生じることがありますが、ずれがなくなるまでの時間は短くなります。したがって、Ti は実際の制御の特定の要件に従って決定する必要があります。

微分:静的エラーを排除することに加えて、実際の制御システムでは調整プロセスを高速化する必要もあります。ズレが生じた瞬間、あるいはズレが変化した瞬間に、ズレに即座に対応すること(比例リンクの役割)だけでなく、状況に応じて事前に適切な補正を行う必要があります。偏差のトレンドを変更します。この機能を実現するために、PI コントローラーに基づいて差動リンクを追加して、PID コントローラーを形成することができます。差動リンクの役割は、偏差が変化しないようにすることです。偏差の変化傾向(変化速度)に合わせて制御します。偏差の変化が速いほど、差動コントローラの出力が大きくなり、偏差が大きくなる前に修正できます。微分動作の導入は、オーバーシュートを減らし、振動を克服し、特に高次システムの場合にシステムを安定させるのに役立ち、システムの追跡速度を高速化します。ただし、微分機能は入力信号のノイズに非常に敏感であるため、ノイズが大きいシステムでは、通常、微分は使用されないか、微分が機能する前に入力信号がフィルタリングされます。微分部の役割は、微分時定数 Td によって決まります。Tdが大きいほど偏差変化を抑制する効果が強く、Tdが小さいほど偏差変化を抑える効果が弱い。差動部分は明らかにシステムの安定性に大きな影響を与えます。微分定数 Td を適切に選択することで、微分動作を最適化できます。

デジタル PID 制御アルゴリズムは、位置 PID 制御アルゴリズムとインクリメンタル PID 制御アルゴリズムに分けることができます。次に、使用する PID アルゴリズムを決定する前に、まずそれぞれの原則を理解する必要があります。

2. 位置 PID アルゴリズム

e(k): ユーザー設定値 (目標値) - コントロール オブジェクトの現在の状態値

割合 Kp: e(k)

積分: ∑e(i)、誤差の累積

微分: e(k) - e(k-1)、このエラー - 最後のエラー

位置 PID の意味は、現在のシステムの実際の位置と、達成したい期待位置からの偏差であり、PID 制御が実行されます。

誤差積分 ∑e(i) が累積されているため、つまり、現在の出力 u(k) は過去のすべての状態に関連付けられており、誤差の累積値が使用されるため、出力 u(k) はアクチュエータに対応します。実際の位置は、一度制御出力が間違っている (制御対象の現在の状態値に問題がある) と、u(k) の大きな変化がシステムに大きな変化を引き起こし、位置型 PID が飽和に達すると、積分項、誤差は積分累積の作用の下で継続します。誤差が逆に変化し始めると、システムは飽和領域から出るのに一定の時間を必要とするため、u(k) が最大値と最小値に達するとき、積分動作を停止する必要があり、積分制限と出力制限が必要なので、使用位置で PID の場合、一般に PD 制御を直接使用しますが、位置 PID はアクチュエータに積分コンポーネントのないオブジェクトに適していますステアリングギア、アップライトバランスカー、温度制御システムの制御など

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

割合 KP: e(k)-e(k-1)、このエラー - 最後のエラー

積分 KI: e(i)、エラー

差分 KD: e(k) - 2e(k-1)+e(k-2)、この誤差 -2* 最後の誤差 + 最後の誤差

インクリメンタル PID の式によると、前後の 3 つの測定値の偏差を使用する限り、式によって得られる制御増分 ▲u(k) は、過去数回の位置誤差インクリメンタル、実際の位置からの偏差に対応する代わりに、誤差累積がない、つまり、インクリメンタル PID での累積は必要ありません。制御増分 Δu(k) の決定は、最新の 3 つのサンプリング値のみに関連し、重み付け処理によってより良い制御効果を得やすく、システムに問題が発生した場合、増分法は深刻な影響を与えません。システムの働き

4. 位置 PID とインクリメンタル PID の違いと長所と短所

4.1、両者の違い

1.増分アルゴリズムは累積を行う必要がなく、制御増分の決定は最後のいくつかの偏差サンプリング値にのみ関連し、計算誤差は制御量の計算にほとんど影響しません。ただし、位置アルゴリズムは過去の偏差の累積値を使用する必要があるため、累積誤差が大きくなりがちです。

2.インクリメンタルアルゴリズムは、制御量の増分を求める.例えば、バルブ制御では、バルブ開度の変化分のみを出力し、誤差動作の影響が少ない.必要に応じて、出力を制限することができる.または論理的判断により禁止されています。システムの動作に深刻な影響を与えることはありません。位置出力は、オブジェクトの出力に直接対応するため、システムにより大きな影響を与えます。

3. インクリメンタル PID 制御出力は、制御量の増分であり、積分機能を持たないため、ステッピングモータなど、アクチュエータ内に部品が組み込まれている場合に適しています。位置タイプの PID は、電気油圧式サーボ バルブなど、アクチュエータに一体型コンポーネントがないオブジェクトに適しています。

4. PID 制御を行う場合、位置 PID には積分リミッタと出力リミッタが必要ですが、インクリメンタル PID には出力リミッタのみが必要です。

4.2 位置 PID の長所と短所

アドバンテージ:

①位置 PID は、アクチュエータ (天秤トロリーなど) を直接制御できる非再帰アルゴリズムです. u(k) の値は、アクチュエータの実際の位置 (トロリーの現在の角度など) に対応します. したがって、一体型部品のないアクチュエーターを備えたオブジェクトで非常にうまく使用できます。

欠点:

①各出力は過去の状態に関連しており、計算時に e(k) を累積する必要があり、計算負荷が大きい。

4.3 インクリメンタル PID の長所と短所

アドバンテージ:

① 誤操作の影響が少なく、必要に応じて論理的な判断で間違ったデータを取り除くことができる。

②手動と自動の切り替え時の衝撃が小さく、邪魔にならず切り替えに便利です。コンピュータに障害が発生した場合でも、元の値を維持できます。

③数式に溜める必要はありません。制御増分 Δu(k) の決定は、最新の 3 つのサンプリング値のみに関連しています。

欠点:

①積分打ち切り効果が大きく、定常誤差がある。

②オーバーフローの影響が大きく、一部の制御対象はインクリメンタル方式であまり良くない。

次にコードを渡して可視化すると、このアルゴリズムの素晴らしさをより実感いただけるかと思いますが、ChatGPT4でコードを生成し、可視化したコードを追加することで、誰もが直感的に感じることができるようになっています。

5. 位置 PID コード

import numpy as np
import matplotlib.pyplot as plt

class PID:
  def __init__(self, Kp, Ki, Kd):
    self.Kp = Kp # 比例系数
    self.Ki = Ki # 积分系数
    self.Kd = Kd #微分系数
    self.last_error = 0 # 上一次的误差
    self.integral = 0 # 误差积分值
    
  def update(self, setpoint, position, dt):
    '''setpoint:目标值、position:当前位置、dt:时间步长'''
    self.setpoint = setpoint
    self.position = position
    self.dt = dt
    error = self.setpoint - self.position # 当前误差
    self.integral += error * dt # 积分项
    derivative = (error - self.last_error) / dt #微分项
    output = self.Kp * error + self.Ki * self.integral + self.Kd * derivative
    self.last_error = error
    self.position = output
    return self.position

  def fit_and_plot(self, count = 100):
    counts = np.arange(count)
    outputs = []
    for i in counts:
      outputs.append(self.update(self.setpoint,self.position,self.dt))
      print('Count %3d: output: %f' % (i, outputs[-1]))
    print('Done')
        
    plt.figure()
    plt.axhline(self.setpoint, c='red',ls='--')
    plt.plot(counts, np.array(outputs), 'g.')
    plt.ylim(min(outputs) - 0.1 * min(outputs), max(outputs) + 0.1 * max(outputs))
    plt.plot(outputs)
    plt.show()


pid = PID(0.2, 0.1, 0.01)
setpoint = 10.0
position = -5.0
dt = 0.5
pid.update(setpoint, position, dt)
pid.fit_and_plot(200)

6. インクリメンタル PID コード

import numpy as np
import matplotlib.pyplot as plt

class PID(object):
    def __init__(self, setpoint, position, dt, Kp, Ki, Kd) -> None:
        self.dt = dt  # 时间步长
        self.Kp = Kp  # 比例系数
        self.Ki = Ki  # 积分系数
        self.Kd = Kd  # 微分系数

        self.setpoint = setpoint
        self.position = position
        self._pre_error = 0  # t-1 时刻误差值
        self._pre_pre_error = 0  # t-2 时刻误差值

    def update(self):
        error = self.setpoint - self.position
        p_change = self.Kp * (error - self._pre_error)
        i_change = self.Ki * error
        d_change = self.Kd * (error - 2 * self._pre_error + self._pre_pre_error)
        delta_output = p_change + i_change + d_change  # 本次增量
        self.position += delta_output  # 计算当前位置

        self._pre_pre_error = self._pre_error
        self._pre_error = error

        return self.position

    def fit_and_plot(self, count=100):
        counts = np.arange(count)
        outputs=[]
        for i in counts:
            outputs.append(self.update())
            print('Count %3d: output: %f' % (i, outputs[-1]))
        print('Done')

        plt.figure()
        plt.axhline(self.setpoint, c='red',ls='-.',lw=2)  # 水平线,其中垂直线就是axvline
        plt.plot(counts, np.array(outputs), 'g.')
        plt.ylim(min(outputs) - 0.1 * min(outputs),max(outputs) + 0.1 * max(outputs))
        plt.plot(outputs)
        plt.show()

pid = PID(100, -80, 0.5, 0.2, 0.1, 0.001)
pid.fit_and_plot(150)

7. 無人車両で PID 駆動を使用する

無人機のドライバーファイルの書き方を見てみましょう. PID.pyのソースコードは以下のとおりです.

'''
@Copyright (C): 2010-2019, Shenzhen Yahboom Tech
@Author: Malloy.Yuan
@Date: 2019-07-30 20:34:09
@LastEditors: Malloy.Yuan
@LastEditTime: 2019-08-08 16:10:46
'''
#  PID控制一阶惯性系统测试程序
#  PPID control first-order inertial system test program
#*****************************************************************#
#                      增量式PID系统                              #
#                 Incremental PID system                          #
#*****************************************************************#
class IncrementalPID:
    def __init__(self, P, I, D):
        self.Kp = P
        self.Ki = I
        self.Kd = D
 
        self.PIDOutput = 0.0             #PID控制器输出
                                         #PID controller output
        self.SystemOutput = 0.0          #系统输出值
                                         #System output value
        self.LastSystemOutput = 0.0      #上次系统输出值
                                         #Last system output value
        self.Error = 0.0                 #输出值与输入值的偏差
                                         #Deviation between output value and input value
        self.LastError = 0.0
        self.LastLastError = 0.0
 
    #设置PID控制器参数
    #Set PID controller parameters
    def SetStepSignal(self,StepSignal):
        self.Error = StepSignal - self.SystemOutput
        IncrementValue = self.Kp * (self.Error - self.LastError) +\
        self.Ki * self.Error +\
        self.Kd * (self.Error - 2 * self.LastError + self.LastLastError)

        self.PIDOutput += IncrementValue
        self.LastLastError = self.LastError
        self.LastError = self.Error

    #设置一阶惯性环节系统  其中InertiaTime为惯性时间常数
    #Set the first-order inertial link system, where inertiatime is the inertial time constant
    def SetInertiaTime(self,InertiaTime,SampleTime):
        self.SystemOutput = (InertiaTime * self.LastSystemOutput + \
            SampleTime * self.PIDOutput) / (SampleTime + InertiaTime)

        self.LastSystemOutput = self.SystemOutput
 
 
# *****************************************************************#
#                      位置式PID系统                               #
#                   Position PID system                            #
# *****************************************************************#
class PositionalPID:
    def __init__(self, P, I, D):
        self.Kp = P
        self.Ki = I
        self.Kd = D
 
        self.SystemOutput = 0.0
        self.ResultValueBack = 0.0
        self.PidOutput = 0.0
        self.PIDErrADD = 0.0
        self.ErrBack = 0.0
    
    #设置PID控制器参数
    #Set PID controller parameters
    def SetStepSignal(self,StepSignal):
        Err = StepSignal - self.SystemOutput
        KpWork = self.Kp * Err
        KiWork = self.Ki * self.PIDErrADD
        KdWork = self.Kd * (Err - self.ErrBack)
        self.PidOutput = KpWork + KiWork + KdWork
        self.PIDErrADD += Err
        self.ErrBack = Err

    #设置一阶惯性环节系统  其中InertiaTime为惯性时间常数
    #Set the first-order inertial link system, where inertiatime is the inertial time constant
    def SetInertiaTime(self, InertiaTime,SampleTime):
       self.SystemOutput = (InertiaTime * self.ResultValueBack + \
           SampleTime * self.PidOutput) / (SampleTime + InertiaTime)
       self.ResultValueBack = self.SystemOutput
#导入模块PID.py
import PID
#创建控制实例
follow_speed_pid = PID.PositionalPID(1.5, 0, 0.05)
turn_gain_pid = PID.PositionalPID(0.15, 0, 0.05)

次に、PID コントローラーの終了 (元の値 + xservo_pid.SystemOutput) と開始値 (Now_value) を構成し、慣性定数 (InertiaTime)、サンプリング時間定数 (SampleTime)、および初期値 (original_value) を設定します。

慣性時定数 (InertiaTime): 簡単に言えば、慣性に打ち勝つのにかかる時間です。

サンプリング時定数 (SampleTime): PID コントローラーの最後の入力データと次の入力データの間の間隔を示します。これは、PID コントローラーのメイン関数に 1 回入るのにかかる時間とほぼ同じです。

follow_speed_pid.SystemOutput = Now_value
follow_speed_pid.SetStepSignal(Target_value)
follow_speed_pid.SetInertiaTime(InertiaTime, SampleTime)
target_valuex = int(original_value + follow_speed_pid.SystemOutput)

通过上面的步骤,基本的PID控制器就完成了

8、PID控制器调参

这个跟前面深度学习中的超参数等类似,要想得到一组合适的参数也需要进行调试,在PID中调参非常烦杂,下面是一些经验调参步骤:

1、确定比例系数Kp

确定比例系数Kp时,首先去掉PID的积分项和微分项,可以令Ti=0、Td=0,使之成为纯比例调节。输入设定为系统允许输出最大值的60%~70%,比例系数Kp由0开始逐渐增大,直至系统出现振荡;再反过来,从此时的比例系数Kp逐渐减小,直至系统振荡消失。记录此时的比例系数Kp,设定PID的比例系数Kp为当前值的60%~70%。

2、确定积分时间常数Ti

比例系数Kp确定之后,设定一个较大的积分时间常数Ti,然后逐渐减小Ti,直至系统出现振荡,然后再反过来,逐渐增大Ti,直至系统振荡消失。记录此时的Ti,设定PID的积分时间常数Ti为当前值的150%~180%。

3、确定微分时间常数Td

微分时间常数Td一般不用设定,为0即可,此时PID调节转换为PI调节。如果需要设定,则与确定Kp的方法相同,取不振荡时其值的30%。

4、系统空载、带载联调

对PID参数进行微调,直到满足性能要求。

网上流传着调试PID的口诀,如下:

参数整定找最佳, 从小到大顺序查。
先是比例后积分, 最后再把微分加。
曲线振荡很频繁, 比例度盘要放大。
曲线漂浮绕大弯, 比例度盘往小扳。
曲线偏离回复慢, 积分时间往下降。
曲线波动周期长, 积分时间再加长。
曲线振荡频率快, 先把微分降下来。
动差大来波动慢, 微分时间应加长。
理想曲线两个波, 前高后低四比一。
一看二调多分析, 调节质量不会低。

PID是比例(P)、积分(I)、微分(D)控制算法,并不是必须同时具备这三种算法,也可以是PD,PI,甚至只有P算法控制。

おすすめ

転載: blog.csdn.net/weixin_41896770/article/details/129744078