前の回では、ずれに比例して曲がる比例制御でライントレースをしました。ただ、2つの問題が残りました。線へもどるときに何度も行きすぎるハンチングと、カーブで残るずれです。今回は、比例制御に2つの数字を足して、この2つを片づけます。それが PID 制御です。
P・I・D を「いま・これまで・これから」として説明する考え方は、次の記事を参考にしています。
オンオフ制御の欠点を補う「PID制御」とは?(MONOist「ETロボコンではじめるシステム制御」第3回)比例制御で残った2つの問題
| 問題 | 起きること | Kp だけで直そうとすると |
|---|---|---|
| ハンチング | 線へもどるとき、何度も行きすぎてから落ち着く。Kp を上げるほど増える | Kp を下げる。するとカーブでずれる |
| 残るずれ | カーブで、線の外側へずれたまま走る | Kp を上げる。するとハンチングが増える |
Kp を上げても下げても、どちらかが残ります。Kp 1つでは、両方を同時に直せません。
PID の式 ― 3つを足す
| 曲がる量 = Kp × ずれ + Ki × ずれの合計 + Kd × ずれの変わる速さ |
| 見るもの | 役目 | |
|---|---|---|
| P(比例) | いまのずれ | ずれた分だけ、もどす |
| I(積分) | これまでのずれの合計 | 残りつづけるずれを、消す |
| D(微分) | ずれの変わる速さ | もどりすぎる前に、ブレーキをかける |
P がいまを見るのに対して、I はこれまでを、D はこれからを見ています。
Kp・Ki・Kd は、それぞれをどれくらい効かせるかを決める数字で、まとめてゲインといいます。Kp が比例ゲイン、Ki が積分ゲイン、Kd が微分ゲインです。
D ― ずれの変わる速さにブレーキをかける
ハンチングが起きるのは、線にもどる勢いがついたまま、線を通りこすからです。
D は、ずれが小さくなっていく速さを見ます。速く線へもどっているほど、反対向きに曲がる量を足します。線の手前で勢いがゆるみ、通りこさずに止まります。
Kd を入れすぎると、こんどは細かくガタつきます。センサーの読みの小さなゆれまで、大きな動きにしてしまうからです。
I ― たまったずれを消す
カーブでずれが残るのは、比例制御ではずれが 0 だと曲がる量も 0 になるからでした。カーブを曲がりつづけるには、ずれを少し残しておくしかありません。
I は、ずれをずっと足しつづけます。外側へずれたままだと、合計がどんどんふえ、曲がる量もふえていきます。ずれが 0 になっても、それまでの合計が残るので、ずれがなくても曲がりつづけられます。
Ki を入れすぎると、合計がふえすぎて、大きく振れます。また、ロボットを持ち上げたり止めたりすると、そのあいだのずれまで合計にたまります。走りはじめる前に、合計を 0 にもどします。
実験室で確かめる ― Kp・Ki・Kd を動かす
①〜④を順に押すと、ここまでの話が順に見られます。
曲がる量の内訳
線からのずれ(さいきんの12秒)
まん中の線が、線の真上。うすい帯が線の太さです。上下に波打っていれば、ロボットは振れています。計算で動かしているので、ここで出した Kp・Ki・Kd は実物のロボットではそのまま使えません。
| やってみること | 見えること |
|---|---|
| ① Kp を小さく | 線の外側へずれたまま走る |
| ② Kp を大きく | ずれは減るが、左右に振れる |
| ③ Kd を足す | 振れがおさまる |
| ④ Ki を足す | 残っていたずれが消える |
| Kd を 0.8 まで上げる | 細かくガタつく |
| Ki を 40 まで上げる | 大きく振れる |
「曲がる量の内訳」を見ると、P・I・D がそれぞれどれだけ効いているかが分かります。円のコースで④にすると、走っているあいだ I が一定の量を出しつづけます。ずれがなくても曲がれるのは、この I のおかげです。
プログラム ― 比例制御に数行足すだけ
from pybricks.pupdevices import Motor, ColorSensor
from pybricks.parameters import Port, Direction
from pybricks.tools import wait
left_motor = Motor(Port.A, Direction.COUNTERCLOCKWISE) # 左は逆向きに付いている
right_motor = Motor(Port.B)
left = ColorSensor(Port.C)
right = ColorSensor(Port.D)
KP = 2.0 # ゲインは目安の値
KI = 4.0
KD = 0.2
BASE = 200 # 基準の速さ(1秒に200度)。目安の値
DT = 0.02 # 20ミリ秒ごとに1回
integral = 0
last_error = 0
while True:
error = right.reflection() - left.reflection() # いまのずれ
integral = integral + error * DT # ずれの合計
derivative = (error - last_error) / DT # ずれの変わる速さ
turn = KP * error + KI * integral + KD * derivative
left_motor.run(BASE - turn)
right_motor.run(BASE + turn)
last_error = error
wait(20)
| 書き方 | やっていること |
|---|---|
| integral + error * DT | ずれを、時間をかけて足していく(I) |
| (error - last_error) / DT | 前の回からどれだけ変わったかを、時間で割る(D) |
| last_error = error | 次の回のために、いまのずれを覚えておく |
| wait(20) | 20ミリ秒ごとに1回。DT と合わせておく |
ゲインと BASE は目安の値です。ロボットとコースに合わせて、次の節の順番で調整します。
前の回の比例制御に、integral と derivative の行が足されただけです。KI と KD を 0 にすれば、比例制御にもどります。
ゲインを決める順番 ― P、D、I の順に
3つを一度に動かすと、どれが効いたのか分からなくなります。1つずつ、決まった順に決めます。
| やること | |
|---|---|
| 1 | Ki と Kd を 0 にして、Kp を上げていく。ハンチングが出はじめたところで止める |
| 2 | Kd を少しずつ足して、ハンチングをおさえる。ガタつきはじめたら少しもどす |
| 3 | カーブでずれが残るなら、Ki を少しだけ足す |
| 4 | 基準の速さを上げたら、1 からやりなおす |
実験室の数字は使わず、自分のロボットを走らせて探します。
動かないときに見るところ ― 症状から原因を引く
| 症状 | 原因 | 対処 |
|---|---|---|
| 左右に大きく振れる | Kp か Ki が大きすぎる | Ki を 0 にして、Kp から決めなおす |
| 細かくガタつく | Kd が大きすぎる | Kd を下げる |
| 走りだしてすぐ、急に曲がる | ずれの合計がたまったまま | 走りはじめる前に integral = 0 にする |
| D を足しても何も変わらない | last_error を更新していない | ループの最後で last_error = error を書く |
| うしろへ走る。その場で回る | モーターの回る向きの指定が、実機と合っていない | Direction.COUNTERCLOCKWISE を、右のモーターに付けかえる |
| 線から逃げるように曲がる | 曲がる向きが逆 | BASE - turn と BASE + turn を入れかえる |
ロボカップジュニア2027 に向けて、レスキュー(災害救助)とサッカーの2種目に取り組みます。プログラミングと電子工作の経験は問いません。
前の回:ON-OFF制御と比例制御 ― なめらかに線をたどる →
ロボサイエンスの記録一覧 →
体験授業・お問い合わせ →