【第09回】 電力計算部を実装する
前回、電圧と電流の計測(A/D変換)で250us毎にサンプリングをする部分を実装しました。
これだけでは、電力は不明なので、サンプリングした結果から電力計算をしたいと思います
A/D変換によりサンプリングされた結果は以下の配列に格納されます。
//サンプリングテーブル
int myArrayA[SAMPLE_CNT]; //電流サンプリング値
int myArrayV[SAMPLE_CNT]; //電圧サンプリング値
この配列に格納されたデータを「void loop() 」関数内で計算します。
計算をするタイミングは、1CH~4CHの電圧と電流をサンプリングし終わったタイミングです。
前回のソースで言うと
case STATE_WAIT1: //しばらく待ち
case STATE_WAIT2: //しばらく待ち
case STATE_WAIT3: //しばらく待ち
case STATE_WAIT4: //しばらく待ち
の4か所。つまり、「しばらく待ち」状態になっている時に、計算をします。
コメントをわかりやすく書くと、、、
case STATE_WAIT1: //CH1用の電流と電圧のサンプリング完了後の待ち
case STATE_WAIT2: //CH2用の電流と電圧のサンプリング完了後の待ち
case STATE_WAIT3: //CH3用の電流と電圧のサンプリング完了後の待ち
case STATE_WAIT4: //CH4用の電流と電圧のサンプリング完了後の待ち
です。ちなみに、この待ち時間は、216.5ms(60Hz時)となります。
≪ソースコード≫
#include <TimerOne.h>
//=====================================================
//電圧67回 + 電流67回 + 待ち866回 = 1000回
//1000回 + タイマー周期250us = 250ms
//250ms * 4ch = 1秒で1巡する
//この設定は60Hz用なので50Hzの場合は、カウントを修正する必要あり
//#define SAMPLE_CNT (80) //50Hz用
//#define WAIT_CNT (840) //50Hz用
//=====================================================
#define SAMPLE_CNT (67) //サンプリング回数(1周期)
#define WAIT_CNT (866) //1ch分の電圧電流をサンプリングした後の待ちカウント
//ステート制御用変数
int state_smp;
int state_calc;
//サンプリング回数(秒)
int sec_cnt;
//サンプリングテーブル
int myArrayA[SAMPLE_CNT]; //電流サンプリング値
int myArrayV[SAMPLE_CNT]; //電圧サンプリング値
int arrCnt; //サンプリング位置
//電力量保持用変数(1秒間だけ保持する)
float watt1;
float watt2;
float watt3;
float watt4;
//タイマー割り込みのステート制御用定数
#define STATE_START 0
#define STATE_ANP1 1
#define STATE_VLT1 2
#define STATE_WAIT1 3
#define STATE_ANP2 4
#define STATE_VLT2 5
#define STATE_WAIT2 6
#define STATE_ANP3 7
#define STATE_VLT3 8
#define STATE_WAIT3 9
#define STATE_ANP4 10
#define STATE_VLT4 11
#define STATE_WAIT4 12
//メインループのステート制御用定数
#define STATE_CALC1 1
#define STATE_CALC2 2
#define STATE_CALC3 3
#define STATE_CALC4 4
//####################################################
//タイマー割り込み関数
//4ch分のサンプリングを定期的に行う
//####################################################
void callback(){
switch(state_smp){
case STATE_START:
arrCnt = 0;
state_smp = STATE_ANP1;
break;
//----------------------------------------------
// CH1
//----------------------------------------------
case STATE_ANP1: //電流のサンプリング
myArrayA[arrCnt] = analogRead(1);
arrCnt++;
if(arrCnt == SAMPLE_CNT){
arrCnt = 0;
state_smp = STATE_VLT1;
}
break;
case STATE_VLT1: //電圧のサンプリング
myArrayV[arrCnt] = analogRead(0);
arrCnt++;
if(arrCnt == SAMPLE_CNT){
arrCnt = 0;
state_smp = STATE_WAIT1;
state_calc = STATE_CALC1; //メインステートをCH1計算をするように変更
}
break;
case STATE_WAIT1: //しばらく待ち
arrCnt++;
if(arrCnt == WAIT_CNT){
arrCnt = 0;
state_smp = STATE_ANP2;
}
break;
//----------------------------------------------
// CH2
//----------------------------------------------
case STATE_ANP2: //電流のサンプリング
myArrayA[arrCnt] = analogRead(2);
arrCnt++;
if(arrCnt == SAMPLE_CNT){
arrCnt = 0;
state_smp = STATE_VLT2;
}
break;
case STATE_VLT2: //電圧のサンプリング
myArrayV[arrCnt] = analogRead(0);
arrCnt++;
if(arrCnt == SAMPLE_CNT){
arrCnt = 0;
state_smp = STATE_WAIT2;
state_calc = STATE_CALC2; //メインステートをCH2計算をするように変更
}
break;
case STATE_WAIT2: //しばらく待ち
arrCnt++;
if(arrCnt == WAIT_CNT){
arrCnt = 0;
state_smp = STATE_ANP3;
}
break;
//----------------------------------------------
// CH3
//----------------------------------------------
case STATE_ANP3: //電流のサンプリング
myArrayA[arrCnt] = analogRead(3);
arrCnt++;
if(arrCnt == SAMPLE_CNT){
arrCnt = 0;
state_smp = STATE_VLT3;
}
break;
case STATE_VLT3: //電圧のサンプリング
myArrayV[arrCnt] = analogRead(0);
arrCnt++;
if(arrCnt == SAMPLE_CNT){
arrCnt = 0;
state_smp = STATE_WAIT3;
state_calc = STATE_CALC3; //メインステートをCH3計算をするように変更
}
break;
case STATE_WAIT3: //しばらく待ち
arrCnt++;
if(arrCnt == WAIT_CNT){
arrCnt = 0;
state_smp = STATE_ANP4;
}
break;
//----------------------------------------------
// CH4
//----------------------------------------------
case STATE_ANP4: //電流のサンプリング
myArrayA[arrCnt] = analogRead(4);
arrCnt++;
if(arrCnt == SAMPLE_CNT){
arrCnt = 0;
state_smp = STATE_VLT4;
}
break;
case STATE_VLT4: //電圧のサンプリング
myArrayV[arrCnt] = analogRead(0);
arrCnt++;
if(arrCnt == SAMPLE_CNT){
arrCnt = 0;
state_smp = STATE_WAIT4;
state_calc = STATE_CALC4; //メインステートをCH4計算をするように変更
}
break;
case STATE_WAIT4: //しばらく待ち
arrCnt++;
if(arrCnt == WAIT_CNT){
arrCnt = 0;
state_smp = STATE_ANP1;
}
}
}
//####################################################
//起動時の処理
//####################################################
void setup() {
Serial.begin(9600) ; // パソコンとシリアル通信の準備を行う
//----------------------------------------------
//変数の初期化
//----------------------------------------------
state_smp = STATE_START;
arrCnt = 0;
state_calc = STATE_START;
sec_cnt = 0;
//----------------------------------------------
//タイマー割り込みの設定(250us周期)
//250usで67回サンプリングすると、ちょうど1周波(60Hz)
//----------------------------------------------
Timer1.initialize(250);
Timer1.pwm(9, 512);
Timer1.attachInterrupt(callback);
}
//####################################################
//空き時間処理
//####################################################
void loop() {
//----------------------------------------------
//割り込みでサンプリングした結果を集計するメインステート制御
//----------------------------------------------
switch(state_calc){
case STATE_START: //何もしないときは1ms待つ
delay(1);
break;
case STATE_CALC1: //======= CH1 =========
Serial.println("--1--");
watt1 = calc_watt(myArrayA, myArrayV);
state_calc = STATE_START;
Serial.println(watt1);
break;
case STATE_CALC2: //======= CH2 =========
Serial.println("--2--");
watt2 = calc_watt(myArrayA, myArrayV);
state_calc = STATE_START;
Serial.println(watt2);
break;
case STATE_CALC3: //======= CH3 =========
Serial.println("--3--");
watt3 = calc_watt(myArrayA, myArrayV);
state_calc = STATE_START;
Serial.println(watt3);
break;
case STATE_CALC4: //======= CH4 =========
Serial.println("--4--");
watt4 = calc_watt(myArrayA, myArrayV);
state_calc = STATE_START;
Serial.println(watt4);
break;
}
}
//####################################################
//電圧と電流のサンプリング値から消費電力(W)を計算
//####################################################
float calc_watt(int *a, int *v){
float tmp;
float sum;
//----------------------------------------------
//サンプリングした電圧波形の「-」側を「+」波形で補完
//----------------------------------------------
for(int c = 0; c < SAMPLE_CNT; c++){
if(v[c] == 0){
if(c < (SAMPLE_CNT/2)){
v[c] = -v[c + (SAMPLE_CNT/2)];
} else {
v[c] = -v[c - (SAMPLE_CNT/2)];
}
}
//Serial.println(v[c]);
}
//----------------------------------------------
//サンプリングした電流波形の「-」側を「+」波形で補完
//----------------------------------------------
//Serial.println("--");
for(int c = 0; c < SAMPLE_CNT; c++){
if(a[c] == 0){
if(c < (SAMPLE_CNT/2)){
a[c] = -a[c + (SAMPLE_CNT/2)];
} else {
a[c] = -a[c - (SAMPLE_CNT/2)];
}
}
//電流計測値が小さいため、A/D値補正のために+-1オフセットさせる
//(OPアンプ搭載するまでは暫定的にやる)
if(a[c] < 0) a[c]--;
if(a[c] > 0) a[c]++;
//Serial.println(a[c]);
}
//----------------------------------------------
//1周期のワット数の合計を計算
//----------------------------------------------
tmp = 0;
sum = 0;
for(int c = 0; c < SAMPLE_CNT; c++){
//電圧:V = (v[c] * 141.4 / 950)
//電流:A = (a[c] * 0.977) 50mv = 1A
tmp = ((a[c]) * 0.977) * (v[c] * 141.4 / 950);
//合計ワット数
sum+=tmp;
}
//----------------------------------------------
//電流と電圧の波形が反転しているとマイナスWになるため、+に反転する
//----------------------------------------------
if(sum < 0){
sum = sum * (-1);
}
//----------------------------------------------
//サンプル数で割って平均ワット数を返す
//----------------------------------------------
return((sum)/SAMPLE_CNT);
}
ソースコード内にある以下の赤い部分の計算式は、今回作成した電圧計(ACアダプタ改)と、CTセンサに合わせているので、各センサー類に合わせた計算式に変更する必要があります。
//----------------------------------------------
//1周期のワット数の合計を計算
//----------------------------------------------
tmp = 0;
sum = 0;
for(int c = 0; c < SAMPLE_CNT; c++){
//電圧:V = (v[c] * 141.4 / 950)
//電流:A = (a[c] * 0.977) 5mv = 1A
tmp = ((a[c]) * 0.977) * (v[c] * 141.4 / 950);
//合計ワット数
sum+=tmp;
}
割り込みと、メインループのステートの関係は、以下の通りとなります。
電圧と電流のサンプリングが完了したとき、メインステートを計算開始用のステートに変更し、メインループ「loop()」で電力の計算が実行されるようにしています。
メインループ側で、電力の計算が終わると再び「STATE_MAIN」に戻り、待ち状態となります。
センサーをつなげて実行すると、シリアルモニタに各チャンネルの電力が出力されます。
シリアルモニタは、以下のメニューを選択すれば表示されます。