ステップ応答に基ずく調整方法で、ジーグラ・ニコルス (Ziegler Nichols) の方法を試してみます。 比例・積分 (PI) 動作の場合 PB = 110 Kp L / T Ti = 3.3 L Kp = Y / X PWMオン時間 = 5%より、X = 5 グラフ1の最低温度22.3℃、最高温度30.0℃より、 Y = 100 × (30.0 - 22.3) / SPAN = 100 × 7.7 / 100 = 7.7 Kp = Y / X = 7.7 / 5 = 1.54 グラフ2の最初の温度の上がらない期間より、L = 90 グラフ1の斜面の期間 (91〜3600) より、T = 3509 PB = 110 × 1.54 × 90 / 3509 ≒ 5 Ti = 3.3 × 90 ≒ 300 となります。この数値の実験結果をグラフ3にのっけておきます。 使用したソフトを下に記載しておきます。 |
スッテプ応答図
グラフ1
グラフ2
グラフ3
How to Compile (コンパイルのしかた) プログラム /* * temp_ctrl5.c * * Created: 2017/03/19 5:46:46 * Author : User */ /* * ステップ応答 * PWMオン時間 = 5% に対するポットの温度を RS-232C(115200 8-N-1)で出力する * * ATmega328P * * * Fuses(システムクロック:20MHz) * * * ディホルト Int. RC Osc. 水晶 外部クロック * DISABLED 2.7V 2.7V 2.7V * 8/8MHz 8MHz * EXTENDED 0xFF 0xFD 0xFD 0xFD * HIGH 0xD9 0xD9 0xD9 0xD9 * LOW 0x62 0xE2 0xF7 0xE0 */ #include <stdio.h> /* sprintf() */ #include <string.h> /* strlen() */ #include <avr/io.h> #include <avr/interrupt.h> ///#define F_CPU 20.0E6 /* 20 MHz */ ///#include <util/delay.h> #define USEC 1024 #define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) typedef struct t_systim { short utime; /* 上位16bit */ unsigned long ltime; /* 下位32bit */ } SYSTIM; ///void delay_us(unsigned short t){while(t--) _delay_us(1);} ///void delay_ms(unsigned short t){while(t--) _delay_ms(1);} void initADC(void); void initTIMER(void); void get_tim(SYSTIM *p_tim); void initUSART0(void); void Send0(char *str); short ad_data; SYSTIM SYSCK; int main(void) { SYSTIM tim; static unsigned long ot[2]; float ondo; /* 温度 (0.0 〜 110.0℃) */ short pwm_on_time; /* PWMオン時間 (0 〜 100%) */ char buff[32], buff2[8]; int len; /* ATmega328P */ DDRB = 0b00000101; /* XTAL2 XTAL1 SCK MISO MOSI OC1B ----- LED */ DDRC = 0b00000000; /* xxxxx RESET ----- ----- ----- ----- ----- ----- */ DDRD = 0b00000000; /* ----- ----- ----- ----- ----- ----- TXD RXD */ PORTB = 0b00111010; PORTC = 0b00111110; PORTD = 0b11111100; initADC(); initTIMER(); initUSART0(); /* * WGM13 WGM12 WGM11 WGM10 動作種別 TOP値 * 1 1 1 1 高速PWM動作 OCR1A * * COM1A1 COM1A0 COM1B0 COM1B0 * 0 0 1 0 比較一致でLow、BOTTOMでHighをOC1Bピンへ出力(非反転動作) */ TCCR1A = 0b00100011; /* 一致時(OCR1B)Low, TOP->BOTTOM(OCR1A)でHighを */ /* OC1Bピンへ出力(非反転動作), 高速PWM動作 */ /// TCCR1B = 0b00011001; /* (1 / 20MHz) * 65535 = 3.3mS */ /// TCCR1B = 0b00011010; /* (8 / 20MHz) * 65535 = 26mS */ TCCR1B = 0b00011011; /* (64 / 20MHz) * 65535 = 210mS */ /// TCCR1B = 0b00011100; /* (256 / 20MHz) * 65535 = 839mS */ /// TCCR1B = 0b00011101; /* (1024 / 20MHz) * 65535 = 3355mS */ OCR1A = 0xffff; /* TOP->BOTTOMでHigh, 固定 */ OCR1B = 0; /* 一致時Low、可変(0はPWM出力on時間0%) */ sei(); Send0("START\r"); get_tim(&tim); ot[0] = tim.ltime; ot[1] = tim.ltime; /* pwm_on_timeの設定 */ pwm_on_time = 5; OCR1B = round(65535.0 * pwm_on_time / 100.0); while(1){ get_tim(&tim); if((unsigned long)(tim.ltime - ot[0]) >= 500000/USEC){ /* 500mS */ ot[0] = tim.ltime; PORTB ^= 0b00000001; } get_tim(&tim); if((unsigned long)(tim.ltime - ot[1]) >= 1000000/USEC){ /* 1S */ ot[1] = tim.ltime; /* 温度を得る 例 12.3℃ */ ondo = 1100.0 * ad_data / 10230; /* 0.0〜110.0℃ */ /* 表示例 pwm_on_time = 20%, 14.2 */ sprintf(buff2, "%ld", round(ondo * 10)); len = strlen(buff2); buff2[len + 1] = buff2[len]; buff2[len] = buff2[len - 1]; buff2[len - 1] = '.'; sprintf(buff, "pwm_on_time = %d%%, %s\r", pwm_on_time, buff2); Send0(buff); } } } void initADC(void) { ADCSRA = 0b10101111; /* AD変換イネーブル, AD自動変換イネーブル,AD変換割り込みイネーブル */ /* 1/128クロック(最大変換時間=200uS @16MHz, 156uS @20MHz) */ ADCSRB = 0b00000011; /* タイマ/カウンタ0比較一致A */ ADMUX = 0b11000000; /* 基準電圧 = 1.1V(@atmega328p), 右詰, アナログ入力 = ADC0 */ } ISR(ADC_vect) { static long result; static short counter; result += ADC; counter++; if(counter >= 100){ counter = 0; ad_data = (short)(result / 100); result = 0; } } void initTIMER(void) { TCCR0A = 0b00000010; /* CTC動作 */ TCCR0B = 0b00000100; /* clk / 256 */ OCR0A = 80 - 1; /* (256 / 20MHz) * 80 = 1024uS */ TIMSK0 = 0b00000010; /* 比較A割り込み要求有効 */ } ISR(TIMER0_COMPA_vect) { if(++SYSCK.ltime == 0) ++SYSCK.utime; } void get_tim(SYSTIM *p_tim) { cli(); *p_tim = SYSCK; sei(); } void initUSART0(void) { UBRR0 = 21; /* U2X0 = 1, 115200ボー/20MHzクロック */ UCSR0A = 0b00000010; /* U2X0 = 1 */ UCSR0B = 0b00001000; /* 送信イネーブル */ UCSR0C = 0b00000110; /* 8ビット, パリティなし, ストップ・ビット1 */ } /* * 1データ送信 */ void Tx0(char data) { while((UCSR0A & _BV(5)) == 0) ; UDR0 = (unsigned char)data; } /* * 文字列を送信 */ void Send0(char *str) { while(*str != '\0'){ Tx0(*str); str++; } } |