ペルチェ温度コントローラーの製作(機能・ソフト解説)
◆まえがき
少し間が空いてしまいましたが、ペルチェ温度コントローラー作りの話の続きです。前回は基板とケース作りでしたが、今回は機能説明とソフトの解説です。
・ペルチェ温度制御装置の外観

◆機能説明
・操作パネル

下段には電源スイッチとOK表示(stabilized 表示)LEDを付けました。このLEDは実際の温度が設定値の±0.2℃以内に入った時に点灯します。上の段は操作スイッチでリセットとスタートボタン、項目選択つまみです。
電源を入れて、実行したい項目を選び、その後にスタートスイッチを押すことで実際に温度制御が開始されるようになっています。こうしておくと、電源を入れただけで加熱(冷却)運転が始まることは無いので安全です。なお、電源スイッチがあるのでリセットボタンは無くても良いのですが、あれば便利かもしれないので付けておきました。
◆表示パネル
・機能選択画面
現在の機能は下記の5種類です。最後に選んだ機能はEEPROMに記録しているので、次回の起動時は最初からその機能が選択されています。機能を追加・変更したい時はコード書いて再コンパイルです。
1. マニュアル :セレクトつまみで温度を設定します。設定値は運転中に任意に変更可能
2. プログラム1:10℃→60℃→10℃を10℃ステップ、各ステップ5分間ホールドの繰り返し。1サイクル50分
3. プログラム2:10℃→60℃→10℃を2℃/分の速度で昇温/降温、上下端で5分ホールドの繰り返し。1サイクル60分
4. プログラム3:10℃/60℃を最高速度で繰り返す。各温度で1.5分ホールド。1サイクル3分
5. プログラム4:20℃/50℃を最高速度で繰り返す。各温度60秒ホールド。1サイクル2分
・機能選択画面

以下同様
・運転中の画面表示

128x32画素のOLEDに出来るだけ多くの情報を詰め込みました。
・運転時間はスタートからの累積時間を秒で表示します。なおマニュアルでも表示。
・ペルチェのパワーは±100%の数値で示すと共に、一番下にバーグラフでグラフィックに表示しています。左に振れると冷却、右に振れると加熱ですが、直感的に判り易いです。
◆プログラム
こちら → ペルチェコントローター制御プログラム 20191221peltierTempCon (shift-JIS です)
【2023/03/23追記】184行目は下記に修正
誤:V = x * 1.1 / 10230.0; // 平均電圧の計算 (1.1V=1023)(10回ループで1.2ms)
正:V = x * 1.1 / 10240.0; // 平均電圧の計算 (1.1V=1024)(10回ループで1.2ms)
機能が多いので長めのプログラムになっています。全体のポイントは以下の通りです。
・タイミングはMsTimer2で5ms周期の割り込みで制御
・ペルチェのパワーはフルブリッジを使ったPWM制御 (Arduino の analogWrite )
・温度はPID制御
◆PID制御
Arduino のライブラリもあったのですが、使い方が判り難かったのと、制御出力が正負に対応していない感じがしたので、自前の関数を作りました。そうはいっても、計算は離散値PID制御で標準的に使われている式でやっています。なお、PIDのパラメーターはプログラム先頭付近の #define の Kp, Ki, Kd の値で定義しています。
一般的な計算式を使ったといっても少しカストマイズしているので、自分用のメモを兼ねて中の仕組みを解説しておきます。
PID制御の出力はfloat pid( ) 関数の戻り値となっています。この関数が制御のキモなのでコードを見ながら説明します。
1) PIDの制御量計算
この関数では毎回制御量を計算し、30行の x = lastMv + dMv; で実際のペルチェの出力を決定しています。この式は過去の計算結果が累積されるので積分型になっています。こういう方式にしておくことで過去のデーターを大量に保存しなくて済み、プログラムが簡単になるのだと思います。
積分した結果でPIDの各要素の効果を発揮させるということは、それぞれの要素はあらかじめ1回微分した値を用意しないといけません。つまり、P(比例)は誤差の微分で、I(積分)は誤差そのままで、D(微分)は誤差を2回微分した値で準備します。dP, dI, dD の各要素はそういう意図で計算されているのですが、最初はこういう理屈が判らなくて悩みました。
2) アンチワインディング
例えば設定を70℃とした場合、実際に到達可能な温度の上限は約65℃なので誤差はゼロになりません。こういう状態が続くと積分項の効果はどんどん蓄積されるので制御量は非常に大きな値になってしまいます。そういう状態から例えば設定値を50℃に下げたとします。当然制御量を減らして追従しようとしますが、すでに非常に大きな値が入っているのでなかなか制御量が減りません。その結果反応時間が遅れてしまいます。これをワインドアップ現象と言います。
そういう問題を無くす方法はいろいろあるようですが、34から39行の処理で制御量の上限を制限しました。
3) 設定値変化速度の制限
以上のような方法でアンチワインドアップ対策を行うと計算結果の情報の一部が失われることになります。一番影響が出るのが微分項です。最初の変化は無視されてしまって、後半の変化量だけの情報が反映されるようなことが起きてしまいます。その結果、極端な場合は操作方向が逆になってしまうようなことまで起きてしまいます。そういう場合でも、時間が経てば誤差ゼロの状態に収束しますが、応答時間が長くなって好ましくありません。
対策はいろいろ考えられそうですが、手っ取り早い処置として、操作の変化速度の上限を制限することにしました。具体的には9~15行までの処理で、ここで1回、つまり0.2秒あたりの変化量を最大で2℃に制限しました。
◆まとめ
・PID制御のプログラムはもっと美しい実装がありそうですが、とりあえず動いているのでまあこれで良いかと思っています。PID制御を本格的に勉強しようとしている学生には非常に良い教材なので、もっと性能の良い実装を考えてもらうと良いと思います。
・ダイソーの3.4AのUSB充電器を何かうまいことに使えないだろうか?というかなり不純な動機で始めたペルチェ温度コントローラーですが、やってみるとなかなか面白かったです。これ作らなかったらPID制御なんてやらなかったので良い経験をさせてもらいました。
・表示に小型のOLEDを使いましたが、表現力が高く、グラフィックな表示も出来るのでなかなか使い易いです。ただ1倍角の文字は小さすぎて読みづらいので、縦だけ2倍角の文字、つまり幅6画素で高さ16画素の文字が表示できるともっと表現の幅が広がると思います。誰かそういうライブラリを作ってくれないですかね。ひょっとしたら Adafruit_SSD1306 で出来るのかも知れないのですが。
少し間が空いてしまいましたが、ペルチェ温度コントローラー作りの話の続きです。前回は基板とケース作りでしたが、今回は機能説明とソフトの解説です。
・ペルチェ温度制御装置の外観

◆機能説明
・操作パネル

下段には電源スイッチとOK表示(stabilized 表示)LEDを付けました。このLEDは実際の温度が設定値の±0.2℃以内に入った時に点灯します。上の段は操作スイッチでリセットとスタートボタン、項目選択つまみです。
電源を入れて、実行したい項目を選び、その後にスタートスイッチを押すことで実際に温度制御が開始されるようになっています。こうしておくと、電源を入れただけで加熱(冷却)運転が始まることは無いので安全です。なお、電源スイッチがあるのでリセットボタンは無くても良いのですが、あれば便利かもしれないので付けておきました。
◆表示パネル
・機能選択画面
現在の機能は下記の5種類です。最後に選んだ機能はEEPROMに記録しているので、次回の起動時は最初からその機能が選択されています。機能を追加・変更したい時はコード書いて再コンパイルです。
1. マニュアル :セレクトつまみで温度を設定します。設定値は運転中に任意に変更可能
2. プログラム1:10℃→60℃→10℃を10℃ステップ、各ステップ5分間ホールドの繰り返し。1サイクル50分
3. プログラム2:10℃→60℃→10℃を2℃/分の速度で昇温/降温、上下端で5分ホールドの繰り返し。1サイクル60分
4. プログラム3:10℃/60℃を最高速度で繰り返す。各温度で1.5分ホールド。1サイクル3分
5. プログラム4:20℃/50℃を最高速度で繰り返す。各温度60秒ホールド。1サイクル2分
・機能選択画面



・運転中の画面表示

128x32画素のOLEDに出来るだけ多くの情報を詰め込みました。
・運転時間はスタートからの累積時間を秒で表示します。なおマニュアルでも表示。
・ペルチェのパワーは±100%の数値で示すと共に、一番下にバーグラフでグラフィックに表示しています。左に振れると冷却、右に振れると加熱ですが、直感的に判り易いです。
◆プログラム
こちら → ペルチェコントローター制御プログラム 20191221peltierTempCon (shift-JIS です)
【2023/03/23追記】184行目は下記に修正
誤:V = x * 1.1 / 10230.0; // 平均電圧の計算 (1.1V=1023)(10回ループで1.2ms)
正:V = x * 1.1 / 10240.0; // 平均電圧の計算 (1.1V=1024)(10回ループで1.2ms)
機能が多いので長めのプログラムになっています。全体のポイントは以下の通りです。
・タイミングはMsTimer2で5ms周期の割り込みで制御
・ペルチェのパワーはフルブリッジを使ったPWM制御 (Arduino の analogWrite )
・温度はPID制御
◆PID制御
Arduino のライブラリもあったのですが、使い方が判り難かったのと、制御出力が正負に対応していない感じがしたので、自前の関数を作りました。そうはいっても、計算は離散値PID制御で標準的に使われている式でやっています。なお、PIDのパラメーターはプログラム先頭付近の #define の Kp, Ki, Kd の値で定義しています。
一般的な計算式を使ったといっても少しカストマイズしているので、自分用のメモを兼ねて中の仕組みを解説しておきます。
PID制御の出力はfloat pid( ) 関数の戻り値となっています。この関数が制御のキモなのでコードを見ながら説明します。
float pid(float sv, float pv) { // 制御量を計算(pid制御)飽和対策
float x;
float e, dP, dI, dD;
static float e1 = 0.0;
static float e2 = 0.0;
static float lastMv = 0.0;
float dMv; // 操作修正量
if ((sv - lastSv) > 2.0) { // 1回の変化量の上限超えていたら
sv = lastSv + 2.0; // 微分成分の飽和防止のために変化量を制限する(2℃/0.2秒)
}
if ((sv - lastSv) < -2.0) {
sv = lastSv - 2.0;
}
lastSv = sv; // 次回監視用に値を保存
// 離散値に対するPID制御の式で制御量を決定
e = sv - pv; // 現在の誤差量
dP = Kp * (e - e1); // 比例部
dI = Ki * e; // 積分部
dD = Kd * ((e - e1) - (e1 - e2)); // 微分部
dMv = dP + dI + dD; // 補正量計算
Serial.print(F(", ")); Serial.print(TTC * 0.2, 1); // 時間
Serial.print(F(", ")); Serial.print(dP); // ΔKp
Serial.print(F(", ")); Serial.print(dI); // ΔKi
Serial.print(F(", ")); Serial.print(dD); // ΔKd
Serial.print(F(", "));
x = lastMv + dMv; // 制御量を決定
e2 = e1;
e1 = e;
if (x > 100.0) { // 調節範囲の上限越していたら
x = 100.0; // 100%でクランプ
}
if (x < -100.0) { // 下限以下なら
x = -100.0; // -100%でクランプ
}
lastMv = x; // 次回の計算用に保存(制約付きで保存)
return x; // 実際の制御量を返す
}
1) PIDの制御量計算
この関数では毎回制御量を計算し、30行の x = lastMv + dMv; で実際のペルチェの出力を決定しています。この式は過去の計算結果が累積されるので積分型になっています。こういう方式にしておくことで過去のデーターを大量に保存しなくて済み、プログラムが簡単になるのだと思います。
積分した結果でPIDの各要素の効果を発揮させるということは、それぞれの要素はあらかじめ1回微分した値を用意しないといけません。つまり、P(比例)は誤差の微分で、I(積分)は誤差そのままで、D(微分)は誤差を2回微分した値で準備します。dP, dI, dD の各要素はそういう意図で計算されているのですが、最初はこういう理屈が判らなくて悩みました。
2) アンチワインディング
例えば設定を70℃とした場合、実際に到達可能な温度の上限は約65℃なので誤差はゼロになりません。こういう状態が続くと積分項の効果はどんどん蓄積されるので制御量は非常に大きな値になってしまいます。そういう状態から例えば設定値を50℃に下げたとします。当然制御量を減らして追従しようとしますが、すでに非常に大きな値が入っているのでなかなか制御量が減りません。その結果反応時間が遅れてしまいます。これをワインドアップ現象と言います。
そういう問題を無くす方法はいろいろあるようですが、34から39行の処理で制御量の上限を制限しました。
3) 設定値変化速度の制限
以上のような方法でアンチワインドアップ対策を行うと計算結果の情報の一部が失われることになります。一番影響が出るのが微分項です。最初の変化は無視されてしまって、後半の変化量だけの情報が反映されるようなことが起きてしまいます。その結果、極端な場合は操作方向が逆になってしまうようなことまで起きてしまいます。そういう場合でも、時間が経てば誤差ゼロの状態に収束しますが、応答時間が長くなって好ましくありません。
対策はいろいろ考えられそうですが、手っ取り早い処置として、操作の変化速度の上限を制限することにしました。具体的には9~15行までの処理で、ここで1回、つまり0.2秒あたりの変化量を最大で2℃に制限しました。
◆まとめ
・PID制御のプログラムはもっと美しい実装がありそうですが、とりあえず動いているのでまあこれで良いかと思っています。PID制御を本格的に勉強しようとしている学生には非常に良い教材なので、もっと性能の良い実装を考えてもらうと良いと思います。
・ダイソーの3.4AのUSB充電器を何かうまいことに使えないだろうか?というかなり不純な動機で始めたペルチェ温度コントローラーですが、やってみるとなかなか面白かったです。これ作らなかったらPID制御なんてやらなかったので良い経験をさせてもらいました。
・表示に小型のOLEDを使いましたが、表現力が高く、グラフィックな表示も出来るのでなかなか使い易いです。ただ1倍角の文字は小さすぎて読みづらいので、縦だけ2倍角の文字、つまり幅6画素で高さ16画素の文字が表示できるともっと表現の幅が広がると思います。誰かそういうライブラリを作ってくれないですかね。ひょっとしたら Adafruit_SSD1306 で出来るのかも知れないのですが。