1
0

More than 5 years have passed since last update.

【世界最大の高校生ロボコン】FRCのプログラムについて~PID制御について~

Last updated at Posted at 2019-03-16

この記事はこちらの記事の一部です。まずはこちらをご覧ください。

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)

InputModulePIDSourceというインターフェースを継承した入力部分で、OutputModulePIDOutputを継承した出力部分である必要があります。
もう一つ必要なことがあるのですが、後ほど紹介します。

PID制御の処理の流れ

WPILibでのPID制御は別スレッドを立てて一ループ0.05秒で実行されます。(ちなみに本ループは0.20秒間隔)

毎ループ、入力部分から今の値を受け取って、それをもとに計算して、出力部分に出力します。

入力部分について

入力部分から今の値を受け取る際には、PIDSourcedouble pidGet()という関数を利用します。
もちろんPIDSourceはインターフェースなので、この関数をオーバーライドする必要があります。
ここにエンコーダなら距離を返す関数、ジャイロなら角度を返す関数とかを利用してdouble型にして返します。

他には、void setPIDSourceType(PIDSourceType pidSource)とか、PIDSourceType getPIDSourceType()もオーバーライドしなきゃいけません。
ただ、PIDSourceType

PIDSourceType.java
public enum PIDSourceType {
  kDisplacement,
  kRate
}

と、正直使い所無いので、適当にオーバーライドしましょう。

出力部分について

出力部分で出力する際には、PIDOutputvoid 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

最後に。

説明下手もあり、だいぶわかりにくかったと思います。申し訳ないです。
書いてるとき眠かったんです(言い訳)
まあわからなくてもどうにかなります。
では。

前の記事⇒Main関係
次の記事⇒Drive関係

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0