えっと,ざっくりと書いてみました。
難しいというか,肝心なところは,ニクロム線に流す電力で使ってるPWM制御のデューティー比をどう変化させるかっていう部分。車は急に止まれない…じゃなくてニクロム線ヒーターは急に温度が下がったり,上がったりはしません。単純に「目標温度<現在温度ならデューティー比下げる,またはその逆」みたいな処理だと,どうしても目標温度との開きが大きい状態で推移してしまいます。で,センサーを読み取ったときに前回の読み取り結果との差から,現在の状態が上昇状態にあるのか下降状態にあるのかを記録しておいて,
現在の温度がターゲット温度より高い場合
→下降指示
→下降中で温度差が大きい場合に大きく引き下げる
→下降中で温度差が小さい場合には少し持ち上げる
→上昇中に下降指示が出た場合は大きく引き下げる
現在の温度がターゲット温度より低い場合
→上昇指示
→上昇中で温度差が大きい場合には大きく引き上げる
→上昇中で温度差が小さい場合には少し引き下げる
→下降中に上昇指示が出た場合は大きく引き上げる
という感じに,目標温度付近で足踏みさせるようなやり方を考えてみました。
プログラムのほうは,↑の発想をそのまま条件分岐させただけっていうテキトー具合なんですが,まぁそこはそれで。もうちょっと美しいアルゴリズムがありそうなんだけど,ちゃっちゃっと作ってしまいたかったので,とりあえず書き切ることを優先しました。
結果はまずまずで,この状態で家の中で試して見ると,目標温度±1℃以内の範囲で動いてくれてます。デューティー比を変化させるかどうかのルーチンを実行するインターバルや,デューティー比の変化量はざっくり設定してますが,悪くない設定っぽいです。もちろん,ベストかどうかは分からないですけど。
あとは,屋外でレンズに巻いた状態だと違う結果になる可能性もあるんで,次はフィールドでテストして,必要なら調整しようと思ってます。一応,スイッチで目標温度を変えられるようにしてあるので,目標温度より大きく離れた位置で動きが止まるようなら,調整が終わるまでは目標温度を無理矢理引き上げ/引き下げてカバーしようとか思ってたりします。
あと,ログ記録のための準備はしたんですが,ルーチンは書いてません。気が付いた方はえらい!(笑。あ,さらに,↓のコードで表示が乱れるのはウチのブログがデザインをちゃんとクロスプラットフォーム対応にしてないのが原因なので,気にしないでください。
え? ハードウェアのほうはどうなったかって? 今度書きます。
#include "mbed.h"
#include "TextLCD.h"
#include "LinearTempSensor.h"
Serial pc(USBTX, USBRX);
TextLCD lcd(p24, p26, p27, p28, p29, p30, TextLCD::LCD16x2); // LCD(RS, E, DB4, DB5, DB6, DB7)
LinearTempSensor sensor[] = {p20,p19,p18}; // sensor([0][1]=heater, [2]=ambient)
PwmOut heat_pwm[] = {p22,p21};
InterruptIn sw[] = {p5, p6};
DigitalIn s_sw(p7);
Ticker sensor_timer, heater_timer, log_timer;
float target_temp = 35.0;
float Tav[3];
int current_direction[3];
// interrupt event - change target temperature
void switch0() {
if(s_sw.read() == 1) {
if(target_temp < 45) {
target_temp += 0.5;
}
lcd.locate (10,1);
lcd.printf ("%2.1f", target_temp);
}
}
void switch1() {
if(s_sw.read() == 1) {
if(target_temp < 10) {
target_temp -= 0.5;
}
lcd.locate (10,1);
lcd.printf ("%2.1f", target_temp);
}
}
// read temperature
void read_temp() {
float Vout[3], previous_Tav[3];
for(int i = 0; i < 3; i++) {
previous_Tav[i] = Tav[i];
Vout[i] = sensor[i].Sense(); // read sensor
Tav[i] = sensor[i].GetAverageTemp(); // calculate average temperature from 10 samples
if((previous_Tav[i] - Tav[i]) > 0) {
current_direction[i] = 0;
} else {
current_direction[i] = 1;
}
}
// print temperature to LCD
lcd.locate(2,0);
lcd.printf("%2.1f", Tav[0]);
lcd.locate(10,0);
lcd.printf("%2.1f", Tav[1]);
lcd.locate(2,1);
lcd.printf("%2.1f", Tav[2]);
}
// change pwm duty cycle
void change_pwm_cycle(int heater_num, int recieve_direction) {
float modified_cycle = heat_pwm[heater_num].read();
float temp_difference = Tav[heater_num] - target_temp;
if(temp_difference < 0) {
temp_difference *= -1;
}
switch(recieve_direction) {
case 0:
if(current_direction[heater_num] == 0) {
if(temp_difference > 5) {
modified_cycle -= 0.2;
} else if(temp_difference < 2) {
modified_cycle += 0.05;
}
} else {
modified_cycle -= 0.2;
}
break;
case 1:
if(current_direction[heater_num] == 1) {
if(temp_difference > 5) {
modified_cycle += 0.2;
} else if(temp_difference < 2) {
modified_cycle -= 0.05;
}
} else {
modified_cycle += 0.2;
}
break;
default:
break;
}
if(modified_cycle < 0) {
modified_cycle = 0;
} else if(modified_cycle > 1) {
modified_cycle = 1;
}
heat_pwm[heater_num].write(modified_cycle);
}
// control heater
void control_heater() {
for(int i = 0; i < 2; i++) {
if(Tav[i] > target_temp) {
change_pwm_cycle(i ,0);
} else if(Tav[i] < target_temp) {
change_pwm_cycle(i, 1);
}
}
pc.printf("0:%1.3f 1:%1.3f \n",heat_pwm[0].read(), heat_pwm[1].read());
}
// main
int main() {
// init LCD
lcd.cls(); // clear LCD
lcd.writeCommand(0x40); // create Celsius symbol and write to CGRAM
wait(0.000040f);
lcd.writeData((int)0x08);
lcd.writeData((int)0x14);
lcd.writeData((int)0x08);
lcd.writeData((int)0x06);
lcd.writeData((int)0x09);
lcd.writeData((int)0x08);
lcd.writeData((int)0x09);
lcd.writeData((int)0x06);
wait(0.000040f);
lcd.locate(0,0); // print fixed char to LCD
lcd.printf("1:");
lcd.locate(8,0);
lcd.printf("2:");
lcd.locate(0,1);
lcd.printf("a:");
lcd.locate(8,1);
lcd.printf("t:");
lcd.locate(6,0);
lcd.putc(0x00);
lcd.locate(14,0);
lcd.putc(0x00);
lcd.locate(6,1);
lcd.putc(0x00);
lcd.locate(14,1);
lcd.putc(0x00);
lcd.locate(10,1);
lcd.printf("%2.1f", target_temp);
// set interrupt event
sw[0].rise(&switch0);
sw[1].rise(&switch1);
// init PWM //
heat_pwm[0].period_ms(10); // set pwm period 10ms=100Hz
heat_pwm[1].period_ms(10);
heat_pwm[0].write(1); //set default pwm duty cycle
heat_pwm[1].write(1);
// read first temperature
read_temp();
// set interval event
sensor_timer.attach(&read_temp, 1);
heater_timer.attach(&control_heater, 5);
}
あー,貼り付けててピンときた。摂氏マイナスになったときのこと考えてないや,これ。LCDの桁数が足りない(笑。頑張って作ったけど,℃←を消すかな……。


コメントする