do i use RunningMedian or RunningAverage?

Hallo

I have made a simple control system for a wind turbine with a hall sensor and a wind speed sensor (0-5V).

but the numbers jump up and down so I would like to have the average of the last 50-100 measurements.

but it Running Median, RunningAverage or something else I need to measure the average

#include <LiquidCrystal_I2C.h>

int onRpm = 1500;     //Omdr kontaktor tænder ved.

#define I2C_ADDR 0x3F // <<- Add your address here.
#define Rs_pin 0
#define Rw_pin 1
#define En_pin 2
#define BACKLIGHT_PIN 3
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7

LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);



 volatile byte half_revolutions;
 unsigned int rpm;
 unsigned long timeold;

int windpin = 0;
int relaypin = 9;
int diodePin = 13;

 
 void setup()
 {
   Serial.begin(115200);
   attachInterrupt(0, magnet_detect, RISING);//Initialize the intterrupt pin (Arduino digital pin 2)
   half_revolutions = 0;
   rpm = 0;
   
   timeold = 0;
   pinMode(relaypin, OUTPUT);
   
   digitalWrite(relaypin, LOW);
  

   lcd.begin (20,4); // <<-- our LCD is a 20x4, change for your LCD if needed

 


  // LCD Backlight ON
lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
lcd.setBacklight(HIGH);
lcd.clear(); // start with a blank screen

  lcd.setCursor(0,0);
   lcd.print("   Cs Wind Power");
   lcd.setCursor(0,1);
   lcd.print("Omdr = ");
   lcd.setCursor(0,2);
   lcd.print("Vind = ");
   lcd.setCursor(0,3);
  lcd.print("Relay = ");
 
 }
 void loop()//Measure RPM
 {
   if (half_revolutions >= 20) { 
     rpm = 30*1000/(millis() - timeold)*half_revolutions;
     timeold = millis();
     half_revolutions = 0;
     Serial.println(rpm);
   }

   if (rpm > 1500){
    digitalWrite(relaypin, HIGH);

     
   }
   else{
    digitalWrite(relaypin, LOW);
   }

  
 int val = analogRead(windpin); 

  float outputVolt = val * (5.0 / 1023);

  float windSpeed = 6.5 * outputVolt;
   

  

  lcd.setCursor(8,1);
  lcd.print(rpm);

  lcd.setCursor(8,2);
  lcd.print(windSpeed);
  lcd.print("       ");
  

  

  

  lcd.setCursor(8,4); 
  if (digitalRead(relaypin) == 1){
     lcd.print("On ");
  }

if (digitalRead(relaypin) == 0){
     lcd.print("OFF");
  }

  

   
 }
 void magnet_detect()//This function is called whenever a magnet/interrupt is detected by the arduino
 {
   half_revolutions++;
   //Serial.println("detect");
 }

/ Bruno

I understand moving median is not so susceptible to the wild spikes, and you are probably seeing a fair bit of those.

Median would be less susceptible to spikes.
It is literally "the middle value"...the magnitude of the values to each side do not bias the median...

The mean average on the other hand, the magnitude of each value will effect the mean.

Without seeing your schematic, I suspect the source of the problem is bounce during the rising signal state change. I also suspect that just adding software debounce would vastly improve, if not eliminate the problem. Care to post a link to your sensor and your interface circuit?

This is the two sensors:

Hall sensor

Wind sensor

This should provide a strong debounced signal:

Now I've tried it, wind sensor seems to work better now, but the hall sensor show 1000 revs too much and is still very unstable.

Is there no easy way to code average of 50-100 times?

You could feed it in to Excel and let that do all the dirty work.

Yes, the averaging could be done in code (there's lots of examples here in the Forum and in the Playground). I should have asked this earlier ... what is the hall sensor used for? (the wind sensor already has various output types (0-5V, 4-20mA, serial 232/485)

What is the relay pin used for?
Could you post a diagram on how you have everything connected?

I think it would be good to make sure the signals and connections are correct prior to averaging the readings.

the wind sensor i have is only 0-5v signal.

the hall sensor is measuring RPM of the generator and connect it to the grid via a relay when it comes up over 1500 rpm

and switches it off when the revs are below 1500.

I am pretty sure that everything is connected properly in arduinoen, I measure the correct rpm if I keep the generator on without there is wind blowing on the wings.

the problem is that the wind run up and down rapidly, causing the relay to trip unnecessarily.

so I must find a way to find the average of the revolution.

There are many ways. One way is to slow down the update interval by counting more pulses before calculating the rpm. Could get the equivalent of 10 existing updates averaged just by changing

if (half_revolutions >= 20) {

to

if (half_revolutions >= 200) {

but the rpm update rate will be 10x slower.

If you need the rpm to update to be faster (like it is now) then you would need to add more code that averages or "smooths" multiple rpm values.

brunokc:
I would like to have the average of the last 50-100 measurements.

To measure the average current on a PWM pulsed LED, I used a Modified Moving Average to calculate the average. It has the benefit of faster calculation and less memory, when compared to a standard moving average. You don't need to keep the last 100 measurements. The concept is used quite a bit in the financial world, works great for calculating the average of a PWM like waveform.

Here's my write-up (focused on current, but I explained MMA as best as I understand it): https://www.baldengineer.com/measure-pwm-current.html

Now I've tried it, wind sensor seems to work better now, but the hall sensor show 1000 revs too much and is still very unstable.

Forgot to mention that your code should use micros() instead of millis() because the low timing resolution creates larger and larger jumps in your calculated rpm readings as the input frequency increases. This could be the main reason for the unstable readings you're getting.

Your MMA idea is similar to one I've used for years....

    ave2 += altitude;
    ave2 *= (1-1/aveLen);
    ave3 = ave2/(aveLen-1);

avelen ( int) is the number of samples over which to average, all else floats.

As you may guess this was an altimeter, but it's generally applicable..

Allan

dlloyd:
Forgot to mention that your code should use micros() instead of millis() because the low timing resolution creates larger and larger jumps in your calculated rpm readings as the input frequency increases. This could be the main reason for the unstable readings you're getting.

When i change millis() to micros() the rpm show 0.

I've tried to "smooths" RPM, but when the RPM is at 1450-1550 rpm so shows the 205 average. but it is much more stable.
Can you explain to me why it does not show 1,500 rpm?
Here is the code:

#include <LiquidCrystal_I2C.h>
 
int onRpm = 1500;     //Omdr kontaktor tænder ved.
 
 
const int numReadings = 50;
 
int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average
 
#define I2C_ADDR 0x3F // <<- Add your address here.
#define Rs_pin 0
#define Rw_pin 1
#define En_pin 2
#define BACKLIGHT_PIN 3
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7
 
LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);
 
 
 
 volatile byte half_revolutions;
 unsigned int rpm;
 unsigned long timeold;
 
int windpin = 0;
int relaypin = 9;
int diodePin = 13;
 
 
 void setup()
 {
   Serial.begin(115200);
   attachInterrupt(0, magnet_detect, RISING);//Initialize the intterrupt pin (Arduino digital pin 2)
   half_revolutions = 0;
   rpm = 0;
   
   timeold = 0;
   pinMode(relaypin, OUTPUT);
   
   digitalWrite(relaypin, LOW);
 
 
   lcd.begin (20,4); // <<-- our LCD is a 20x4, change for your LCD if needed
 
 
 
 
  // LCD Backlight ON
lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
lcd.setBacklight(HIGH);
lcd.clear(); // start with a blank screen
 
  lcd.setCursor(0,0);
   lcd.print("   Cs Wind Power");
   lcd.setCursor(0,1);
   lcd.print("Omdr = ");
   lcd.setCursor(0,2);
   lcd.print("Vind = ");
   lcd.setCursor(0,3);
  lcd.print("Relay = ");
 
   for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
 
  }
 }
 void loop()//Measure RPM
 {
   if (half_revolutions >= 10) {
     rpm = 30*1000/(millis() - timeold)*half_revolutions;
     timeold = millis();
     half_revolutions = 0;
     Serial.println(rpm);
   }
 
 total = total - readings[readIndex];
 
  readings[readIndex] = rpm;
 
  total = total + readings[readIndex];
 
  readIndex = readIndex + 1;
 
 
  if (readIndex >= numReadings) {
 
    readIndex = 0;
  }
 
 
  average = total / numReadings;
   
 
   if (average > 190){
    digitalWrite(relaypin, HIGH);
 
     
   }
   else{
    digitalWrite(relaypin, LOW);
   }
 
 
 int val = analogRead(windpin);
 
  float outputVolt = val * (5.0 / 1023);
 
  int windSpeed = 6.5 * outputVolt;
   
 int windspeed1 = map(val, 204, 1023, 0, 32);
 
 
  lcd.setCursor(8,1);
  lcd.print(rpm);
 
  lcd.setCursor(14,1);
  lcd.print(average);
   lcd.print(" ");
 
  lcd.setCursor(8,2);
  lcd.print(windspeed1);
  lcd.print(" ");
 
   lcd.setCursor(12,2);
  lcd.print(outputVolt);
  lcd.print("v  ");
 
 
 
 
 
 
  lcd.setCursor(8,4);
  if (digitalRead(relaypin) == 1){
     lcd.print("On ");
  }
 
if (digitalRead(relaypin) == 0){
     lcd.print("OFF");
  }
  delay(200);
 
Serial.println(average);
   
 }
 void magnet_detect()//This function is called whenever a magnet/interrupt is detected by the arduino
 {
   half_revolutions++;
   
 }

I think the signal needs debouncing ... you're probably averaging multiple false readings near 0 and that's throwing off the average. I don't think you need averaging until it's determined how stable the your true readings really are.

You can try this test code ... software debounce is included (stableTime = 1000µs). No averaging is implimented. You can self test with a 100Hz timer by connecting a jumper wire from pin 9 or 10 to pin 2. The reading should be 3000 (half revolutions). Then remove the jumper and connect your singal to pin 2 (INT0) and see what you get for RPM readings.

const word stableTime = 1000; //µs
const word printInterval = 250; //ms
const byte sensorPin = 2;
bool calculate = false;
volatile unsigned long currentTime, previousTime, elapsedTime, count;
unsigned long currentMillis, previousPrint, rpm;

void setup()
{
  timer();
  Serial.begin(115200);
  attachInterrupt(digitalPinToInterrupt(sensorPin), rpm_func, RISING);
}

void loop()
{
  currentMillis = millis();
  if ((currentMillis - previousPrint) >= printInterval ) {
    previousPrint = currentMillis;
    noInterrupts();
    unsigned long elapsedTimeCopy = elapsedTime;
    unsigned long countCopy = count;
    interrupts();
    if (calculate) {
      rpm = (30 * 1000000) / elapsedTimeCopy; // half revolutions
      calculate = false;
    }
    Serial.println(rpm);
    //Serial.println(elapsedTimeCopy);
    //Serial.println(countCopy);
  }
}

void rpm_func() {
  currentTime = micros();
  elapsedTime = currentTime - previousTime;
  previousTime = currentTime;
  if (elapsedTime >= stableTime) {
    count++;
    calculate = true;
  }
}

void timer() {
  pinMode(9, OUTPUT);    // 100Hz
  pinMode(10, OUTPUT);   // 100Hz

  TCCR1A = 0;            //clear timer registers
  TCCR1B = 0;
  TCNT1 = 0;

  TCCR1B |= _BV(CS10) | _BV(CS11) ; // prescaler = 64, (250,000 Hz)
  ICR1 = 1250;           //PWM mode counts up 1250 then down 1250 counts (100Hz)

  OCR1A = 625;           //0-1250 = 0-100% duty cycle
  TCCR1A |= _BV(COM1A1); //output A clear rising/set falling

  OCR1B = 625;           //0-1250 = 0-100% duty cycle
  TCCR1A |= _BV(COM1B1); //output B clear rising/set falling

  TCCR1B |= _BV(WGM13);  //PWM mode with ICR1 Mode 10
  TCCR1A |= _BV(WGM11);  //WGM13:WGM10 set 1010
}