Hareketli ortalamaları hesaplamak için verimli algoritma / veri yapısı


9

Şu anda bir ısı pompası sisteminde sıcaklıkları, akışları, gerilimleri, gücü ve enerjiyi görüntülemek için bir grafik LCD sistemi geliştiriyorum. Grafik LCD kullanımı, SRAM'ımın yarısının ve flaşımın ~% 75'inin bir ekran arabelleği ve dizeleri tarafından kullanıldığı anlamına gelir.

Şu anda enerji için min / maks / ortalama rakamları gösteriyorum Günlük rakam sıfırlandığında gece yarısı sistem, günlük tüketimin bir önceki minimum veya maksimumun altında mı altında mı olduğunu kontrol eder ve değeri saklar. Ortalama, kümülatif enerji tüketiminin gün sayısına bölünmesiyle hesaplanır.

Geçen hafta ve ay boyunca günlük ortalama göstermek (basitlik için 4 hafta) yani hareketli ortalama. Şu anda bu, son 28 gün için bir değer dizisinin korunmasını ve tüm dizi boyunca haftalık ve aylık 7 gün boyunca bir ortalama hesaplanmasını içerir.

Başlangıçta bunu bir dizi şamandıra kullanarak yapıyordum (enerji "12.12kWh" biçiminde olduğu gibi), ama bu 28 * 4 bayt = 112 bayt (SRAM'ın% 5.4'ü) kullanıyordu. Tek bir ondalık çözünürlük noktasına sahip olmak umrumda değil, bu yüzden uint16_t kullanmak ve rakam 100 ile çarpmak için değişti. Bu 12.12 1212 olarak temsil edilir ve görüntüleme amacıyla 100'e bölmek anlamına gelir.

Dizinin boyutu şimdi 56 bayta düştü (çok daha iyi!).

Rakamı görebildiğim uint8_t değerine düşürmenin önemsiz bir yolu yok. Ondalık bir yerin kaybını tolere edebilirim ("12.12kWh" yerine "12.1kWh"), ancak tüketim genellikle 25.5kWh'den (255, 8-bit işaretsiz tamsayı ile temsil edilen en yüksek değerdir) daha yüksektir. Tüketim asla 10.0kWh'nin altında veya 35.0kWh'nin üzerinde olmamıştır, bu yüzden muhtemelen depolanan rakamlardan 10 çıkarabilirim, ancak bir gün bu sınırları aşacağımızı biliyorum.

Daha sonra bir diziye 9 bitlik değerleri paketlemek için kod test ettim. Bu 0-51.2kWh aralığı verir ve toplam 32 bayt kullanır. Bununla birlikte, böyle bir diziye erişmek oldukça yavaştır, özellikle de bir ortalama hesaplamak için tüm değerleri yinelemeniz gerektiğinde.

Benim sorum şu - üç pencere ile hareketli bir ortalama hesaplamanın daha verimli bir yolu var mı - ömür boyu, 28 gün ve 7 gün? Verimlilik, SRAM kullanımı açısından daha küçük, ancak büyük kodun cezası olmadan anlamına gelir. Tüm değerleri saklamaktan kaçınabilir miyim?


Belirli pencereler üzerinde hareketli bir ortalama hesaplamak mı yoksa ortalama bir tahmin / yaklaşık değer mi yapmak istiyorsunuz?
asheeshr

7 günlük ve 28 günlük bir pencerede hareketli bir ortalama istiyorum.
Cybergibbons

0.2kWh'lik bir çözünürlük kullanabilirsiniz (faktör 5 ile bölün ve çarpın) ve yine de 8 bitte 0-51.2kWh aralığı elde edersiniz
mandal ucube

Harici RAM veya harici Flash'a dizeler ve diğer sabitler koyabilirsiniz - bkz. "Flash bellek veya SRAM biterse ne yapabilirim?" .
David Cary

Yanıtlar:


2

Verilerinizde standart sapma düşükse, yöntemlerden biri pencerenin üzerindeki değerleri toplamak ve yeni değeri eklerken ortalamayı toplamdan çıkarmaya devam etmektir.

Aykırı değer yoksa, bu durum işe yarayacaktır , böylece toplam hata zaman içinde sıfıra düşmektedir.

//Pseudocode

count=0
while new_reading and count<7:
    sum += new_reading        //Calculate the sum of first 7 values
    count++

while new_reading:            //Loop till new readings available
    avg = sum / 7             //Calculate average
    sum -= avg                //Subtract average from sum
    sum += new_reading        //Add next reading to sum
    print avg

2

farklı bir yöntem kullanabilir, geçerli ortalamayı koruyup

average = (weight1*average+weight2*new_value)/(weight1+weight2);

gerçek bir yuvarlanma ortalaması değildir ve farklı semantiğe sahiptir, ancak yine de ihtiyaçlarınızı karşılayabilir

değer çözümü başına 9 bitiniz için daha verimli bir hesaplama yöntemi için, bir dizideki değerlerin en yüksek 8 bitini tutabilir ve en az önemli bitleri ayırabilirsiniz:

uint8_t[28] highbits;
uint32_t lowbits;

bir değeri ayarlamak için onu bölmeniz gerekir

void getvalue(uint8_t index, uint16_t value){
    highbits[index] = value>>1;
    uint32_t flag = (value & 1)<<index;
    highbits|=flag;
    highbits&=~flag;
}

2 ve bir VE ve bir OR ve bir

ortalamayı hesaplamak için hızlandırmak için çeşitli bit hileleri kullanabilirsiniz:

uint16_t getAverage(){
    uint16_t sum=0;
    for(uint8_t i=0;i<28;i++){
        sum+=highbits[i];
    }
    sum<<=1;//multiply by 2 after the loop
    sum+=bitcount(lowbits);
    return sum/28;
}

Bir kullanabilirsiniz verimli paralel bitcount içinbitcount()


1
Bunun 7 ve 28 günlük ortalamaları hesaplamama nasıl izin verdiğini daha fazla açıklayabilir misiniz?
Cybergibbons

Bu yaklaşımı daha önce gürültülü analog değerleri yumuşatmak için kullandım ve kesinlikle oldukça etkili oldu. Ortaya çıkan değerler çok kaba bir nicelik belirleyiciden geçtiği için çok fazla hassasiyete ihtiyacım yoktu. Tarihsel ortalamalara da ihtiyacım yoktu.
Peter Bloomfield

Bu, belirli bir pencere için ortalamanın hesaplanmasına izin vermez.
asheeshr

@Cybergibbons, eski değerlerin daha önce veya daha sonra önemsiz hale gelmesi için pencereye yaklaşmak için farklı ağırlıklar kullanabilir veya 7 günlük pencere için 7 gün ve 28 günlük ortalama için bu hareketli ortalama
mandal manyağı

1

Sadece önceki değerden farkı saklamaya ne dersiniz? Elektronikte, DA / AD dönüştürücüler için kullanılan Delta Sigma dönüştürücü adlı benzer bir kavram vardır. Önceki ölçümün makul olarak mevcut ölçümün yakınında olmasına dayanır.


Başka bir ilginç fikir. Ne yazık ki enerji tüketiminin her zaman böyle olacağından emin değilim, çünkü bir ısı pompası sistemi ve bir gün 30kWh, bir sonraki 10kWh alabilir. Gerçekten veri toplamalı ve görmeliyim.
Cybergibbons

0

Neden değerleri alır almaz toplayamıyorsunuz? Demek istediğim, 1. gün için değeri elde edersiniz, 1'e bölüp 1 ve bir yerde saklarsınız. Sonra 1 değerini değerle çarpıp bir sonraki değere ekler ve ikisini de 2'ye bölersiniz.

Bu yöntemin yapılması düşünebildiğim gibi iki veya üç değişkenli bir yuvarlanma ortalaması yaratacaktır. Bazı kod yazmak istiyorum ama stackexchange yeni benimle ayı lütfen.


Bunun 7 ve 28 günlük pencerelerle nasıl başa çıktığını anlamıyorum?
Cybergibbons

Önceki ve sonraki değerleri takip edin ve bunları koşu ortalamanızdan
toplamaya

1
Öyleyse, 27 günlük tarihi hatırlamaya ihtiyaç duyduğuma geri döndüm, elbette?
Cybergibbons

Düşünüyordum ve haklısın. Bu teknik olarak cevabımı yanlış yapıyor. Buna biraz daha zaman ve sabır yatırıyorum. Belki kutunun dışında bir şey. Bir şey bulursam size haber vereceğim. İşyerimde böyle bir şey yapıyoruz. Sormama izin ver. Karışıklık için üzgünüm.
Aditya Somani

0

28 gün ve 7 gün ile hareketli bir ortalama hesaplamanın daha verimli bir yolu var mı? ... 27 günlük tarihi hatırlamaya mı ihtiyacınız var?

Belki de 28 değer yerine 11 değer depolamaya yetecek kadar yakın olabilirsiniz:

// untested code
// static variables
uint16_t daily_energy[7]; // perhaps in units of 0.01 kWh ?
uint16_t weekly_energy[4]; // perhaps in units of 0.1 kWh ?

void print_week_status(){
    Serial.print( F("last week's total energy :") );
    Serial.println( weekly_energy[0] );
    int sum = 0;
    for( int i=0; i<4; i++ ){
        sum += weekly_energy[i];
    };
    Serial.print( F("Total energy over last 4 complete weeks :") );
    Serial.println( sum );
    int average_weekly_energy = sum/4;
    int average_daily_energy = average_weekly_energy/7;
    Serial.print( F("Average daily energy over last 4 weeks :") );
    Serial.println( average_daily_energy );
}
void print_day_status(){
    Serial.print( F("Yesterday's energy :") );
    Serial.println( daily_energy[0] );
    Serial.print( F("average daily energy over the last 7 complete days: ") );
    int sum = 0;
    for( int i=0; i<7; i++ ){
        sum += daily_energy[i];
    };
    int average = sum/7;
    Serial.println( average );
}

Diğer bir deyişle, son 27 gün boyunca her günün her ayrıntısını saklamak yerine, (a) son 7 gün için 7 günlük detaylı bilgi değerlerini depolamak ve ayrıca (b) 4 veya daha fazla "özetlenmiş" depolamak son 4 haftaya ait her biri için toplam veya ortalama bilgi değerleri.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.