ゆるふわフィジカルAI
パソコンで作業中のゆるロボ

PID制御をPythonで実装する|ロボットを"ちょうどよく"目標へ動かす

2026-09-01実践

#PID制御#Python#制御#ロボット#実践

モーターを目標角度でピタッと止める、ドローンを高度に保つ——ロボットの「ちょうどよく動かす」を支える基本が PID制御 です。この記事ではNumPyだけでPIDをゼロから実装し、目標に収束する様子をシミュレーションで確かめます。理屈だけでなく、手を動かすと3つの項の役割が腹落ちします。

PIDの3つの項

PIDは「今の誤差(目標との差)」をもとに、次の3つを足し合わせて操作量を決めます。

誤差から操作量を作る3項

📏

P(比例)

誤差が大きいほど強く

🧮

I(積分)

たまった誤差を消す

🛞

D(微分)

行き過ぎを抑える

🕹️ ロボットの「動きの調整」を体験しよう(PD制御)

アームを⭐の位置にピタッと止めるのが目標。2つのつまみで調整します

🦾位置の変化(時間 →)目標

✅ いい感じ!素早く、行き過ぎずに止まっています

P=目標までの距離に比例した力/D=速度にブレーキをかける力。この2つの綱引きが制御の基本です

準備

pip install numpy

① PIDコントローラを実装する

3項を素直にコードにします。update は「目標値と現在値」を受け取り、操作量を返します。

class PID:
    def __init__(self, kp, ki, kd, dt):
        self.kp, self.ki, self.kd, self.dt = kp, ki, kd, dt
        self.integral = 0.0
        self.prev_error = 0.0

    def update(self, target, current):
        error = target - current
        self.integral += error * self.dt                 # I: 誤差を積み上げる
        derivative = (error - self.prev_error) / self.dt  # D: 誤差の変化率
        self.prev_error = error
        return self.kp * error + self.ki * self.integral + self.kd * derivative

② 動かす対象(プラント)を用意する

質量1の物体を、操作量(力)で目標位置1.0へ動かします。ごく単純な物理シミュレーションです。

dt = 0.01
pid = PID(kp=20.0, ki=5.0, kd=8.0, dt=dt)

x, v = 0.0, 0.0     # 位置・速度
target = 1.0
history = []
for _ in range(600):
    u = pid.update(target, x)   # PIDが力を決める
    v += u * dt                 # 加速度=力(質量1)
    x += v * dt
    history.append(x)

print("最終位置:", round(x, 4))         # ≈ 1.0
print("最大行き過ぎ:", round(max(history), 4))

実行すると 最終位置がほぼ 1.0 に収束します。目標にピタッと止まる、これがPID制御です。

🌱

各ゲインを1つずつ触ってみる

ki=0, kd=0 にしてPだけにすると、目標手前で止まったり振動したりします。そこにDを足すと行き過ぎ(オーバーシュート)が減り、Iを足すと「あと一歩」の定常誤差が消えます。1つずつ変えて max(history) の変化を見ると、各項の役割が体感できます。

③ ゲイン調整の勘所

  • Pを上げる → 反応は速いが、上げすぎると振動・不安定に
  • Iを上げる → 定常誤差を消すが、上げすぎると揺り戻し(ワインドアップ)
  • Dを上げる → ブレーキが効いて安定するが、上げすぎるとノイズに過敏
⚠️

実機ならではの注意

実際のロボットではセンサーにノイズが乗り、モーターには出せる力の上限があります。D項はノイズを増幅しがちなので、フィルタをかけたり、積分の暴走を防ぐ「アンチワインドアップ」を入れるのが定番です。シミュレーションで型を掴んでから実機へ、が安全です。

まとめ

  • PID=P(比例)・I(積分)・D(微分)の3項で操作量を決める
  • Pは反応、Iは定常誤差の解消、Dは行き過ぎ抑制を担当
  • NumPiだけで実装でき、目標へ収束する様子を確認できる
  • 実機ではノイズ・出力上限・アンチワインドアップに注意

もう少し詳しく(背景と理論)

PID制御の歴史は古く、1922年に Minorsky が艦船の自動操舵を解析したのが体系的な起源とされます1。ゲイン調整には Ziegler–Nichols 法などの古典的な手引きがありますが2、実機では理論値そのままでは振動しやすく、現場調整が要ります。実装上の要注意点は2つ——積分項が飽和して応答が鈍る積分ワインドアップ(アンチワインドアップで対処)と、目標値の急変で微分項が跳ねる微分キック(測定値のみ微分する等で回避)です3。ディジタル実装では連続系の式を離散化(後退差分など)し、サンプリング周期を制御対象の時定数より十分短く取る必要があります4

次の一歩 🌸

制御する相手のモーターはモーターの選び方、実装環境の準備はPython環境構築、歩行への応用は脚ロボットの歩行へどうぞ。

Footnotes

  1. Minorsky, N. (1922). "Directional stability of automatically steered bodies." J. Amer. Soc. Naval Eng., 34(2), 280–309. 比例・積分・微分の考え方を操舵に導入した先駆的研究。

  2. Ziegler, J. G. & Nichols, N. B. (1942). "Optimum Settings for Automatic Controllers." Trans. ASME, 64, 759–768. 限界感度法などの経験的チューニング則。

  3. 積分ワインドアップはアクチュエータ飽和時に積分値が過大化する現象。アンチワインドアップ(積分のクランプやバックカリキュレーション)で抑える。微分キックは目標値変化ではなく測定値を微分することで回避する。

  4. 離散PIDは e[k] の和(積分)と差分(微分)で近似する。サンプリング周期 Ts は系の時定数より十分短く(ナイキストの観点でも)取る必要がある。

あわせて読みたい