Algoritma / struktur data yang efisien untuk menghitung rata-rata bergerak

9

Saat ini saya sedang mengembangkan sistem LCD grafis untuk menampilkan suhu, aliran, tegangan, daya dan energi dalam sistem pompa panas. Penggunaan LCD grafis berarti bahwa setengah dari SRAM saya dan ~ 75% dari flash saya telah digunakan oleh penyangga layar dan string.

Saat ini saya menampilkan angka min / maks / rata-rata untuk energi Pada tengah malam ketika angka harian diatur ulang, sistem memeriksa apakah konsumsi untuk hari itu di atas atau di bawah minimum atau maksimum sebelumnya, dan menyimpan nilainya. Rata-rata dihitung dengan membagi konsumsi energi kumulatif dengan jumlah hari.

Saya ingin menampilkan rata-rata harian selama minggu dan bulan terakhir (4 minggu untuk kesederhanaan) yaitu rata-rata bergulir. Saat ini melibatkan mempertahankan array nilai untuk 28 hari terakhir dan menghitung rata-rata seluruh array untuk bulanan dan 7 hari terakhir untuk mingguan.

Awalnya saya melakukan ini menggunakan array pelampung (karena energinya dalam bentuk "12.12kWh"), tetapi ini menggunakan 28 * 4 byte = 112 byte (5,4% dari SRAM). Saya tidak keberatan hanya memiliki satu titik desimal resolusi, jadi saya mengubah menggunakan uint16_t dan mengalikan angka dengan 100. Ini berarti bahwa 12.12 direpresentasikan sebagai 1212, dan saya membaginya dengan 100 untuk keperluan tampilan.

Ukuran array sekarang turun menjadi 56 byte (jauh lebih baik!).

Tidak ada cara sepele untuk mengurangi angka menjadi uint8_t yang bisa saya lihat. Saya bisa mentolerir hilangnya tempat desimal ("12.1kWh" bukan "12.12kWh"), tetapi konsumsi sering kali lebih tinggi dari 25.5kWh (255 adalah nilai tertinggi yang diwakili oleh bilangan bulat 8-bit unsigned). Konsumsi tidak pernah di bawah 10.0kWh atau di atas 35.0kWh, jadi saya bisa mengurangi 10 dari angka yang disimpan, tetapi saya tahu bahwa suatu hari kita akan melampaui batas ini.

Saya kemudian menguji kode untuk mengemas nilai 9-bit ke dalam array. Ini memberikan kisaran 0-51.2kWh dan menggunakan total 32 byte. Namun, mengakses array seperti ini sangat lambat, terutama ketika Anda harus mengulang semua nilai untuk menghitung rata-rata.

Jadi pertanyaan saya adalah - apakah ada cara yang lebih efisien untuk menghitung rata-rata bergerak dengan tiga jendela - seumur hidup, 28 hari dan 7 hari? Efisiensi berarti lebih kecil dalam hal penggunaan SRAM, tetapi tanpa penalti kode besar. Bisakah saya menghindari menyimpan semua nilai?

Cybergibbons
sumber
Apakah Anda ingin menghitung rata-rata bergerak di atas jendela tertentu atau perkiraan / perkiraan rata-rata akan dilakukan?
asheeshr
Saya ingin rata-rata bergerak selama 7 hari dan 28 hari.
Cybergibbons
Anda bisa menggunakan resolusi 0.2kWh (bagi dan gandakan dengan faktor 5) dan Anda masih mendapatkan kisaran 0-51.2kWh dalam 8 bit
ratchet freak
Anda mungkin akhirnya meletakkan string dan konstanta lain dalam RAM eksternal atau Flash eksternal - lihat "Apa yang bisa saya lakukan jika saya kehabisan memori Flash atau SRAM?" .
David Cary

Jawaban:

2

Jika data Anda memiliki standar deviasi yang rendah, maka salah satu metode adalah menjumlahkan nilai di atas jendela, dan kemudian terus mengurangi rata-rata dari jumlah tersebut, sambil menambahkan nilai baru.

Ini akan bekerja dengan baik jika tidak ada outlier , sehingga mengarah ke kesalahan agregat yang cenderung nol dari waktu ke waktu.

//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
asheeshr
sumber
2

Anda dapat menggunakan metode yang berbeda, Anda mempertahankan rata-rata saat ini dan kemudian lakukan

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

itu bukan rolling average yang sebenarnya dan memiliki semantik yang berbeda, tetapi mungkin sesuai dengan kebutuhan Anda

untuk metode perhitungan yang lebih efisien untuk solusi 9 bit per nilai, Anda dapat menyimpan 8 bit nilai tertinggi dalam array dan memisahkan bit yang paling tidak signifikan:

uint8_t[28] highbits;
uint32_t lowbits;

untuk menetapkan nilai, Anda perlu membaginya

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

menghasilkan 2 menggeser sebuah AND dan sebuah OR dan sebuah tidak

untuk menghitung rata-rata, Anda dapat menggunakan berbagai trik bit untuk mempercepatnya:

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;
}

Anda dapat menggunakan bitcount paralel yang efisien untukbitcount()

aneh ratchet
sumber
1
Bisakah Anda menjelaskan lebih lanjut bagaimana ini memungkinkan saya menghitung rata-rata 7 dan 28 hari?
Cybergibbons
Saya telah menggunakan pendekatan ini untuk menghaluskan nilai analog yang bising sebelumnya, dan itu tentu saja cukup efektif. Saya tidak membutuhkan banyak presisi, karena nilai yang dihasilkan dimasukkan melalui quantiser yang sangat kasar. Saya juga tidak membutuhkan rata-rata historis.
Peter Bloomfield
Ini tidak memungkinkan menghitung rata-rata untuk jendela tertentu.
asheeshr
@Cybergibbons Anda dapat menggunakan bobot yang berbeda untuk mendekati jendela sehingga nilai lama menjadi tidak penting lebih awal atau lebih lambat, atau menyimpan 7 hari untuk jendela 7 hari dan rata-rata bergerak ini untuk rata-rata 28 hari
ratchet freak
1

Bagaimana kalau hanya menyimpan perbedaan dari nilai sebelumnya? Dalam elektronik ada konsep serupa yang disebut konverter Delta Sigma, yang digunakan untuk konverter DA / AD. Itu bergantung pada fakta bahwa pengukuran sebelumnya cukup dekat dengan yang sekarang.

jippie
sumber
Ide lain yang menarik. Sayangnya saya tidak yakin konsumsi energi akan selalu seperti ini, karena ini adalah sistem pompa panas dan suatu hari bisa memakan waktu 30kWh, 10kWh berikutnya. Saya benar-benar perlu mengumpulkan data dan melihat.
Cybergibbons
0

Mengapa Anda tidak bisa menambahkan nilai-nilai itu bersama begitu Anda mendapatkannya. Jadi yang saya maksud adalah Anda mendapatkan nilai untuk hari 1, Anda membaginya dengan 1 dan menyimpannya dan 1 di suatu tempat. Kemudian Anda kalikan 1 dengan nilai dan tambahkan ke nilai berikutnya dan bagi keduanya dengan 2.

Melakukan metode ini akan membuat rata-rata bergulir dengan dua atau tiga variabel seperti yang dapat saya pikirkan. Saya akan menulis beberapa kode tapi saya baru di stackexchange jadi tolong ikut saya.

Aditya Somani
sumber
Saya tidak mengerti bagaimana ini berkaitan dengan jendela 7 hari dan 28 hari?
Cybergibbons
Melacak nilai sebelumnya dan selanjutnya dan terus menambahkan dan mengurangi mereka dari rata-rata Anda berjalan ...
Aditya Somani
1
Jadi saya kembali dalam keadaan perlu mengingat 27 hari sejarah, tentunya?
Cybergibbons
Saya sudah berpikir dan Anda benar. Sehingga secara teknis membuat jawaban saya salah. Saya menginvestasikan lebih banyak waktu dan kesabaran untuk itu. Mungkin sesuatu di luar kotak. Saya akan memberi tahu Anda jika saya menemukan sesuatu. Kami sering melakukan hal seperti ini di tempat kerja saya. Biarkan saya bertanya-tanya. Maaf tentang kebingungannya.
Aditya Somani
0

apakah ada cara yang lebih efisien untuk menghitung rata-rata bergerak dengan ... 28 hari dan 7 hari? ... perlu mengingat 27 hari sejarah ...?

Anda mungkin cukup dekat menyimpan 11 nilai daripada 28 nilai, mungkin sesuatu seperti:

// 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 );
}

Dengan kata lain, alih-alih menyimpan setiap detail setiap hari selama 27 hari terakhir, (a) menyimpan 7 atau lebih nilai informasi harian terperinci selama 7 hari terakhir, dan juga (b) menyimpan 4 atau lebih "diringkas". nilai total atau rata-rata informasi untuk masing-masing 4 minggu terakhir.

David Cary
sumber