2024年11月21日。ArduinoでPID温度制御。
2リットルの鍋にしてから、温度調節がうまくいきません。以前600mLの鍋でPID制御をしてみたことがあったけど、オーバーシュート(上がりすぎ)やハンチング(上がり下がり)がひどくて、単純な「設定温度未満ならON、設定温度以上ならOFF」だけで変動幅±0.5℃程度の正確さが得られたことに比べて見劣りがしたので、利用検討をやめた経緯があります。
手さぐりで、水温が45℃まで温まったらヒーターをOFFにすると、約20分遅れで約60℃に達することが分かりました。この調子で、「47℃くらいでヒーターOFF → ピークに達した後 → 63℃以上ならヒーターOFF → 63℃未満なら最弱(Duty比=0.4%;PWMで1)でON」という制御をしようとしたら、どうしてもArduinoに定期的温度モニタリングと温度制御のマルチタスクをさせなければならない、と思うようになりました。
そのことを知り合いに話すと、「それこそPID制御ですれば良いよ。」とのこと。
で、反省を込めて、改めてPIDで温度制御を検討することにしました。
自作300Wニクロム線ヒーターが熱で壊れたので、AliExpressで怪しげな電気コンロを買ったところ、回路の5Aのフューズが飛んだ(多分1000W)ので、ArduinoのDuty比を最大50%までとして使っています。
以下にArduinoのスケッチを示します。
// file name: PWM_PID_Heater_v1_20241120.ino
// 0.2秒ごとにAnalog出力をしています。
// 設定温度は変更可能です。
// P、I、D の係数も変更可能です。各々0.5〜2 の範囲で変えてみてください。
// オーバーシュートやハンチングのパターンが変わります。
// Iを小さくすると、オーバーシュートが小さくなります。
// 対応するPythonコードは"Data_from_Arduino_vF.py"です。
// 温度の経時的変化を120分までグラフ化します。
// 2024.07.31 by Kero
// 2024.11.20 改訂 by Kero
// 温度センサDS18B20の設定(以下のサイトからもらいました。感謝)
// https://github.com/matmunk/DS18B20?tab=readme-ov-file
#include <PID_v1_bc.h>
#include <DS18B20.h> // センサからの読み込み値はセ氏温度です
DS18B20 ds18(2); // センサの黄線はArduinoの端子2につなぎます
// PID制御の設定。Google Gemini に教えてもらいました。
double Setpoint, Input, Output;
PID myPID(&Input, &Output, &Setpoint, 1, 1, 1, DIRECT); // P=1, I=1, D=1
// ディスプレイLCDの設定
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);
// 変数割り当て
int SSR = 6; // SSRのアドレス
float sett = 63; // 設定温度
float Duty; // Duty比
// Duty比からアナログ用値に変換は「ヒーターの出力」で
// 変数宣言
String ds18C; // センサからの読込は文字列変数「ds18C」に入ります
float ds18CV; // その文字列を実数に変換して入れる変数です
void setup() {
Serial.begin(9600); // 通信速度セットアップ
// PIDの初期化
Setpoint = sett; // 設定温度
myPID.SetOutputLimits(0, 127); // 出力範囲を0から127に設定
myPID.SetMode(AUTOMATIC); // Google Gemini の言う通り。
// LCDディスプレイ用セットアップ
lcd.init();
lcd.backlight();
// ピンモードを「出力」に設定(不要かも?)
pinMode(SSR,OUTPUT);
}
void loop() {
// DS18センサから読み込み(とにかくこう書くようです)
while (ds18.selectNext()) {
ds18C = ds18.getTempC(); // 文字列でds18Cに読み込み
ds18CV = ds18C.toFloat(); // 文字列を実数に変換
Serial.println(ds18C); //シリアルモニタに温度を文字列として出力
}
// PIDの計算
Input = ds18CV;
myPID.Compute(); // Dのための前値はどうなっているの?
// ヒーターの出力
analogWrite(SSR, Output);
Duty = map(Output, 0, 127, 0, 49);
// LCDへADT温度とDuty比の表示
lcd.setCursor(0,0);
lcd.print("TempSD18= ");
lcd.print(ds18C);
lcd.setCursor(15,0);
lcd.print("C");
lcd.setCursor(0,1);
lcd.print("Duty = ");
lcd.setCursor(10,1);
lcd.print(Duty);
lcd.setCursor(15,1);
lcd.print('%');
delay(200); // 0.2秒そのまま
}
Arduinoで、ライブラリ「PID_v1_bc」のPID係数を 1,1,1 にして、SetMode = AUTOMATICで動かしています。
P、I、D の各パラメータを1、1、1 にした場合の温度変化グラフを下に示します。
対象:水が2L入っている金属鍋。撹拌のために、熱帯魚水槽用のエアポンプを投入。
制限:ArduinoのPWMは0〜255の整数値で指定。Duty比は0〜100%に対応。今回は49%までに制限。
グラフの横軸は経過時間(分)、縦軸は温度(℃)。
【結果】
★ 65℃くらいまでヒーターON。
★ 最初のピークは30分で78℃。15℃のオーバーシュート。
★ 以降、だんだん落ち着いては来ている。