Softwarové ošetření vstupních hodnot (kmitání, nestabilita)

Většinou nějaká verze průměru stačí a kdyžtak je mnohem jednodušší spáchat běžnou DP, kterou si v něčem navrhneš.
Na druhou stranu FIR taky vlastně dělá konvoluci, takže by mělo stačit pro něj zjistit koeficienty (získat je z parametrů rozdělení).
Když bude zejtra čas, zkusim něco smotnout, ale nic neslibuju :slight_smile:.

Nějaký ten link:
tvorbawebu.wz.cz/faiirfe/help/filtry.htm
automatizace.cz/article.php?a=725
programujte.com/?akce=clanek&cl= … m-jazyce-c

To by bylo super!!! Byl bych ti moc vdecny, pokud bys chtel, aj bych se odmenil:-) Hlavne se zase neco naucim!!:slight_smile: hned se vrham na tve odkazy, takove jsem nenasel…

a ty odkazy taky super…nechapu ze jsem nic z toho za celou dobu nenasel, zvlast ten na progrmujte.com kdyz tam taky pisu:D

Už to chodí. Je to obyčejný FIR. Ten napíše i malý děcko. Jediný, co musíš vyřešit, je získat koeficienty z gaussova rozdělení (jinej název je normální rozdělení). Jejich výpočet najdeš na wiki. en.wikipedia.org/wiki/Normal_distribution. Pak je přepočítat do celočíselnýho rozsahu a použít :slight_smile:.

Kód pro matlab/octave:[code]close all;
clear;
clc;

S=3;
N=1000;
t=1:1:N;
x=zeros(1,N);
x(100) = N/10;
x(200:400) = 17;
yg=zeros(1,N);
ya=zeros(1,N);

%% Moving average 8
for i=8:N
ya(i) = sum(x(i-7:i))/8;
end

%% Gaussian filter

L = 11; % filter length
% ----------------
gx = -(L-1)/2 : 1 : (L-1)/2;
mi = 0; % mean
sig2 = 1; % variance
G = 1/(sqrt(2pisig2)) * exp( -(gx-mi).^2 /(2sig2) ); % normal distr.
% ----------------
% filter compute
for i=L:N
yg(i) = sum( x(i-L+1:i) .
G );
end

%% Plots
%plot(t, x, ‘b’, t, yg, ‘r’, t, ya, ‘g’);
stem(t, x, ‘b’);
hold on;
stem(t, ya, ‘g’);
stem(t, yg, ‘r’);
hold off;
xlabel(‘vzorek’); ylabel(‘amplituda’); title(‘Impulse response’);
legend(‘x’, ‘y average 8’, ‘y gauss’);[/code]
gaus filter.gif

Zdravim, hele, jak si mi poslal ten posledni odkaz z toho programujte.com, tak nad tim tak uvazuju to je naprogramovano ke zpetne filtraci a ne filtraci v prubehu ze? Ja si zjistil ze ten array list umi pracovat dynamicky, ale to jak je to napsane asi neni ten pripad? Potreboval bych aby to pocital neustale s novymi hodnotami…:-/

CHtel jsem je totiz pouzit oba dva…a chci je aplikovat na ten pripad a chtel bych aby to fungovalo jak ten prumer co si psal drive s tim bufferem…A taky nejsem vubec chytry s tech koeficientu…pls help…:frowning:

Buffer v klouzavym průměru = zpožďovací linka ve FIR. Do funkce budeš jen muset přidat pole s koeficienty a to pronásobit se zpožďovací linkou (= bufferem minulých vzorků) a celý to sečít. Je to jednoduchý. Členy “z^-1” ve schématu filtrů jsou zpožděné prvky. Tedy první Z^-1 je minulý. Druhý Z^-1 za prvním je předminulý atd… Realizace této zpožďovací linky se dělá kruhovým bufferem. Jelikož ho C nezná(DSP pro to mají hw podporu), používají se k tomu pointery - je to stejný princip jako jsem použil u klouzavýho průměru. Trochu se snaž, preci za tebe nebudu tu bakalářku nebo co to je dělat celou :slight_smile: (ikdyž by pro mě bylo rychlejší ti sem vysypat kód a vyřešeno).

Koeficinty si spočítej třeba v excelu, rovnici máš na odkazu na wiki. Jsou tam i grafy, podle nich si můžeš svoje výpočty zkontrolovat. Pro filtr 11. řádu z příkladu pro matlab výše vyšly koeficienty:[code]N(0,1): 1,48671951473430e-06 0,000133830225764885 0,00443184841193801 0,0539909665131881 0,241970724519143 0,398942280401433 0,241970724519143 0,0539909665131881 0,00443184841193801 0,000133830225764885 1,48671951473430e-06

N(0,3): 0,00357099380847855 0,0160040839217032 0,0513934432679231 0,118255073909459 0,194969655722741 0,230329432980890 0,194969655722741 0,118255073909459 0,0513934432679231 0,0160040839217032 0,00357099380847855[/code]
V matlabu jsem je počítal přímo, do procesoru si samozřejmě nahraješ jen tabulku s nimi.

Řekněme, že budeš počítat v 16 bitech, vstupní hodnoty budou v rozsahu <0, 255>. Koeficienty do tabulky pro C je tedy nutné přednásobit 65535/255 = 257 (a zaokrouhlit). Výstup bude v rozsahu <0, 65535> a tedy pro úpravu výsledku do <0, 255> stačí vydělit 256 (8x posun vpravo).

Pro rozdělení N(0, 3) zjistíš, že výstup je pouze v rozsahu <0, 65025> a po vydělení <0, 254>. Je to dáno tím, že součet zaokrouhlených koeficientů již není plných 257, ale pouze 255 (to je věc statistiky a gaussova rozdělení). To lze vyřešit třeba přičtením 1 ke krajním koeficientům.

Kurnik…nevim proste:D Jsem z toho uplne vedle a nemam bohuzel cas si s tim hrat mam jeste moc prace…jedine co se mi povedlo je prevest toho tveho gausse do Cecka…akorat mi nejde udelat prubezny buff, je tam jen buff na dany pocet hodnot ktery filtruje a pak nacte dalsich par hodnot a zase zfiltruje…, ale u toho gausse to asi nejde aby pricital novou a posledni vyhodil, rozhodi to cely vyraz…nebo myslis ze jde? Kdybych ti poslal co jsem vytvoril?:smiley:

To sis měl vzpomenout dřív když nemáš čas. Holt půjdeš až v září :slight_smile:.

[code]#define FIR_BUF_LEN 11
unsigned char fir(unsigned char newSample)
{
// 11th order FIR filter
// y[n] = b0x[n] + b1x[n-1] + …
// b10 b9 b8 b7 … b1 b0
static unsigned char coef] = {2, 4, 13, 30, 50, 59, 50, 30, 13, 4, 2}; // N(0,3)
// filter coefitients (gaussian), requires sum(coef) = 65535/255 = 257
// same length as “buff”
static unsigned char buff[FIR_BUF_LEN];
unsigned int sum = 0;
unsigned char i;

for(i=0; i<FIR_BUF_LEN-1; i++)	// posunout zpozdovaci linku
{
	buff* = buff*;
}
buff[FIR_BUF_LEN - 1] = newSample;	// vlozit novy vzorek

for(i=0; i<FIR_BUF_LEN; i++)	// spocitat sumu
{
	sum += (unsigned int)buff* * coef*;
}

// divide by 256
if(sum < 65408) return (unsigned char)((sum + (1<<7)) >> 8);
else return (unsigned char)(sum >> 8);

}[/code]
Jednodušejc to už napsat snad nejde (a proto je to dost neoptimalizovaný). Nějak nevim co nechápeš na pronásobení dvou polí a sečtení součinů.
Polovinu výpočetního času zabere jen přerovnání pole vzorků a proto by ho programátor vynechal (vyřešil to přes pointery), ale to tebe asi netlačí.

Na víc taky dneska (ani zejtra) čas nemám :confused:.****

Cauec…ja vim ze nemas cas ja uz ted taky moc ne, jen kdybys mrknul na meho gausse pripadne…mam to opsane z toho matlabu do Cecka jeslti si myslis ze to mam dobre, diky…
main.c (4.22 KB)

:slight_smile: Tak schválně… co myslíš?

Že špatně? Áno, trefa :smiley:. Nejen implementační chyby filtru (neposouváš zpožďovací linku ani nevyužíváš pointery pro eliminaci této potřeby, datové typy tě též moc netrápí…), nýbrž i matematické - závorky navíc nejsou na škodu, chybějící jaksi ano :wink:. To má do funkčního filtru bohužel moc daleko. Ty (gaussovský) koeficienty si radši spočítej třeba v excelu a do programu je naklapej ručně, je to jistota.

Kurnik jasne mi to je!! ja nevim, posledni dobou jsem trochu mimo, stres a tak:D Takze to posledni je vlastne hotove…diky, a ty koeficienty jsou pro to rozdeleni N(0,3) vynasobene temi 257 a zaokrouhlene…akorat by tam na zacatku nemela byt dvojka ale jednicka, to me chvili matlo:-) ale diky…kdyby neco zapisu, diky za trpelivost:D

Zdravim, mam problem…on zrejme bere hodnoty z 8 bitu a pocita v 16ti a vraci do osmi. Ja kdyz mam vstupni hodnoty z adc prevodniku v desetibitech tak bych mel nasobit jen 64 ne? taky ty koeficienty bych mel nasobit jen 64 a nepovedlo se. aj kdyz jsem zmenil tzp na float kdyz tam hazu desetinne porad tam neni prubeh od krajni hodnoty ke krajni ale nejak to tam hapruje kolem stredu…i kdyz jsem to vubec neprevadel do tech 16ti bitu a nechal tak…pak jen podelil ctevrku na osmibitovy timer pro pwm. porad to same, neni tam neco s tim buffem?? ja nejak nevim:-(((((((((((( ykousel jsem co se dalo… kurnik:-(

a nezda se mi ze tam neni ten cyklus kdyz dojede na konec buffu tak aby jel od zacatku, dik moc, pls help, dneska chci aby to fakt slapalo ten filtrik…

Pro 10b vstup a výstup bude:
-návratovej typ funkce uint
-koeficienty násobený 64 místo 257 (a jejich součet taky doladit na 64)
-buffer bude uint místo uchar
-na koci se bude dělit 64
-> “return ((sum + (1<<5)) >> 6);”
Podmínka a větev else jsou zbytečné (1023*64 + 32 < 65535)

No na vystup potrebuju osm bitu, protoze to jde do PWMka, takze to deleni jsem nechal…a jak rikam, vstup i koef jsem nasobil 64…Takze by to melo fungovat a protooze jsou tam ted desetinna cisla tak spis pouzit float nez unsignet int, ne? ten jsem popuzil i u koef a i u buffu…

Fakt tam nema byt podminka na preteceni buffu? a neni tam proste jina chyba jeste? pripadne ti muzu poslat zdrojak, ale je to to same jen se zmenou nasobeni a typu hodnot, tak jak jsi rikal, to uz jsem v labaku zkousel…

Jo a co jsi myslel tim doladit soucet na 64? diky.

To se stále snažíš o tvůj beznadějný pokus nebo upravuješ můj filtr? U mýho žádný násobení vstupu konstantou není ani nemá bejt.
Jakou máš vzorkovačku, že tady zkoušíš něco s floatem? Ten je totiž na AVR výpočetně podstatně náročnější.

Nene, ja pak pochopil ten tvuj, tak mam ten tvuj…vzdyt se o nem bavime…no tak je tam desetinne cislo, tak musim pouzit float…ne? protoze kdyz mam 10bit vstup a koeficienty se nasobi tou 64, tak jsou docela male, tak bych az tak moc nezaokrouhloval, ne?..noo jinak kdyz pocitas v 16tce tak vstupni hodnoty ktere jsou v 10tce nemas nasobit staci jen koef? ahaaa…mno tak nevim jestli jenom toto vyresi ten problem…ale muzu to zitra zkusit…jinak jak jsem se ptal na to vyladeni sumy jak si rikal, co jsi tim myslel? a na to preteceni buffu? je to tam dobre jo? tady totiz moc nevidim ze by tam rusil predesle hodnoty nebo co…Dik moc

Koeficienty jsou desetinná čísla, ale na 8bitech je z hlediska výpočetní náročnosti lepší se floatu vyhnout pokud je to možné.

Jesli se ti zdá přesnost koeficientů příliš malá, můžem to přepsat do 32 bitů. Jelikož se používají pouze součty a násobení, spomalí se to jen asi 2x. Kdybys to napsal ve float, zpomalilo by se to řádově.
Ovšem v tomto případě bych to s přesností rozhodně nehrotil.

Se vstupními daty se nic nedělá. Z jejich maximální hodnoty právě odvozuju konstantu, kterou násobíš koeficienty tak, aby během výpočtů nemohlo dojít k přetečení. Kdybys vstupní data vynásobil 64 (zvýšil rozsah z 10b na 16b), už by ses do 16b při násobení koeficienty nevešel.

Tak jak je to primitivně napsaný buffer nepřeteče. Vždy po zavolání funkce se pole přerovná - celé se posune dolů (= nejstarší vzorek se přepíše předposledním) a na vrchol se vloží vzorek nový.

Jinak s tím dělením - na koci se bude tedy dělit opět 256ti:
“return (unsigned char)((sum + (1<<7)) >> 8 );”
Podmínka tem bejt nemusí - viz. výše.

cau, porad stejny problem…vstupni hodnoty jsem neprepocitaval, jenom koef. ale porad je to na vystupu tak ve ctvrtine puvodniho rozsahu. zkousel jsem i cele pocitat v desitkove, koef jsem si samozrejme prepocitaval, vsecko kde byly desetinne jsem dal float ale porad to same…kdyz jsem snizil to konecne deleni (smazal nebo nechal jen deleni 4 uz nevim) tak rozsah sice byl, ale prubeh nahoru dolu nahoru dolu nahoru dolu, jako by stale pretikal nebo ja nevim…misto toho co by mel v danem pripade delat, na grrafu mela byt nejaka linie nejak zvlnena podle signalu, ale nekde z vrchu rozsahu az dolu nebo naopak, jen jednou.

Klouzavy prumer funguje i kdyz obcas vyskoci strasne moc…coz nevim proc…ale jinak dobry…to rikam jen proto ze to neni zpusobene nicim jinym nez tim programem, nevim jestli ten buff nebo co, zkousel jsem tam neco pomenit, ale neuspesne:-(

Je tam sum+= buff**coef*…nemelo by tam byt nekde i sum- ? ja vim to je asi blbost, on se sam pomaze… ale i tak, ja nevim, jen vymyslim co by to mohlo byt:-(

takze kdyz jsem to predelal na pocitani v desitkove, dal tam floaty, jak jsem rikal furt stejne ale ted jsem zmenil i na vstupu z char na int a uz tam je jeden prubeh…i kdyz asi vic zakmitu nez puvodni signal…(takze porad se vse chovalo “stejne” az pri zmene vstupu na int zmena…)**
main.c (4.64 KB)

To teda nevim, co s tim stále děláš…
Tohle je přímej přepis kódu z matlabu, kterej filtruje vstupní 10b signál a vrací 8b signál dle obrázku níže:[code]unsigned char gauss(unsigned int newSample)
{
// 11th order FIR filter
// newSample = 10b data
// returns 8b data
// y[n] = b0x[n] + b1x[n-1] + …
// b10 b9 b8 b7 … b1 b0
static unsigned char coef] = {0, 1, 3, 8, 12, 16, 12, 8, 3, 1, 0}; // N(0,3)
// filter coefitients (gaussian), requires sum(coef) = 65535/1023 = 64
// same length as “buff”
static unsigned int buff[FIR_BUF_LEN];
unsigned int sum = 0;
unsigned char i;

for(i=0; i<FIR_BUF_LEN-1; i++) // posunout zpozdovaci linku
{
buff* = buff*;
}
buff[FIR_BUF_LEN - 1] = newSample; // vlozit novy vzorek

for(i=0; i<FIR_BUF_LEN; i++)   // spocitat sumu
{
	sum += buff* * coef*;
}

// divide by 256
return ((unsigned char)(sum >> 8)); // mel by byt jeste soucet “+ (1 << 7)” a podminka proti preteceni
}[/code]

Co do toho pouštíš za signál? Tvoje úpravy jsou totiž drsný zabijáci výkonu(včetně nahrazení shiftu dělením v klouzavym průměru) a takovou kHz sinusovku už to patrně nezvládne.****
gaus_filtr.gif

cauec. signal poustim ruzny, proste to co leze z potenciometru, to nemusis resit. Ja jsem vetsi cast vyresil tim ze jsem tam zrusil to preruseni a hazeni vystupu toho pwmka na ted PA7, prepajel jsem desku a nastavil to normalne klasicky, prestoze deska vypada hrozne, je to funkcni:D

Ale problem stale pretrvava, verim ze jsou ty funkce napsany spravne, bez pochyb se v tom vyznas, znas, umis a jeste tu i ukazes vysledky:-) bomba, jenze bohuzel tak jak to tam mam napsane mi to nechce pocitat:-(

nevis kde je chybka? Neboj uz to mam prepsane ty malickosti mnohem lepe, me ted neslo o vykon, jenom o to at to pocita, jen jsem zkousel co to mohlo zpusobit…ale jinak je to vlastne “stejne” aj ta chyba…

ja to tam asi spatne volam nebo kdyz tam hazu z toho ADC1 neni tam neco spatne? to co z toho leze ven je vlastne to same jako bez te funkce, kdyz posilam rovnou hodnoty z ADC1 a zkousel jsem to treba napsat ttrosku oklikou, napsal nove promenne a v mainu pridal toto:

prumer = avg(read_ADC1()/4); // deleni 4 kvuli osmi bitovemu pwm

PWM1(prumer); //volani toho pwm

a nastala zmena, vyhazovalo mi to nejakou malou vicemene konstantni hodnotu, to by mozna mohlo odpovidat tomu proc se to proste chovalo stejne jako i bez toho avg, kdyz to nasobil malou konstantni hodnotou…kdyz jsem to napsal primo:

PWM1(avg(read_ADC1()/4));

ale ted me napada, nemel jsem tam napsat PWM1(prumer()); ?:smiley:

ja uz nevim (stejne to je i s pripadem toho gausse, vsechno funkcni jenom ty filtry proste nepocitaji…nebo mi tam chybi nejaka knihovna? define? ) ja to tam asi spatne volam nebo nevim…pro jistotu pripojuji ten novy zdrojak, ale je to vlastne “to same”, pls kouknete na to, podle me je jen nekde nejaka mala chybka, diky moc…
main.c (8.63 KB)