「PID制御とは?編」で触れていますが、PID制御での肝は三つのパラメータ「Kp,Ki,Kd」を決める事です!
しかし、パラメータを手動で調整するのは大変ですし、PID制御を使おうとしている場所の条件が毎回異なる場合も想定されます。
そんな時に便利な「オートチューニング法」について、良いライブラリを発見しましたので使ってみました!
1. PID ATライブラリのインストール
1.1 ダウンロード
GitHubと言うサイトに「br3ttb/Arduino-PID-AutoTune-Library」と言うページが有ります。
PIDライブラリはこのページで入手します。
ダウンロード方法は図1右側に有る緑のボタン「Clone or download」をクリックし、次に「Download ZIP」をクリックすれば「Arduino-PID-AutoTune-Library-master」と言うPID ATライブラリが含まれたZIPファイルがダウンロードされます。
図1:GitHubでライブラリを入手!
1.2 フォルダ名の変更
次に「Arduino-PID-AutoTune-Library-master」と言うフォルダ中に有る「PID_AutoTune_v0」フォルダ(図4)と、「README」テキストファイルを取り出します。
最後に「PID_AutoTune_v0」フォルダに「README」テキストファイルを移します。
→ →
図2:ZIPファイル 図3:中身 図4:リネイム
1.3 インストール
図3:ライブラリのインストール
2. 実験!
PID ATライブラリがどんな感じか実験してみました!
2.1 接続
図6の様にArduinoUNOの各ピンには何も接続しなくてOKです!
図6:ArduinoUNO
2.2 サンプルプログラム
PID ATライブラリを使ったサンプルプログラムが「platformio」のサイトに公開されていましたので使わせて頂きました(人''▽`)ありがとう☆ございます!
図7:PID ATライブラリのサンプルプログラム
プログラムの内容としては、3番ピンで発生したPWM信号を2番ピンで測定し、その結果から3番ピン出力を、設定したDuty比70.6%(180)となるように操作量をPIDライブラリで求め出力します・・・。
と、ここまでは前回と同じですが、内部的に勝手にPIDのパラメータ「Kp、Ki、Kd」を状況に応じて調整してくれると言う優れものです!
#include <PID_v1.h> #include <PID_AutoTune_v0.h> byte ATuneModeRemember = 2; double input = 80, output = 50, setpoint = 180; double kp = 2, ki = 0.5, kd = 2; double kpmodel = 1.5, taup = 100, theta[50]; double outputStart = 5; double aTuneStep = 50, aTuneNoise = 1, aTuneStartValue = 100; unsigned int aTuneLookBack = 20; boolean tuning = false; unsigned long modelTime, serialTime; PID myPID(&input, &output, &setpoint, kp, ki, kd, DIRECT); PID_ATune aTune(&input, &output); //set to false to connect to the real world boolean useSimulation = true; void AutoTuneHelper(boolean start) { if (start) ATuneModeRemember = myPID.GetMode(); else myPID.SetMode(ATuneModeRemember); } void changeAutoTune() { if (!tuning) { //Set the output to the desired starting frequency. output = aTuneStartValue; aTune.SetNoiseBand(aTuneNoise); aTune.SetOutputStep(aTuneStep); aTune.SetLookbackSec((int)aTuneLookBack); AutoTuneHelper(true); tuning = true; } else { //cancel autotune aTune.Cancel(); tuning = false; AutoTuneHelper(false); } } void SerialSend() { Serial.print("setpoint: "); Serial.print(setpoint); Serial.print(" "); Serial.print("input: "); Serial.print(input); Serial.print(" "); Serial.print("output: "); Serial.print(output); Serial.print(" "); if (tuning) { Serial.println("tuning mode"); } else { Serial.print("kp: "); Serial.print(myPID.GetKp()); Serial.print(" "); Serial.print("ki: "); Serial.print(myPID.GetKi()); Serial.print(" "); Serial.print("kd: "); Serial.print(myPID.GetKd()); Serial.println(); } } void SerialReceive() { if (Serial.available()) { char b = Serial.read(); Serial.flush(); if ((b == '1' && !tuning) || (b != '1' && tuning))changeAutoTune(); } } void DoModel() { //cycle the dead time for (byte i = 0; i < 49; i++) { theta[i] = theta[i + 1]; } //compute the input input = (kpmodel / taup) * (theta[0] - outputStart) + input * (1 - 1 / taup) + ((float)random(-10, 10)) / 100; } void setup() { if (useSimulation) { for (byte i = 0; i < 50; i++) { theta[i] = outputStart; } modelTime = 0; } //Setup the pid myPID.SetMode(AUTOMATIC); if (tuning) { tuning = false; changeAutoTune(); tuning = true; } serialTime = 0; Serial.begin(9600); } void loop() { unsigned long now = millis(); if (!useSimulation) { //pull the input in from the real world input = analogRead(0); } if (tuning) { byte val = (aTune.Runtime()); if (val != 0) { tuning = false; } if (!tuning) { //we're done, set the tuning parameters kp = aTune.GetKp(); ki = aTune.GetKi(); kd = aTune.GetKd(); myPID.SetTunings(kp, ki, kd); AutoTuneHelper(false); } } else myPID.Compute(); if (useSimulation) { theta[30] = output; if (now >= modelTime) { modelTime += 100; DoModel(); } } else { analogWrite(0, output); } //send-receive with processing if it's time if (millis() > serialTime) { SerialReceive(); SerialSend(); serialTime += 500; } }
図1:プログラム例
2.3 測定
3. まとめ
PID ATライブラリなんか凄そうです!
しかしサンプルプログラム中の「analogWrite(0,output);」をして「analogRead(0);」をして、図9のグラフの様に上手く制御出来ているのがイマイチ理解できません!?
//compute the inputあたりが怪しいですがなぜこれで動くのでしょうか
励みになりますのでよければクリック下さい(^o^)/