NOBのArduino日記!

NOBのArduino日記!

趣味は車・バイク・自転車・ラジコン・電子工作です。

PID制御!(PID_AutoTune_v0.hのインストール編)

イメージ 2
PID制御とは?編」で触れていますが、PID制御での肝は三つのパラメータ「Kp,Ki,Kd」を決める事です!
 しかし、パラメータを手動で調整するのは大変ですし、PID制御を使おうとしている場所の条件が毎回異なる場合も想定されます。
 そんな時に便利な「オートチューニング法」について、良いライブラリを発見しましたので使ってみました!

※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
図1:GitHubでライブラリを入手!

1.2 フォルダ名の変更
  1.1でダウンロードしたZIPフォルダそのままではArduinoIDEにインストール出来ませんので、一度ZIPファイル(図2)から中身(図3)を取り出します。
 次に「Arduino-PID-AutoTune-Library-master」と言うフォルダ中に有る「PID_AutoTune_v0フォルダ(図4)と、「README」テキストファイルを取り出します。
 最後に「PID_AutoTune_v0」フォルダに「README」テキストファイルを移します。
 
イメージ 2 → イメージ 3 → イメージ 4
     図2:ZIPファイル    図3:中身    図4:リネイム
 
1.3 インストール
 図4で用意した「PID_AutoTune_v0」フォルダを、ArduinoIDEがインストールされているフォルダ(図5)中の「Libraries」フォルダ中に移動するだけでインストール終了です!
イメージ 5
図3:ライブラリのインストール

2. 実験!

 PID ATライブラリがどんな感じか実験してみました!
 
2.1 接続
 図6の様にArduinoUNOの各ピンには何も接続しなくてOKです!
イメージ 8
図6:ArduinoUNO

2.2 サンプルプログラム
 PID ATライブラリを使ったサンプルプログラムが「platformio」のサイトに公開されていましたので使わせて頂きました(人''▽`)ありがとう☆ございます!
イメージ 5
図7:PID ATライブラリのサンプルプログラム
 
 ArduinoUNO単体に下記プログラムを書き込みます。
 プログラムの内容としては、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
図1:プログラム例
 
 
2.3 測定
 上記プログラムを実行すると、設定値(setpoint)・測定値(input)・出力値(output)・比例項(Kp)・積分項(Ki)・微分項(Kd)が図8の様にPCのシリアルモニタ上に出力されます。
イメージ 7
図8:Arduino IDE シリアルモニター

2.4 結果
 図8で得られた出力結果をEXCELでグラフ化したものを図9に示します。
 グラフより、今回設定した180と言う目標に対してPIDライブラリが自動的に制御量を調整し、測定値が180に達した時点で安定している様子が確認されました!
イメージ 6
図9:図8グラフ化

3. まとめ

 PID ATライブラリなんか凄そうです!
 しかしサンプルプログラム中の「analogWrite(0,output);」をして「analogRead(0);」をして、図9のグラフの様に上手く制御出来ているのがイマイチ理解できません!?
 //compute the inputあたりが怪しいですがなぜこれで動くのでしょうか
 
イメージ 1 イメージ 3
励みになりますのでよければクリック下さい(^o^)/

↩【PID制御!】目次に戻る