この記事はこちらの記事の一部です。まずはこちらをご覧ください。
##FIRST
最初に。
PID制御の話になります。
彡(゚)(゚)「PID制御なんてもう知っとるわ!いらんいらん」
という方はこちらのWPILibでのPID制御の扱いまで。
普通にプログラム書くのとだいぶ違う書き方になりますので...
PID制御は初見だと理解しづらいと思います。
言ってることを汲み取ってふーんこんなんなんだなと思って読んでください。
詳しい仕組みはわからなくてもコードはかけると思いますし、触れてきゃわかるようになります(適当)
##PID制御とは
N個記事があると思うのでそちらに任せます()
僕がわかりやすいと思ったのを挙げておきます。
ただ、微積分の心をわかってないとどちらにしろ難しいので、軽く。
微分というのは、値の増え具合(グラフでいうなら傾き)を求める操作。
積分というのは、PID制御においては目標値との差の合計(グラフでいうなら目標値と計測値の間の面積)
これは結構簡潔
PID制御って何?
これは後半はつらいかも
モータのPID制御法
これは数学とか用語を知っとかないと難しいかもしれない
PID制御について宇宙一わかりやすく解説してみる
##WPILibでのPID制御の扱い
PID制御について理解していただいたところで、プログラム的な話になります。
その前に用語について整理しておきたいと思います。
エアコンに例えた場合、
温度センサーを、入力部分
空気を送るところを、出力部分
kp, ki, kdとかで示されるものを、ゲイン
と呼びます。
###基本
WPILibではPID制御はPIDController
というクラスで行います。
pidController.setPoint(setpoint)
というように目標値を設定して動かします。
そこで必要なのがゲインと入力部分と出力部分ですね。
これはコンストラクタにて設定します。
new PIDController(kp, ki, kd, SourceModule, OutputModule)
InputModule
はPIDSource
というインターフェースを継承した入力部分で、OutputModule
はPIDOutput
を継承した出力部分である必要があります。
もう一つ必要なことがあるのですが、後ほど紹介します。
###PID制御の処理の流れ
WPILibでのPID制御は別スレッドを立てて一ループ0.05秒で実行されます。(ちなみに本ループは0.20秒間隔)
毎ループ、入力部分から今の値を受け取って、それをもとに計算して、出力部分に出力します。
####入力部分について
入力部分から今の値を受け取る際には、PIDSource
のdouble pidGet()
という関数を利用します。
もちろんPIDSource
はインターフェースなので、この関数をオーバーライドする必要があります。
ここにエンコーダなら距離を返す関数、ジャイロなら角度を返す関数とかを利用してdouble
型にして返します。
他には、void setPIDSourceType(PIDSourceType pidSource)
とか、PIDSourceType getPIDSourceType()
もオーバーライドしなきゃいけません。
ただ、PIDSourceType
は
public enum PIDSourceType {
kDisplacement,
kRate
}
と、正直使い所無いので、適当にオーバーライドしましょう。
####出力部分について
出力部分で出力する際には、PIDOutput
のvoid pidWrite(double output)
という関数を利用します。
もちろんPIDOutput
はインターフェースなので、この関数をオーバーライドする必要があります。
引数のoutput
をモーターに代入したりします。
代入はこの関数内で閉じてなければいけません
うっかり本スレッドで代入しちゃうと0.20秒ごとの代入で、PID制御の制度がだいぶ落ちます
###PIDControllerの使い方
インスタンスを代入して使えるようになったところで使っていきます。
pidController.setSetpoint(double setpoint)
で目標値(Setpoint)を設定(set)して、
pidController.enable()
で有効にすれば動くようになります。
ちなみにSetpointはpidGet()
で単純に入力部分の値を受け渡すようにした場合、その入力部分を初期化した状態を基準とした絶対座標で代入する形になります。
足回りのモーターを例として考えると、
仮にエンコーダをスタート地点で初期化した場合、どれだけ動こうとも、目標値はスタート地点を基準とした距離という扱いになります。
エンコーダーを初期化してから、前に1m進んだ後、そこから2m下がりたいなと思った場合、
pidController.setSetpoint(-2.0)
とか入力するとそこから3m下がってしまいます。
正しく動くようにするには、
pidController.setSetpoint(-1.0)
、もっというならpidController.setSetpoint(1.0 - 2.0)
という代入をします。
###補足
ロボットをまっすぐ走らせたいと思ったら足回りのPID制御は二つ必要になります。
前に進むためのものと、横ずれしないためのものとで分ける必要があります。
##LAST
最後に。
説明下手もあり、だいぶわかりにくかったと思います。申し訳ないです。
書いてるとき眠かったんです(言い訳)
まあわからなくてもどうにかなります。
では。