Вычисление среднего арифметического с данных которые приходят раз в 10 сек.

AndryGladky
Offline
Зарегистрирован: 09.10.2014

Здравствуйте, просю помощи не могу разобратся сам в двух вопросах как не пробывал.

Первый не могу найти среднее арифметическое число с массива данных которые измеряются раз в 10 секунд, а среднее арифметическое что бы было за 5 минут.

И второе почему то не могу сбросить максимум по прохождении 5 минут, пробовал использовать разницу millis и присваивать максимуму 0, но почему то не работает.

Скетч прилагаю. 

//Код для nRF хаба ATT.
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#define chclient 1 // номер клиента 1...
#define timesend 300 // интервал отправки данных,для обычных датчиков можно установить время выше.

unsigned long time1=0;
unsigned long time2=0;

// переменные датчика скорости ветра
volatile int impuls = 0;
volatile unsigned long Ntime;
float resalt1;
float resalt2;
float k=1; // 4 импульcа за 1 секунду = 1 м/с
float MAX; //переменная хранения максимума
float MAX1; //переменная временного хранения максимума
unsigned long time11=0;
unsigned long time22=0;

const int n = 12; // колличество данных в массиве
int readings[n];      // данные, считанные с входного аналогового контакта
int i = 0;                  // индекс для значения, которое считывается в данный момент
int total = 0;                  // суммарное значение
int sred = 0;                // среднее значение

//функция щетчика количества импульсов
void imp()
{
  impuls++;
}


// Set up nRF24L01 radio on SPI bus plus pins 9 & 10
RF24 radio(9,10);

// 0 -прием , 1 -передача
const uint64_t pipes[2] = {0xF0F0F0F0E1LL,0xF0F0F0F0D2LL};

// структура отправляемых данных.Изменяемые данные.Размер структуры должен быть не больше 32 байт !
typedef struct{
byte identifier;// номер передатчика.МЕНЯТЬ НЕЛЬЗЯ
int Analog; //вывод напряжения
int temperature_Sensor; //вывод скорости ветра
int temperature_Sensor2; //вывод максимума ветра за 5 минут
unsigned long count;// счетчик передач для контроля качества канала
}
nf1;
nf1 clientnf;

void setup() {

// выставляем все считываемые значения на ноль:
for (int thisReading = 0; thisReading < n; thisReading++)
  readings[thisReading] = 0;

//прерывания для подщета импульсов
  impuls = 0; //присваеваем переменной количества импульсов 0
  Ntime = millis(); //начальное время равняется переменной миллис
  time11 = millis();
  attachInterrupt(0, imp, RISING); //вызов функции по прерыванию
// (при приходе импульса с датчика вызывает функцию имп)
    time11 = millis(); //начальное время подсчета максимума
radio.begin();
Serial.begin(9600);
// выбор скорости
//radio.setDataRate(RF24_250KBPS);
radio.setDataRate(RF24_1MBPS);
// radio.setDataRate(RF24_2MBPS);

radio.setPALevel(RF24_PA_MAX);
radio.setChannel(100); //тут установка канала
radio.setCRCLength(RF24_CRC_16);

radio.setAutoAck(false); // выключить аппаратное потверждение

radio.setRetries(15,15);

radio.openWritingPipe(pipes[1]); // Открываем канал передачи
//radio.openReadingPipe(1,pipes[0]); // Открываем канал приема

clientnf.identifier = chclient;
}

byte errorstate;

//функция подсчета импульсов
float calcimp() {
    int KOLimp=0;
    float res=0;
    unsigned long Ktime = millis();
    if (Ktime-Ntime>10000) {
    cli();
    int KOL1imp = impuls;
    impuls=0;
    sei();
    Serial.println (KOL1imp);
    res = KOL1imp*1000.0/(Ktime-Ntime)*k;
    Ntime=Ktime;
   }
return res;   
}

//измерение напряжения
long readVcc()
{
// Read 1.1V reference against AVcc
// set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = _BV(MUX3) | _BV(MUX2);
#else
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
delay(75); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Start conversion
while (bit_is_set(ADCSRA,ADSC)); // measuring
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
uint8_t high = ADCH; // unlocks both
long result = (high<<8) | low;
// result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
// result = 1074150L / result; // Calculate Vcc (in mV); 1125300 = 1.05*1023*1000
result = 1094610L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000 //Подбирал сам по тестеру
return result; // Vcc in millivolts
}


void loop() {
float s=calcimp();
resalt1 = s;
if (resalt1>0){
resalt2 = resalt1;
}

if (resalt2>MAX) {
  MAX = resalt2;
  }
  
    total= total - readings[i];// берем последнее значение...
    if (time22-time11>10000) {
    readings[i] = resalt2;// ...которое было считано от сенсора:
    Serial.println(sred);  // выводим его на компьютер цифрами в кодировке ASCII
    time11 = time22;
    } 
    total= total + readings[i]; // добавляем его к общей сумме:
    i = i + 1; // продвигаемся к следующему значению в массиве:                      
    if (i>=n) {  // если мы в конце массива...           
    i = 0; // ...возвращаемся к началу:                         
    sred = total / n; // вычисляем среднее значение:  
    }    
//для чтения сенсоров
if ((millis()-time1) >= 1000) { // обновляем сенсоры раз в секунду (1000млс)
// тут будут опросы сенсоров
clientnf.Analog=readVcc();
Serial.println (readVcc());
clientnf.temperature_Sensor = resalt2*10+1;
Serial.println (resalt2);
clientnf.temperature_Sensor2 = MAX*10+1;
Serial.println (MAX);
time1 = millis();
}

if ((millis() - time2) >= timesend) {
radio.stopListening();
bool ok = radio.write( &clientnf, sizeof(clientnf) );
radio.startListening();
time2 = millis();
}
} // конец loop

 

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

А зачем массив? Храните накопительную сумму за 5ть минут и количество накопленного, этого хватит чтобы среднее посчитать. Если надо раз в 5ть минут выводить данные, то копите 5ть минут сумму и количество (количетсво поидее 30 должно быть), как 5ть минут прошло поделили вывели срднее, сбросили две переменные и снова начали тоже самое.

dimin21590
dimin21590 аватар
Offline
Зарегистрирован: 26.10.2016
long NAME = 0;

//some code


NAME += /*reading*/;

 

5N62V
Offline
Зарегистрирован: 25.02.2016

Penni пишет:

А зачем массив? Храните накопительную сумму за 5ть минут и количество накопленного, этого хватит чтобы среднее посчитать. Если надо раз в 5ть минут выводить данные, то копите 5ть минут сумму и количество (количетсво поидее 30 должно быть), как 5ть минут прошло поделили вывели срднее, сбросили две переменные и снова начали тоже самое.

  В массиве можно скользящее усреднение сделать. Мин максы отфильтровать. Зависит от задачи, конечно.

AndryGladky
Offline
Зарегистрирован: 09.10.2014

Какая стоит задача, сделал датчик ветра, сделал на оптопаре, подсчитываю импульсы перевожу в метры за секунду, хочу эти данные передавать на народный мониторинг, через esp8266, но снятие данных сайтом происходит раз в пять минут, потому и хочу усреднять данные ветра за пять минут, и максимум, это порывы ветра тоже за пять минут и потом максимум обнулять, но что то максимум не обнуляется.

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

Так у вас в коде нет сброса MAX. Я так понимаю надо после отправки данных обнулить MAX. После 171 строки написать MAX=0

AndryGladky
Offline
Зарегистрирован: 09.10.2014

В конце добавил строки сброса максимума, но не работает. Почему я честно сам не пойму, по идее должно работать??!!!

if (time44-time33>30000) //сбрасываем максимум через 30 секунд
    {
    MAX=0;
    time33 = time44;
    }

 

FAI4
Offline
Зарегистрирован: 23.09.2016

Проще всего сделать скользящее среднее

AndryGladky
Offline
Зарегистрирован: 09.10.2014

Тогда можно пример кода.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Максимумы тоже бывают скользящие. Вычисляете максимумы и минимумы за 5 минут . Вот и будет у вас естественая корректировка.

AndryGladky
Offline
Зарегистрирован: 09.10.2014

qwone пишет:

Максимумы тоже бывают скользящие. Вычисляете максимумы и минимумы за 5 минут . Вот и будет у вас естественая корректировка.

Ну вычислить, а потом что с ними сделать?

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

 вот у вас идут значения (1)-12 // (2)-15 //(3)-11 //(4)-3 //(5)-12 // (6)-9 //(7)-14 //(8)-3   . Вот сейчас (5) и максимум  15 , через минуту в (6) максимум 15 в (7) мак 14  ну так далее Посто надо выборки за 5 минут сохранять и вычислять максимум каждый раз. Поэтому сброса не надо делать.

AndryGladky
Offline
Зарегистрирован: 09.10.2014

qwone пишет:

 вот у вас идут значения (1)-12 // (2)-15 //(3)-11 //(4)-3 //(5)-12 // (6)-9 //(7)-14 //(8)-3   . Вот сейчас (5) и максимум  15 , через минуту в (6) максимум 15 в (7) мак 14  ну так далее Посто надо выборки за 5 минут сохранять и вычислять максимум каждый раз. Поэтому сброса не надо делать.

А выборку как в масивы делать?

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

ну это насколько памяти хватит. раз в минуту . Или 1 найбольший из серии в течении минуты. Я же не знаю зачем вам надо. ПС: я бы не через массивы а класс очередь написал. первый зашел послений вылетел. среднее из очереди.

FAI4
Offline
Зарегистрирован: 23.09.2016

AndryGladky пишет:
Тогда можно пример кода.
Можно. Алгоритм такой.

Не надо никакого массива. Скользящие среднее делается следующим образом:

Пусть у вас   F(t-1) - значение параметра за предыдущее время (то, что выводится на индикатор как "среднее"),

а f(t) - последнее считанное значение

Тогда F(t) = [(1-k) * F(t-1)] + [k * f(t)]

где k - параметр сглаживания (усреднения):

 при k = 1 - вообще нет никакого сглаживания (сразу отображается последнее значение)

при k = 0 - нет изменения среднего

Т.е. k должен строго больше нуля, но не более 1

 

Если k = 0.5, то у вас примерно усрднение из 2-х последних

Если k=0,2 - усреднение за 5 последних отсчетов

Если k=0,1 - усреднение за 10 последних отсчетов (!!! условно)

 

Вообще правильное название "Экспоненциальное скользящее среднее"

Достоинство такого метода, что вам нужна только ОДНА ячейка памяти для хранения "среднего"

(не нужны никакие массивы)

FAI4
Offline
Зарегистрирован: 23.09.2016

Если данные приходят каждые 10 сек, а интегрировать нужно за 5 минут (т.е. 5 * 60 / 10 = 30 отсчетов)

Тогда k должен быть равен примерно 1/30, т.е.

k = 0.03

Но показания будут "уточняться" каждые 10 сек НЕПРЕРЫВНО (нет наобности их "сбрасывать")

 

Есть еще более продвинутые алгоритмы, усредняющие в зависимости от "поведения" измеряемой величины (дисперсии и наличия тенденций в изменении параметра).

Также Алгоритм может сам определит когда показания установились достаточно точно (отправить данные потребителю), а когда еще стоит подождать ...

 

Короче говоря здесь непочатое поле чтобы "порезвиться" для настоящих математиков....

 

 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

FAI4 пишет:
Короче говоря здесь непочатое поле чтобы "порезвиться" для настоящих математиков....

Скоро "настоящие математики" будут резвиться с таблицой умножения. Деградация математиков полная.

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

У человека просто скорость ветра измеряется, а ему тут сейчас курс вышки зачитают ) По мне так проще как я выше писал, без всяких массивов, сумма количество за 5ть минут, порывы ветра сбрасывать после передачи раз в 5ть минут и все.

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

FAI4 пишет:

Тогда F(t) = [(1-k) * F(t-1)] + [k * f(t)]
где k - параметр сглаживания (усреднения):
при k = 1 - вообще нет никакого сглаживания (сразу отображается последнее значение)
при k = 0 - нет изменения среднего
Т.е. k должен строго больше нуля, но не более 1

 

Если k = 0.5, то у вас примерно усрднение из 2-х последних

Если k=0,2 - усреднение за 5 последних отсчетов

Если k=0,1 - усреднение за 10 последних отсчетов (!!! условно)

Вообще правильное название "Экспоненциальное скользящее среднее"

Достоинство такого метода, что вам нужна только ОДНА ячейка памяти для хранения "среднего"

(не нужны никакие массивы)

Есть готовое: http://playground.arduino.cc/Main/RunningAverage

А я "недавно" пытался это-же "на пальцах" объяснить:

http://arduino.ru/forum/programmirovanie/kak-vytsepit-pervoe-podkhodyash...

Или тут уже с цифрами, так понятнее: http://arduino.ru/forum/proekty/dosvetka-v-teplitse#comment-49692

averageWaterTemp= (averageWaterTemp*9 + waterTemp)/10

Причём числа 9 и 10 могут быть  1 и 2 или 745 и 746

FAI4
Offline
Зарегистрирован: 23.09.2016

Penni пишет:
У человека просто скорость ветра измеряется, а ему тут сейчас курс вышки зачитают ) По мне так проще как я выше писал, без всяких массивов, сумма количество за 5ть минут, порывы ветра сбрасывать после передачи раз в 5ть минут и все.
- так ПЛОХО.

Недостаток: Нужно ждать 5 минут чтобы увидеть новое значение

Pablos
Pablos аватар
Offline
Зарегистрирован: 18.07.2017

В строке 151 i сбрасывается на 0 только когда достигнет 13, а последнее значение индекса в массиве - 11.
При i=12 к среднему прибавляется какое-то еще число из памяти.
Еще ошибка -  i в ноль сбрасывается, а сумма - нет?!

Вообще, программа слишком запутана.
Я много баловался со скользящим средним, написал красиво... А потом все заменил на 1 строчку

 

//    Апериодическое звено первого порядка  
//    Y0:=(K*X0+(Tf/SAMPLE_T*Y1))/((Tf/SAMPLE_T)+1);
//    Y1:=Y0;

 filteredWeight = (lastWeight+3*filteredWeight)/4;     // Фильтр Tf/Sample_T = 3
// lastWeight - последнее измерение
// filteredWeight - значение фильтра

Результат тот же, а гемора меньше на порядок и float можно заменить на long без потери качества и с увеличением скорости. 

Pablos
Pablos аватар
Offline
Зарегистрирован: 18.07.2017

P.S.  щетчика и подщет   - это от слова щетка?  Кстати, result тоже пишется не так )))))     

(только без обид)

AndryGladky
Offline
Зарегистрирован: 09.10.2014

Подсчет среднего сделал по совету Penni,

if ((millis()-time11)>=10000) {
    SUM = SUM+resalt2;
    n = n + 1;
    time11 = millis();
    }
    if ((millis()-time22)>=300000) {
    sred = SUM/n;
    n = 0;
    SUM = 0;
    time22 = millis();
    }

Так работает и принцыпе вычисляет среднее, как мне нужно, с сносными данными.

AndryGladky
Offline
Зарегистрирован: 09.10.2014

Но почему то, сброс максимума не работает, подскажите почему. Скетч полный прилагаю.

//Код для nRF хаба ATT.
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#define chclient 1 // номер клиента 1...
#define timesend 300 // интервал отправки данных,для обычных датчиков можно установить время выше.

unsigned long time1=0;
unsigned long time2=0;

// переменные датчика скорости ветра
volatile int impuls = 0;
volatile unsigned long Ntime;
float resalt1;
float resalt2;
float k=1; // 4 импульcа за 1 секунду = 1 м/с
float MAX; //переменная хранения максимума
float MAX1; //переменная временного хранения максимума
unsigned long time11=0;
unsigned long time22=0;
unsigned long time33=0;
unsigned long time44=0;
float sred = 0;
float SUM = 0;
int n = 0;

//функция щетчика количества импульсов
void imp()
{
  impuls++;
}


// Set up nRF24L01 radio on SPI bus plus pins 9 & 10
RF24 radio(9,10);

// 0 -прием , 1 -передача
const uint64_t pipes[2] = {0xF0F0F0F0E1LL,0xF0F0F0F0D2LL};

// структура отправляемых данных.Изменяемые данные.Размер структуры должен быть не больше 32 байт !
typedef struct{
byte identifier;// номер передатчика.МЕНЯТЬ НЕЛЬЗЯ
int Analog; //вывод напряжения
int temperature_Sensor; //вывод скорости ветра
int temperature_Sensor2; //вывод максимума ветра за 5 минут
unsigned long count;// счетчик передач для контроля качества канала
}
nf1;
nf1 clientnf;

void setup() {

//прерывания для подщета импульсов
  impuls = 0; //присваеваем переменной количества импульсов 0
  Ntime = millis(); //начальное время равняется переменной миллис
  time33 = millis();
  attachInterrupt(0, imp, RISING); //вызов функции по прерыванию
// (при приходе импульса с датчика вызывает функцию имп)
radio.begin();
Serial.begin(9600);
// выбор скорости
//radio.setDataRate(RF24_250KBPS);
radio.setDataRate(RF24_1MBPS);
// radio.setDataRate(RF24_2MBPS);

radio.setPALevel(RF24_PA_MAX);
radio.setChannel(100); //тут установка канала
radio.setCRCLength(RF24_CRC_16);

radio.setAutoAck(false); // выключить аппаратное потверждение

radio.setRetries(15,15);

radio.openWritingPipe(pipes[1]); // Открываем канал передачи
//radio.openReadingPipe(1,pipes[0]); // Открываем канал приема

clientnf.identifier = chclient;
}

byte errorstate;

//функция подсчета импульсов
float calcimp() {
    int KOLimp=0;
    float res=0;
    unsigned long Ktime = millis();
    if (Ktime-Ntime>10000) {
    cli();
    int KOL1imp = impuls;
    impuls=0;
    sei();
    Serial.println (KOL1imp);
    res = KOL1imp*1000.0/(Ktime-Ntime)*k;
    Ntime=Ktime;
   }
return res;   
}

//измерение напряжения
long readVcc()
{
// Read 1.1V reference against AVcc
// set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = _BV(MUX3) | _BV(MUX2);
#else
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
delay(75); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Start conversion
while (bit_is_set(ADCSRA,ADSC)); // measuring
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
uint8_t high = ADCH; // unlocks both
long result = (high<<8) | low;
// result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
// result = 1074150L / result; // Calculate Vcc (in mV); 1125300 = 1.05*1023*1000
result = 1094610L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000 //Подбирал сам по тестеру
return result; // Vcc in millivolts
}


void loop() {
float s=calcimp();
resalt1 = s;
if (resalt1>0){
resalt2 = resalt1;
}

if (resalt2>MAX1) {
  MAX1 = resalt2;
  }
  if ((time44-time33)>30000) //сбрасываем максимум через 30 секунд
    {
    MAX1=0;
    time33 = time44;
    }
  MAX = MAX1;
    
    if ((millis()-time11)>=10000) {
    SUM = SUM+resalt2;
    n = n + 1;
    time11 = millis();
    }
    if ((millis()-time22)>=300000) {
    sred = SUM/n;
    n = 0;
    SUM = 0;
    time22 = millis();
    }
    
//для чтения сенсоров
if ((millis()-time1) >= 1000) { // обновляем сенсоры раз в секунду (1000млс)
// тут будут опросы сенсоров
clientnf.Analog=readVcc();
Serial.println (readVcc());
clientnf.temperature_Sensor = resalt2*10+1;
Serial.println (resalt2);
clientnf.temperature_Sensor2 = MAX*10+1;
Serial.println (MAX);
Serial.println (sred);
time1 = millis();
}

if ((millis() - time2) >= timesend) {
radio.stopListening();
bool ok = radio.write( &clientnf, sizeof(clientnf) );
radio.startListening();
time2 = millis();
}
} // конец loop

 

Pablos
Pablos аватар
Offline
Зарегистрирован: 18.07.2017

А что тут не ясно?
Строка 139. Time44 никогда нигде ничего не присваивается.
Посему уже на втором кругу разница равна 0 и никогда не станет >300000

Кроме того такое большое количество millis() - моветон!
В самом начале loop нужно один вызов поставить. uint32_t currTime=millis();
А далее с ним все сравнивать.

AndryGladky
Offline
Зарегистрирован: 09.10.2014

Спасибо понял свою ошибку. Не досмотрел.

FAI4
Offline
Зарегистрирован: 23.09.2016

Такой способ - ОТСТОЙ!

Данные появляются ТОЛЬКО 1 раз за 5 минут.

 

Хотя они могут быть доступны постоянно

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

Лучшее враг хорошего. Отстой это использовать универсальные методы там где они не нужны. С кучей избыточности и матанализа. Надо выбирать реализацию под конкретную задачу. Если бы это использовалось в ветряке я бы еще понял, а когда это используется в домашней метеостанции то узнать скорость ветра раз в 5ть минут это нормально, к тому же у вас  есть всегда данные за прошедшие 5ть минут.

Logik
Offline
Зарегистрирован: 05.08.2014

trembo пишет:

averageWaterTemp= (averageWaterTemp*9 + waterTemp)/10

Причём числа 9 и 10 могут быть  1 и 2 или 745 и 746

Оно конечно так, числа любые можна, но (2^n)-1 и (2^n) предпочтительней, особенно при фиксированой точке. Для одного просчета в 10сек это не критично, но лучше привыкать к правильному. Вобще для фиксированой точки лучше эту формулу несколько преобразовать для исключения потерь точности, сделать так 

averageWaterTemp_x_10= (averageWaterTemp_x_10 - (averageWaterTemp_x_10/10)+ waterTemp)

 а результат получать как averageWaterTemp=averageWaterTemp_x_10 / 10. Т.е. хранить значение умноженім на 10. Ну и заменить 10 либо на 8 либо на 16. Тогда вместо деления - сдвиги, умножения нет, скорость максимальная.

FAI4 пишет:

Такой способ - ОТСТОЙ!

Данные появляются ТОЛЬКО 1 раз за 5 минут.

 

Хотя они могут быть доступны постоянно

 

Мало того, они еще и от раза к разу будут сильно скакать если ветер с порывами. Прийдется усреднять на большем промежутке времени, а значить изменения ситуации, например поднялся ветер т.к. начинается гроза, будут определены с большим запаздыванием. Так что верно, именно ОТСТОЙ. Хотя для помойки народного мониторинга сойдет ;) AndryGladky, а подумайте, нужен ли на той помойке еще и ваш вклад? Напомню, измерения, в т.ч. метеорологические, должны выполнятся по установленной методике на повереном оборудовании  иначе им грош цена. Скорость сквозняков и нагрев термометра на солнце хрен кому нужны.

Penni пишет:

С кучей избыточности и матанализа. 

Ну вы осознаете, что таким заявлением вы чистосердечно признаетесь что матана вы вглаза не видели? Хотя не важно, ваше признание принято к сведенью))

 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Сомневаюсь что вам подойдет это. Но так посмотрите , кто хочет.

/**/
unsigned long mill; // переменная под millis()
//--------------------------------
struct mean_t {
  int dat;
  struct mean_t (*next) = NULL;
};
class Cl_mean {
  protected:
    struct mean_t (*start);
    unsigned num = 0;
  public:
    /*конструктор*/
    Cl_mean() {
    }
    /*добавить еще одно значение*/
    void push(int dat) {
      ++num;
      struct mean_t (*_new) = new struct mean_t; ;
      (*_new).dat = dat;
      (*_new).next = start;
      start = _new;

    }
    /*выкинуть самое старое значение*/
    void pop() {
      if (num == 0) return ;
      struct mean_t (**ii) = &start;
      while ((**ii).next != NULL) {
        ii = &(**ii).next;
      }
      delete (*ii);
      (*ii) = NULL;
      --num;
    }
    /*показать среднее*/
    int viev() {
      if (num == 0) return 0;
      long sum = 0;
      for (struct mean_t (*ii) = start; ii != NULL; ii = (*ii).next) {
        sum += (*ii).dat;
      }
      return sum / num;
    }
    /*показать наибольшее*/
    int vievMax() {
      if (num == 0) return 0;
      long _max = 0;
      for (struct mean_t (*ii) = start; ii != NULL; ii = (*ii).next) {
        if (_max < (*ii).dat) _max = (*ii).dat;
      }
      return _max;
    }
    /*показать меньшее*/
    int vievMin() {
      if (num == 0) return 0;
      long _min = 100000;
      for (struct mean_t (*ii) = start; ii != NULL; ii = (*ii).next) {
        if (_min > (*ii).dat)  _min = (*ii).dat;
      }
      return _min;
    }
};
Cl_mean Mean;
//---Компоновка-----------------------------
const byte analogPin =/*пин*/A0;
unsigned long past1;
//---main-----------------------------
void setup() {
  Serial.begin(9600);
  for (byte i = 0; i < 5; ++i)Mean.push(100);
}

void loop() {
  mill = millis();
  if (mill - past1 >= 500) {
    past1 = mill;
    int dat = analogRead(analogPin);
    Mean.push(dat);
    Mean.pop();
    Serial.print("val:");
    Serial.print(dat);
    Serial.print("  midl:");
    Serial.print(Mean.viev());
    Serial.print("   min:");
    Serial.print(Mean.vievMin());
    Serial.print("   max:");
    Serial.print(Mean.vievMax());
    Serial.println();
  }
}
/**/

 

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

Logik пишет:

Хотя не важно, ваше признание принято к сведенью))

Можете принимать, это ваше право.

Была в 2013 году такая вот история у газпрома: на одной из пограничных станций со стороны Украины, установили новую АСУ по учету коммерческого газа и сразу пошли расхождения в суточных отчетах, Украинская сторона насчитывала то больше то меньше. Создали совместную комиссию, начали копать, а в итоге выяснилось, что в новой АСУ происходило усреднение за 5ть минут и вот это усредненное значение складывалось в базу. Потом эти усредненные суммировались за сутки и вот получалось то больше то меньше. В итоге выяснилось, что они усредняли примерно как там выше предлагалось и теряли на первый взгляд копейки, а в сутки выходило по 40-60 тысяч кубометров газа. После исправления две системы показывали идентичные данные (в пределах неопределенности). А мораль проста: не везде нужны крутые алгоритмы, зачастую бывает достаточно обычной математики.

А по поводу порывов, они и в вашем алгоритме будут влиять, а куда без них? Можно конечно сглаживать и отбрасывать все это, но тогда какой смысл в таких измерениях. Была у ТС задача сделать раз в пять минут, вот и сделал. А по поводу метрологии вообще не понятно к чему это было. Понятно что люди делают для себя это все и ни в каких коммерческих расчетах их измерения не учавствуют, ну хочет человек удаленно смотреть температуру в доме, котла или труб или чего угодно вот и смотрит и "помойки" народного монитора для этого достаточно.

Logik
Offline
Зарегистрирован: 05.08.2014

Penni пишет:

А по поводу метрологии вообще не понятно к чему это было. 

Не сомниваюсь. Где матан, там и метрология ;) Но вопрос был не Вам, ваша точка  зрения и так очевидна. Замечу только что "для себя" и в публичный доступ - разные вещи.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

 Соглашусь. Для себя могут быть варианты разные , а для публики приходится фильтровать ...код.

Pyotr
Offline
Зарегистрирован: 12.03.2014

"Интересней" задачка по усреднению направления ветра. Особенно когда флюгер мотает из стороны в сторону или даже делает обороты вокруг своей оси (завихрения за деревьями, домами..). 
Если мгновенные значения лежат в диапазоне 0-15 (шаг 22.5*) и флюгер мотает на границе 14-15-0-1-2...
Есть ли простой подход для расчетов в таком случае?

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Разумеется есть. Прежде всего вынуть голову из трусов , даже если вы думаете жопой. Есть такое понятие роза ветров. Вот там и посчитывается куда и сколько дует ветер в определеное время.  А не так -если 5 минут дует на север и 5 минут дует на юг , то все 10 минут стояла безветреная погода. Но прикол даже не в этом.  Ветер не дует с области высокого давления в область низкого , а блин вращается по кругу , большому кругу - циклоны или антициклоны. Вот и их движение надо ловить.  А не локальное дуновение ветерков.

Pyotr
Offline
Зарегистрирован: 12.03.2014

qwone, трусы зачем ? Настроение чтоль "праздничное"... Про циклоны конечно интересно), но мне нужно именно локальное направление ветра на моем участке.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Pyotr. Вот разберем пример. Пусть возле города есть река(море,озеро). Набираем в ванну воду и меряем. Надо соленая вода, добавляем соль.  Сложно мерять температуру. Ну да то откроем холодную воду, то горячую . Сложно усреднять.  А что местность одна и та же, вода она и в Африке вода. Так что температура в ванной и температура в море одинакова.  Скажете что я на тяжелой наркоте сижу. Так это я перевел  в явную область тоже самое что вы несли про ветер. Знать направление ветра на локальном участке бессмыслено. Это как судить о температуре море по температуре в ванной.  Вам что нужно, что бы растения заваливались в нужном направлении. Вычислять погоду по локальному ветру бессмысленно.

Pyotr
Offline
Зарегистрирован: 12.03.2014

qwone пишет:

 ... Вам что нужно, что бы растения заваливались в нужном направлении. Вычислять погоду по локальному ветру бессмысленно.

Почти... Решать с какой стороны фрамуги открывать и насколько. А прогноз на трое суток  есть где глянуть. 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

ПС: Если ветер постоянный и длится день или два, то центр антициклона(циклона) далеко. То значит погода стабильна. А вот если у вас постоянно меняется ветер, сила . То это значит в вошли в зону циклона , то погода изменится. И не важно какого направление ветер и его сила. И разумеется не важно "усреднять" его направление и величину. Потому что порывистость и есть тот показатель.

Pyotr пишет:

Почти... Решать с какой стороны фрамуги открывать и насколько. А прогноз на трое суток  есть где глянуть. 

Фрамуги лучше открывать при стабильном ветре или безветрии.

Logik
Offline
Зарегистрирован: 05.08.2014

Pyotr пишет:

"Интересней" задачка по усреднению направления ветра. 

Таких интересных много. Но сразу надо убедится что считать среднее имеет хоть какой смысл. Ну помните про средную температуру по больнице - вот. Среднее не всегда имеет смысл. Еще пример - средная скорость авто на трасе- ОК, средний их вес - тоже, а средний их цвет - это как? Среднее время в пути - ОК, а среднее время выезда - бред. Вобщем не всегда среднее определяемо.

FAI4
Offline
Зарегистрирован: 23.09.2016

Автор должен уточнить для каких целей далее будут использоваться полученные данные.

Тогда можно предложить наиболее подходящий алгоритм

Pyotr
Offline
Зарегистрирован: 12.03.2014

Logik пишет:

Таких интересных много. Но сразу надо убедится что считать среднее имеет хоть какой смысл...

Средним я неправильно назвал. Скорее это преобладающее направление ветра за какое-то время в мин.

На данный момент алгоритм таков:
На высоте 1 м от конька теплицы находится флюгер и анемометр. При каждом обороте анемометра читаю показания флюгера, значение 0-16 (при тихой погоде направление не учитывается).
 В массиве byte windDirection[16] инкриминирую значение в  соответствующей ячейке. После 255 измерений ищу макс.значение в массиве. Дальше опускаю "горб" в массиве вычитанием константы из каждого элемента (конечно при этом слежу за переполнением). При изменении направления ветра  "горб" смещается по массиву и всегда можно найти его вершину.

Фрамуги могут двигаться с интервалом кратно 1 мин. Обычно задаю 3 мин. За это время кардинально микроклимат не может изменится.
При тихой погоде обе стороны фрамуг работают синхронно.
Ветер вдоль конька - тоже синхронно, но с учетом скорости ветра (ветер ограничивает макс.положение).
При других направлениях ветра каждая сторона работает по своему алгоритму. При этом учитываются заданные настройки в меню для наветренной и подветренной сторон, направление, скорость ветра, Т наружная, влажность в теплице и т.д.

Вот меня и интересуют другие способы найти преобладающее направление ветра, такое "скользящее" без массива. Может есть варианты проще. В любом случае мне для общего развития полезно знать. 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

для общего развития так.Но у меня сомнения в пользе.

/**/
unsigned long mill; // переменная под millis()
//--------------------------------
struct mean_t {
  int value;/*скорость потока*/
  int dir;/*направление*/
  struct mean_t (*next) = NULL;
};
class Cl_mean {
  protected:
    struct mean_t (*start);
    unsigned num = 0;
  public:
    /*конструктор*/
    Cl_mean() {
    }
    /*добавить еще одно значение*/
    void push(int value, int dir) {
      struct mean_t (*_new) = new struct mean_t; ;
      (*_new).value = value;
      (*_new).dir = dir;
      (*_new).next = start;
      start = _new;

    }
    /*выкинуть самое старое значение*/
    void pop() {
      if (num == 0) return ;
      struct mean_t (**ii) = &start;
      while ((**ii).next != NULL) {
        ii = &(**ii).next;
      }
      delete (*ii);
      (*ii) = NULL;
      --num;
    }
    /*показать наибольшее*/
    void vievMax(int &value, int &dir) {
      if (num == 0) return;
      value = 0;
      for (struct mean_t (*ii) = start; ii != NULL; ii = (*ii).next) {
        if (value < (*ii).value) {
          value = (*ii).value;
          dir = (*ii).dir;
        }
      }
      return ;
    }
};
Cl_mean Mean;
//---Компоновка-----------------------------
const byte analog1Pin =/*пин скорости*/A0;
const byte analog2Pin =/*пин поворота*/A1;
unsigned long past1;
//---main-----------------------------
void setup() {
  Serial.begin(9600);
  for (byte i = 0; i < 5; ++i)Mean.push(0, 0);
}

void loop() {
  mill = millis();
  if (mill - past1 >= 500) {
    past1 = mill;
    int value = analogRead(analog1Pin);
    int dir = analogRead(analog2Pin);
    Mean.push(value, dir);
    Mean.pop();
    Serial.print("val:");
    Serial.print(value);
    Serial.print("dir:");
    Serial.print(dir);
    Mean.vievMax(value, dir);
    Serial.print("   max val:");
    Serial.print(value);
    Serial.print("   max dir:");
    Serial.print(dir);
    Serial.println();
  }
}
/*Скетч использует 2816 байт (9%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 232 байт (11%) динамической памяти, оставляя 1816 байт для локальных переменных. Максимум: 2048 байт.
*/

 

Pyotr
Offline
Зарегистрирован: 12.03.2014

qwone, Ваш пример конечно проще... особенно для начинающих дуинщиков))
Объясните в чем принципиальная разница в том как у меня - заменять старые значения в массиве на новые по мере их поступления, и дальше делать расчет, и как Вы предложили. Может памяти меньше требуется, или быстрее работает?

Logik
Offline
Зарегистрирован: 05.08.2014

Pyotr пишет:

 В массиве byte windDirection[16] инкриминирую значение в  соответствующей ячейке. После 255 измерений ищу макс.значение в массиве. 

От отэта штука называется гистограммой. Штука хорошая, но с своими фокусами и методами обработки. Например она может быть и такой.

Картинки по запросу гистограмма

Ну и где тут максимум? Правый узкий но высокий или левый жирный но пониже. 

Искать проще гистограммы не стоит, она итак проста. Почитайте про них, может чего полезного вычитайте.

По задаче. Я бы не делал. Или делал предельно просто не расчитывая на хороший результат. Дело в том что определить необходимость открыть на 1-3минуты наперед по данным из прошлого или текущего - это задача прогнозирования. Как прогноз погоды или спортивного результата. Хороших решений неизвестно. 

Простое решение: ветер  не дул последние допустим 5минут в ненужную сторону - можна открывать. Или чтото в таком духе. Или оставте теже гистограммы, но поиск максимума усовершенствовать и учесть что гистограмма у вас кольцевая- её правый край состыковывается с левым.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Pyotr, я не понял ваше последнее сообщение.  Почему я перешел на С++ . Разумеется это потребовало немного времени на изучение. Но в дальнейшем упростило жизнь. Мне не надо каждый раз переписывать код под новые задачи. Я банально переписываю прошлый код и все. Почему все же не массив. Так в Си нет изменение размера массива.  Разумеется можно создать класс Массив с изменяемой размерностью. Но я просто предложил один из вариантов. По поводу "новичков". Новички это такие новички, что даже им все разжуй, с рот положи, все равно "миньет потребуют", хотя бы потому что бы не зазнавался. Почему я выкладываю свои скетчи. Так это тренировка своих "программистких мускул и мозгов". Если не решать такие задачи и таким способом, так мозги усядутся и буду писать примитивный код.

ПС struct mean_t (**ii) это указатель на указатель структуры. Как не странно пригодилась такая конструкция.

Pyotr
Offline
Зарегистрирован: 12.03.2014

Logik, вот про несколько максимумов на гистограмме я упустил. Спасибо, почитаю, подумаю.

Qwone, Ваш подход пока не готов применить... Я конечно глянул в учебнике что есть "векторы","списки", и даже примерно понял Ваш код).

FAI4
Offline
Зарегистрирован: 23.09.2016

Pyotr пишет:
Средним я неправильно назвал. Скорее это преобладающее направление ветра за какое-то время в мин.

На данный момент алгоритм таков:
На высоте 1 м от конька теплицы находится флюгер и анемометр. При каждом обороте анемометра читаю показания флюгера, значение 0-16 (при тихой погоде направление не учитывается).
 В массиве byte windDirection[16] инкриминирую значение в  соответствующей ячейке. После 255 измерений ищу макс.значение в массиве. Дальше опускаю "горб" в массиве вычитанием константы из каждого элемента (конечно при этом слежу за переполнением). При изменении направления ветра  "горб" смещается по массиву и всегда можно найти его вершину.

Фрамуги могут двигаться с интервалом кратно 1 мин. Обычно задаю 3 мин. За это время кардинально микроклимат не может изменится.
При тихой погоде обе стороны фрамуг работают синхронно.
Ветер вдоль конька - тоже синхронно, но с учетом скорости ветра (ветер ограничивает макс.положение).
При других направлениях ветра каждая сторона работает по своему алгоритму. При этом учитываются заданные настройки в меню для наветренной и подветренной сторон, направление, скорость ветра, Т наружная, влажность в теплице и т.д.

Вот меня и интересуют другие способы найти преобладающее направление ветра, такое "скользящее" без массива. Может есть варианты проще. В любом случае мне для общего развития полезно знать.

- здесь нужно переходить  к физическому смыслу.

А физический смысл - это ВРЕМЯ за которое дует ветер с той или иной стороны.

 

В данном случае аналог - интегрирование (силы ветра * направление) * dt

 

Тогда будем иметь ВОЗДЕЙСТВИЕ ветра с каждого направления,

что далее уже обрабатывается форточками

FAI4
Offline
Зарегистрирован: 23.09.2016

Что в итоге нужно найти автору:

Самое ветренное направление

или самое безветренное направление

Это совершенно разные задачки...

Pyotr
Offline
Зарегистрирован: 12.03.2014

FAI4 пишет:

Что в итоге нужно найти автору:

Самое ветренное направление

или самое безветренное направление

Для моего случая думаю достаточно определить откуда дует ветер с погрешностью скажем +-10 градусов.
А если складывать в массив типа uint16_t, то получится ещё большее "усреднение". А поддерживая пики гистограммы на каком то уровне (больше-меньше данных), можно управлять "периодом усреднения".

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Мда , "чем дальше в лес , тем толще партизаны". Вот зачем человеку нужен ветер. У него что ветреная мальница или ветрогенератор. Или у него "ветряная вентиляция теплицы". Так для всего этого нужен объем перемещаемого воздуха. И главное не направление , а направление преобладающего перемещения воздуха. Так что в массив или куда-еще пихать не только сколько времени дует воздух, но и с какой силой. 

ПС: Там еще много еще, но не я не знаю зачем ТС ветер,и поэтому дальше советовать не хочу.