So I get pretty good AC amp readings with an ACS712 Hall sensor. I am deriving RMS voltage with DC voltage divider. I power the voltage sensor with an unregulated 5v DC power supply (basically a transformer, bridge rectifier and electrolytic cap). I use a pretty good RMS calc to get the amps from the Hall sensor. If I multiply RMS amps times RMS volts I get the VA, which of course equals watts for resistive loads, but as soon as the PF is not 1, I am pretty screwed for determining true power. So how can I derive PF from all of this so I can get to true watts? It seems that I can calculate the following from the Hall sensor:
-RMS amps (already being done and it works well)
-average amps (always equals zero)
-average absolute amps (comes out to 75% of RMS amps with resistive loads)
-max amps
-min amps
It seems I should be able to get the PF by using the average absolute amps, or the max amps but I cannot seem to figure out how. I tried looking through the emon library for several hours, but they seem to be using the peak intervals from the volt sensor. Of course this does not help me because my voltage sensor reads the voltage after converting to DC, which of course means i have no peaks to measure. If at all possible, before building myself a new voltage sensor that keeps the AC peaks intact, I'd really like to see if I can get the math to work with my current setup.
To calculate active power for a load that
- Is Reactive and/or
- Not linear and/or
- Is fed from a non sinusoidal source
You will have to take a number of samples each period and synchronize the sampling with the periods. then you
- Multiply voltage and current for each sample giving you instantaneous power
- Average instantaneous power over one period
Also observe that there are 2 definitions of power factor
- True power factor as calculated from the results above P/S or VA/W
- Displacement power factor dpf which equals cos phi for a reactive load driven with sinusoidal voltage. This does not work for not linear loads or non sinusoidal voltages
What are you intending to power with this set up?
Determining RMS volts is easy, but RMS amps is difficult if the load has any kind of active devices in the
power supply , like switch mode regulators, as the current waveform then is not sinusoidal.
You then need a true RMS converter to get the correct result.
Something like this.
Thanks you both for responding,
Mauried, the power supply should be sinusoidal as I'm getting it from a true sine wave inverter. I know I need to put it on a scope to know for sure, but the manufacturer is pretty reputable (SMA), so here's hoping. The loads will vary but since this is largely a prototype to demonstrate some software, I can choose what I put on it. I was hoping to at least be able to use a few light dimmers, small refrigerator or two and a space heater.
Nilton61, getting a synchronous reading of volts and amps, if I read you correctly, would seem to be nearly impossible with an Arduino since processing runs serially, meaning logically one sensor would have to be read a split microsecond ahead of the other, no? Or perhaps I am over thinking that part since the processor is a good deal faster than 60hz. Ok, lets assume that I can sample both voltage and amps withing a close enough spot, do I take a simple average, mean of absolute values or RMS of the samples? I'm guessing RMS, but I want to make sure I understand. Thanks again in advance.
RMS means root mean square: This is used for voltage and current measurements if you do not know the waveform (TRMS). It is calculated this way:
- Get a number of samples each period synchronized with the period.
- Square each sample(S)
- Sum the squares and divide the sum by the number of samples/period(M)
- Take the root of the sum(R)
When you are calculation active power you are measuring voltage and current simultaneously so the square in the above algorithm is replaced with the product of voltage and current. (since, for instantaneous power, p=u^2/R or p=i^2R or p=ui) and the last step, taking the root of the mean value is not needed and therefore skipped.
The number of samples you need will vary, but it should preferably be a power of 2. The more samples the more accurate the calculation would be for higher harmonics. Somewhere between 16 and 256 samples/period will work.
You are mentioning dimmers. These are non linear load so 32 samples/period is a recommended minimum. This gives you a sampling frequency of 1600Hz (@50Hz) or 1920Hz (@60Hz) which is well within the limits of what a arduino is capable of. Once you found a zero crossing you can process a whole period or even several periods.
The code should be laid out so that you sample voltage and current within a few instructions, perhaps with an timer interrupt. The calculations then should interleave the sampling.
Thank you very much. Your reply is really great. I of course need to build a new volt meter since mine at present converts to DC before reading. As soon as I get that built I report results and add the code that I used so the community can benefit.
If you are concerned about the delay between getting the voltage and current readings, you can use an external A/D chip which can capture both quantities simulataneously.
Success! Thanks everyone especially nilton61. This works surprisingly well.
I used this to build the volt sensor:
http://openenergymonitor.org/emon/node/58
I used and ACS712 Hall sensor for amperage readings.:
http://www.ebay.com/itm/1pcs-Hall-Sensor-30A-Range-ACS712T-ELC-30A-Module-Current-Sensor-Module-/251336440651?pt=LH_DefaultDomain_0&hash=item3a84d1bb4b
And here is the code:
// set variables for AC readings
const unsigned long sampleTime = 100000UL; // sample over 100ms, it is an exact number of cycles for both 50Hz and 60Hz mains
const unsigned long numSamples = 250UL; // choose the number of samples to divide sampleTime exactly, but low enough for the ADC to keep up
const unsigned long sampleInterval = sampleTime/numSamples; // the sampling interval, must be longer than then ADC conversion time
//-------------------------------------
//reads rms current from sensor
float truePower(){
float iAmps, iVolts, iPower;
float sumPower = 0;
unsigned int count = 0;
unsigned long prevMicros = micros() - sampleInterval ;
while (count < numSamples)
{
if (micros() - prevMicros >= sampleInterval)
{
iAmps = ((analogRead(A1) - 513) * (50.76053976653696 / 1024.0));
iVolts = ((analogRead(A0) - 513)* 0.58); //513 gets the sensor to zero and .58 multiplies the results to get volts
iPower = (iAmps * iVolts);
sumPower += iPower;
++count;
prevMicros += sampleInterval;
}
}
float avg = abs((sumPower/(float)numSamples));
return avg;
}
Updated code, I changed it to capture Volts, Watts, and VA all in one shot:
// set variables for AC readings
const unsigned long sampleTime = 100000UL; // sample over 100ms, it is an exact number of cycles for both 50Hz and 60Hz mains
const unsigned long numSamples = 250UL; // choose the number of samples to divide sampleTime exactly, but low enough for the ADC to keep up
const unsigned long sampleInterval = sampleTime/numSamples; // the sampling interval, must be longer than then ADC conversion time
const float ampFactor = (50.76053976653696 / 1024.0);
const float voltFactor = .61;
const int voltADC_Zero = 512;
const int curCalFactor = 513;
//-------------------------------------
//reads all electric sensor values into global variables
void getSensorValues(){
float iAmps, iVolts, iPower;
unsigned long sumAmps = 0;
unsigned long sumVolts = 0;
float sumPower = 0;
unsigned int count = 0;
unsigned long prevMicros = micros() - sampleInterval ;
while (count < numSamples)
{
if (micros() - prevMicros >= sampleInterval)
{
iAmps = (analogRead(curSensorPin) - curCalFactor); // read amp sensor
iVolts = (analogRead(A0) - voltADC_Zero); //read volt sensor
sumAmps += (unsigned long)(iAmps * iAmps); // add to volts total
sumVolts += (unsigned long)(iVolts * iVolts); // add to amps sample
iAmps = (iAmps * ampFactor); //get variable set for power calc
iVolts = (iVolts * voltFactor); //get variable set for power calc
iPower = (iAmps * iVolts); // calculate power
sumPower += iPower; // add to power sample
++count;
prevMicros += sampleInterval;
}
}
curWatts = abs((sumPower/(float)numSamples));
curVolts = sqrt(((float)sumVolts/(float)numSamples)) * voltFactor;
curAmps = sqrt(((float)sumAmps/(float)numSamples)) * ampFactor;
curAmps -= .01;
if (curAmps < .03) {curAmps = 0;}
curVA = curVolts * curAmps;
}