Generátor harmonického signálu: Rozdiel medzi revíziami
Zo stránky SensorWiki
d uart.c updates |
Bez shrnutí editace |
||
| (38 medziľahlých úprav od rovnakého používateľa nie je zobrazených.) | |||
| Riadok 1: | Riadok 1: | ||
Záverečný projekt predmetu MIPS / LS2026 - | Záverečný projekt predmetu MIPS / LS2026 - Oleksandr Mykyta | ||
== Zadanie == | == Zadanie == | ||
Úlohou bolo generovať harmonický | Úlohou bolo generovať harmonický signal bez použitia funkcií sin() alebo cos(). | ||
Na tento účel bol použitý oscilátor realizovaný ako prenosová funkcia: | |||
<code> | |||
H(s) = 1 / ((s · T)^2 + 1) | H(s) = 1 / ((s · T)^2 + 1) | ||
</code> | |||
Zároveň bolo potrebné zmerať jeden bod frekvenčnej charakteristiky systému: | Zároveň bolo potrebné zmerať jeden bod frekvenčnej charakteristiky systému: | ||
<code> | |||
H(s) = 1 / (s · T + 1) | H(s) = 1 / (s · T + 1) | ||
</code> | |||
pre frekvenciu ω = 1 / T, kde T = 0 | pre frekvenciu: | ||
<code>ω = 1 / T</code>, | |||
kde | |||
<code>T = 0.5 s</code>. | |||
Výstupný | Výstupný signal má mať tvar: | ||
<code> | |||
A₀ + A₁ · sin(ωt + φ) | A₀ + A₁ · sin(ωt + φ) | ||
</code> | |||
kde: | kde: | ||
A₀ = 128 | <code>A₀ = 128</code>, | ||
A₁ = 100 | <code>A₁ = 100</code>. | ||
'''Literatúra:''' | |||
* [http://ap.urpi.fei.stuba.sk/sensorwiki/index.php/Acrob_technical_description Dokumentácia k doske Acrob] | |||
* [https://en.wikipedia.org/wiki/Harmonic_oscillator Harmonic oscillator] | |||
* [https://en.wikipedia.org/wiki/Transfer_function Transfer function] | |||
* [https://en.wikipedia.org/wiki/Euler_method Euler method] | |||
__TOC__ | __TOC__ | ||
| Riadok 25: | Riadok 42: | ||
== Analýza a opis riešenia == | == Analýza a opis riešenia == | ||
Cieľom riešenia je vytvoriť | Cieľom riešenia je vytvoriť sinusový signal bez použitia matematických funkcií sin() alebo cos(). Tento problém sa rieši pomocou diskretneho oscilátora, ktorý vychádza z diferenciálnej rovnice harmonického kmitania. | ||
Základom je rovnica: | Základom je rovnica: | ||
<code> | |||
y'' + ω²y = 0 | y'' + ω²y = 0 | ||
</code> | |||
Táto rovnica popisuje harmonické kmity | Táto rovnica popisuje harmonické kmity a jej riešením sú funkcie <code>sin()</code> a <code>cos()</code>. To znamená, že ak vieme túto rovnicu numericky riešiť, vieme generovať sinus. | ||
=== Teoretický základ a odvodenia === | === Teoretický základ a odvodenia === | ||
| Riadok 39: | Riadok 58: | ||
Vychadzajme z prenosovej funkcie systému: | Vychadzajme z prenosovej funkcie systému: | ||
<code> | |||
H(s) = Y(s) / X(s) = 1 / (T · s + 1) | H(s) = Y(s) / X(s) = 1 / (T · s + 1) | ||
</code> | |||
Po úprave: | Po úprave: | ||
<code> | |||
Y(s) · (T · s + 1) = X(s) | Y(s) · (T · s + 1) = X(s) | ||
</code> | |||
<code> | |||
T · s · Y(s) + Y(s) = X(s) | T · s · Y(s) + Y(s) = X(s) | ||
</code> | |||
Použitím inverznej Laplaceovej transformacie (kde s predstavuje deriváciu) dostaneme: | Použitím inverznej Laplaceovej transformacie (kde s predstavuje deriváciu) dostaneme: | ||
<code> | |||
T · dy(t)/dt + y(t) = x(t) | T · dy(t)/dt + y(t) = x(t) | ||
</code> | |||
Týmto získame diferenciálnu rovnicu systému v časovej oblasti. | Týmto získame diferenciálnu rovnicu systému v časovej oblasti. | ||
==== | ==== Diskretizacia ==== | ||
Mikrokontrolér pracuje v | Mikrokontrolér pracuje v diskretnom čase, preto je potrebné nahradiť derivácie rozdielmi medzi vzorkami. | ||
Pre druhú deriváciu použijeme aproximáciu: | Pre druhú deriváciu použijeme aproximáciu: | ||
<code> | |||
y'' ≈ (y[n] − 2y[n−1] + y[n−2]) / T_s² | y'' ≈ (y[n] − 2y[n−1] + y[n−2]) / T_s² | ||
</code> | |||
Po dosadení do | Po dosadení do <code>y'' + ω²y = 0</code>: | ||
<code> | |||
y[n] = (2 / (1 + ω²T_s²)) · y[n−1] − (1 / (1 + ω²T_s²)) · y[n−2] | y[n] = (2 / (1 + ω²T_s²)) · y[n−1] − (1 / (1 + ω²T_s²)) · y[n−2] | ||
</code> | |||
Táto rovnica predstavuje numerickú aproximáciu oscilátora, ale nie je ideálna z hľadiska stability amplitúdy. | Táto rovnica predstavuje numerickú aproximáciu oscilátora, ale nie je ideálna z hľadiska stability amplitúdy. | ||
==== | ==== Diskretny oscilátor (presný model) ==== | ||
Presnejší prístup vychádza z trigonometrických identít: | Presnejší prístup vychádza z trigonometrických identít: | ||
<code> | |||
sin(A + B) = sin(A)cos(B) + cos(A)sin(B) | sin(A + B) = sin(A)cos(B) + cos(A)sin(B) | ||
</code> | |||
<code> | |||
sin(A − B) = sin(A)cos(B) − cos(A)sin(B) | sin(A − B) = sin(A)cos(B) − cos(A)sin(B) | ||
</code> | |||
Po sčítaní: | Po sčítaní: | ||
<code> | |||
sin(A + B) + sin(A − B) = 2 · sin(A) · cos(B) | sin(A + B) + sin(A − B) = 2 · sin(A) · cos(B) | ||
</code> | |||
Dosadením: | Dosadením: | ||
A = (n−1)θ | <code>A = (n−1)θ</code>, | ||
B = θ | <code>B = θ</code> | ||
dostaneme: | dostaneme: | ||
<code> | |||
sin(nθ) + sin((n−2)θ) = 2 · cos(θ) · sin((n−1)θ) | sin(nθ) + sin((n−2)θ) = 2 · cos(θ) · sin((n−1)θ) | ||
</code> | |||
Označením: | Označením: | ||
y[n] = sin(nθ) | <code>y[n] = sin(nθ)</code> | ||
vznikne rekurentný vzťah: | vznikne rekurentný vzťah: | ||
<code> | |||
y[n] = 2 · cos(θ) · y[n−1] − y[n−2] | y[n] = 2 · cos(θ) · y[n−1] − y[n−2] | ||
</code> | |||
Tento vzťah generuje stabilný | Tento vzťah generuje stabilný sinusový signal bez zmeny amplitúdy. | ||
==== Výpočet parametrov ==== | ==== Výpočet parametrov ==== | ||
| Riadok 100: | Riadok 143: | ||
Platí: | Platí: | ||
<code> | |||
θ = ω · T_s | θ = ω · T_s | ||
</code> | |||
kde: | kde: | ||
T = 0 | <code>T = 0.5 s</code> | ||
ω = 1 / T = 2 rad/s | <code>ω = 1 / T = 2 rad/s</code> | ||
SAMPLE_RATE = 1000 Hz | <code>SAMPLE_RATE = 1000 Hz</code> | ||
<code>T_s = 0.001 s</code> | |||
θ = 0 | <code>θ = 0.002</code> | ||
==== Aproximacia cos() ==== | ==== Aproximacia cos() ==== | ||
| Riadok 113: | Riadok 160: | ||
Keďže nie je dovolené použiť funkciu cos(), použije sa Taylorov rozvoj: | Keďže nie je dovolené použiť funkciu cos(), použije sa Taylorov rozvoj: | ||
<code> | |||
cos(θ) ≈ 1 − θ²/2 | cos(θ) ≈ 1 − θ²/2 | ||
</code> | |||
Z toho: | Z toho: | ||
<code> | |||
2cos(θ) ≈ 2 · (1 − θ²/2) | 2cos(θ) ≈ 2 · (1 − θ²/2) | ||
</code> | |||
==== Inicializácia oscilátora ==== | ==== Inicializácia oscilátora ==== | ||
| Riadok 125: | Riadok 174: | ||
Pre správnu činnosť oscilátora sú potrebné počiatočné hodnoty: | Pre správnu činnosť oscilátora sú potrebné počiatočné hodnoty: | ||
y1 = 1 | <code>y1 = 1</code>, | ||
y2 = 1 − θ²/2 | <code>y2 = 1 − θ²/2</code> | ||
==== Generovanie signalu ==== | |||
Oscilátor generuje hodnoty v rozsahu: | |||
<code>[-1, 1]</code> | |||
Požadovaný výstup: | Požadovaný výstup: | ||
<code> | |||
x = A0 + A1 · y | x = A0 + A1 · y | ||
</code> | |||
kde: | kde: | ||
<code>A0 = 128</code> | |||
<code>A1 = 100</code> | |||
==== Diskretizacia systému 1 / (sT + 1) ==== | ==== Diskretizacia systému <code>1 / (sT + 1)</code> ==== | ||
Zo spojitej rovnice: | Zo spojitej rovnice: | ||
<code> | |||
T · dy/dt + y = x | T · dy/dt + y = x | ||
</code> | |||
Použitím Eulerovej metódy: | Použitím Eulerovej metódy: | ||
<code> | |||
dy/dt ≈ (y[n] − y[n−1]) / T_s | dy/dt ≈ (y[n] − y[n−1]) / T_s | ||
</code> | |||
Po úprave: | Po úprave: | ||
<code> | |||
y[n] = x − (T / T_s) · y[n] − (T / T_s) · y[n−1] | y[n] = x − (T / T_s) · y[n] − (T / T_s) · y[n−1] | ||
</code> | |||
<code> | |||
y[n] · (1 + (T / T_s)) = x − (T / T_s) · y[n−1] | y[n] · (1 + (T / T_s)) = x − (T / T_s) · y[n−1] | ||
</code> | |||
<code> | |||
y[n] = (T_s / (T + T_s)) · x + (T / (T + T_s)) · y[n−1] | y[n] = (T_s / (T + T_s)) · x + (T / (T + T_s)) · y[n−1] | ||
</code> | |||
Po zavedení: | Po zavedení: | ||
α = T_s / (T + T_s) | <code>α = T_s / (T + T_s)</code> | ||
dostaneme praktický tvar: | dostaneme praktický tvar: | ||
<code> | |||
y[n] = α · x + (1 − α) · y[n−1] | y[n] = α · x + (1 − α) · y[n−1] | ||
</code> | |||
<code> | |||
y[n] = y[n−1] + α · (x − y[n−1]) | y[n] = y[n−1] + α · (x − y[n−1]) | ||
</code> | |||
=== Algoritmus a program === | === Algoritmus a program === | ||
Algoritmus programu využíva | Algoritmus programu využíva diskretny oscilátor a numericku aproximáciu systému <code>1 / (sT + 1)</code>. Základné výpočty prebiehajú v prerušení Timer1 s frekvenciou 1 kHz: | ||
* výpočet oscilátora | * výpočet oscilátora | ||
| Riadok 181: | Riadok 248: | ||
<tabs> | <tabs> | ||
<tab name="main.c"><syntaxhighlight lang="c++" style="background: LightYellow;"> | <tab name="main.c"><syntaxhighlight lang="c++" style="background: LightYellow;"> | ||
#define F_CPU | #define F_CPU 16000000 UL | ||
#include <avr/io.h> | #include <avr/io.h> | ||
#include <avr/interrupt.h> | #include <avr/interrupt.h> | ||
#include <stdio.h> | #include <stdio.h> | ||
#include "uart.h" | #include "uart.h" | ||
| Riadok 206: | Riadok 276: | ||
FILE mystdout = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE); | FILE mystdout = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE); | ||
ISR(TIMER1_COMPA_vect) | ISR(TIMER1_COMPA_vect) { | ||
{ | y = OSC_COEFF * y1 - y2; | ||
y = OSC_COEFF * y1 - y2; | |||
y2 = y1; | y2 = y1; | ||
y1 = y; | y1 = y; | ||
float x = A0 + A1 * y; | float x = A0 + A1 * y; | ||
y_sys = y_sys + alpha * (x - y_sys); | y_sys = y_sys + alpha * (x - y_sys); | ||
OCR0A = (uint8_t)(y_sys); | OCR0A = (uint8_t)(y_sys); | ||
printf("%d,%d\n", (int)x, (int)y_sys); | printf("%d,%d\n", (int) x, (int) y_sys); | ||
} | } | ||
void timer1_init() | void timer1_init() { | ||
{ | TCCR1B |= (1 << WGM12); | ||
TCCR1B |= (1 << WGM12); | |||
OCR1A = 15999; | OCR1A = 15999; | ||
TCCR1B |= (1 << CS10); | TCCR1B |= (1 << CS10); | ||
TIMSK1 |= (1 << OCIE1A); | TIMSK1 |= (1 << OCIE1A); | ||
} | } | ||
void pwm_init() | void pwm_init() { | ||
{ | DDRD |= (1 << PD6); | ||
DDRD |= (1 << PD6); | |||
TCCR0A |= (1 << COM0A1) | (1 << WGM01) | (1 << WGM00); | TCCR0A |= (1 << COM0A1) | (1 << WGM01) | (1 << WGM00); | ||
TCCR0B |= (1 << CS01); | TCCR0B |= (1 << CS01); | ||
} | } | ||
int main(void) | int main(void) { | ||
{ | uart_init(); | ||
uart_init(); | stdout = & mystdout; | ||
stdout = &mystdout; | |||
pwm_init(); | pwm_init(); | ||
timer1_init(); | timer1_init(); | ||
float Ts = 1.0 / SAMPLE_RATE; | float Ts = 1.0 / SAMPLE_RATE; | ||
alpha = Ts / (T + Ts); | alpha = Ts / (T + Ts); | ||
float theta = (1.0 / T) * (1.0 / SAMPLE_RATE); | float theta = (1.0 / T) * (1.0 / SAMPLE_RATE); | ||
y1 = 1.0; | y1 = 1.0; | ||
y2 = 1.0 - (theta * theta) / 2.0; | y2 = 1.0 - (theta * theta) / 2.0; | ||
OSC_COEFF = 2.0 * (1.0 - (theta * theta) / 2.0); | OSC_COEFF = 2.0 * (1.0 - (theta * theta) / 2.0); | ||
sei(); | sei(); | ||
while (1) {} | |||
} | } | ||
</syntaxhighlight></tab> | |||
<tab name="uart.h"><syntaxhighlight lang="c++" style="background: LightYellow;"> | <tab name="uart.h"><syntaxhighlight lang="c++" style="background: LightYellow;"> | ||
#define set_bit(ADDRESS, BIT)(ADDRESS |= (1 << BIT)) | |||
#define clear_bit(ADDRESS, BIT)(ADDRESS &= ~(1 << BIT)) | |||
#define set_bit(ADDRESS,BIT) (ADDRESS |= (1<<BIT)) | |||
#define clear_bit(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT)) | |||
#ifndef UART_H_ | #ifndef UART_H_ | ||
| Riadok 290: | Riadok 344: | ||
#include <stdio.h> | #include <stdio.h> | ||
#define BAUD_PRESCALE | #define BAUD_PRESCALE(((F_CPU / (BAUDRATE * 16 UL))) - 1) | ||
void uart_init(void); | |||
void uart_init( void ); | int uart_putc(char c, FILE * stream); | ||
void uart_puts(const char * s); | |||
char uart_getc(void); | |||
void delay(int delay); | |||
int uart_putc( char c, FILE *stream ); | |||
void uart_puts( const char *s ); | |||
char uart_getc( void ); | |||
void delay(int delay); | |||
#endif /* UART_H_ */ | #endif /* UART_H_ */ | ||
| Riadok 310: | Riadok 356: | ||
<tab name="uart.c"><syntaxhighlight lang="c++" style="background: LightYellow;"> | <tab name="uart.c"><syntaxhighlight lang="c++" style="background: LightYellow;"> | ||
#include <avr/io.h> | #include <avr/io.h> | ||
#include <util/delay.h> | #include <util/delay.h> | ||
#include "uart.h" | #include "uart.h" | ||
void uart_init( void ) | void uart_init(void) { | ||
{ | #include <util/setbaud.h> | ||
UBRR0H = UBRRH_VALUE; | |||
UBRR0L = UBRRL_VALUE; | |||
#if USE_2X | |||
UCSR0A |= (1 << U2X0); | UCSR0A |= (1 << U2X0); | ||
#else | |||
UCSR0A &= ~(1 << U2X0); | UCSR0A &= ~(1 << U2X0); | ||
#endif | |||
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); | UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); | ||
| Riadok 330: | Riadok 376: | ||
} | } | ||
int uart_putc(char c, FILE *stream) | int uart_putc(char c, FILE * stream) { | ||
{ | if (c == '\n') { | ||
if (c == '\n') | |||
loop_until_bit_is_set(UCSR0A, UDRE0); | loop_until_bit_is_set(UCSR0A, UDRE0); | ||
UDR0 = '\r'; | UDR0 = '\r'; | ||
| Riadok 344: | Riadok 388: | ||
} | } | ||
void uart_puts(const char * s) {} | |||
void | char uart_getc(void) { | ||
loop_until_bit_is_set(UCSR0A, RXC0); | |||
return UDR0; | |||
} | } | ||
void delay(int delay) { | |||
{ | for (int i = 1; i <= delay; i++) | ||
_delay_ms(1); | |||
} | } | ||
</syntaxhighlight></tab> | </syntaxhighlight></tab> | ||
</tabs> | </tabs> | ||
Zdrojový kód: [[Médiá:MykytaOleksandr_sources.zip|MykytaOleksandr_sources.zip]] | |||
=== Overenie === | === Overenie === | ||
| Riadok 367: | Riadok 408: | ||
Funkcia systému bola overená pomocou výpisu dát cez UART. Do sériového portu sa posielajú dvojice hodnôt: | Funkcia systému bola overená pomocou výpisu dát cez UART. Do sériového portu sa posielajú dvojice hodnôt: | ||
x, y_sys | <code>x, y_sys</code> | ||
Tieto hodnoty je možné zobraziť napríklad pomocou | Tieto hodnoty je možné zobraziť napríklad pomocou '''SerialPlot''', kde je viditeľný vstupný sinusový signal a výstup systému. | ||
Zo | |||
'''Namerené priebehy vstupu a výstupu zo Serial Plot''' | |||
<gallery widths="200" heights="150" mode="packed"> | |||
Súbor:x_high.png|<code>x_max = 227</code> | |||
Súbor:x_low.png|<code>x_min = 28</code> | |||
Súbor:y_high.png|<code>y_max = 198</code> | |||
Súbor:y_low.png|<code>y_min = 57</code> | |||
</gallery> | |||
Pri overovaní bolo sledované, či: | |||
* amplitúda výstupu zodpovedá očakávaniu | |||
* systém vykazuje fázový posun | |||
* priebeh signalu je stabilný bez driftu | |||
* výstup reaguje správne na vstupný sinus | |||
Zo signalov je možné pozorovať zmenu amplitúdy a fázový posun, čo predstavuje bod frekvenčnej charakteristiky systému. | |||
'''Video:''' | |||
<center><youtube>MkgRE0QNgtk</youtube></center> | |||
Video demonštruje reálne správanie systému. Je na ňom viditeľné generovanie sinusového signalu, jeho spracovanie systémom a výstupné dáta zobrazené v '''SerialPlot'''. Video slúži na overenie, že implementácia funguje podľa očakávania. | |||
== Čo by som urobil inak == | == Čo by som urobil inak == | ||
Na ďalšíkrát by som si lepšie rozmyslel, ako presne riešiť diskretizáciu, lebo ten jednoduchý prístup funguje, ale nie je úplne ideálny. | |||
A ešte by som si skôr urobil priamo meranie aj na mikrokontroléri v kode, aby som nemusel všetko riešiť cez SerialPlot. | |||
[[Category:AVR]] [[Category:MIPS]] | [[Category:AVR]] [[Category:MIPS]] | ||
Aktuálna revízia z 16:47, 18. apríl 2026
Záverečný projekt predmetu MIPS / LS2026 - Oleksandr Mykyta
Zadanie
Úlohou bolo generovať harmonický signal bez použitia funkcií sin() alebo cos(). Na tento účel bol použitý oscilátor realizovaný ako prenosová funkcia:
H(s) = 1 / ((s · T)^2 + 1)
Zároveň bolo potrebné zmerať jeden bod frekvenčnej charakteristiky systému:
H(s) = 1 / (s · T + 1)
pre frekvenciu:
ω = 1 / T,
kde
T = 0.5 s.
Výstupný signal má mať tvar:
A₀ + A₁ · sin(ωt + φ)
kde:
A₀ = 128,
A₁ = 100.
Literatúra:
Analýza a opis riešenia
Cieľom riešenia je vytvoriť sinusový signal bez použitia matematických funkcií sin() alebo cos(). Tento problém sa rieši pomocou diskretneho oscilátora, ktorý vychádza z diferenciálnej rovnice harmonického kmitania.
Základom je rovnica:
y + ω²y = 0
Táto rovnica popisuje harmonické kmity a jej riešením sú funkcie sin() a cos(). To znamená, že ak vieme túto rovnicu numericky riešiť, vieme generovať sinus.
Teoretický základ a odvodenia
Laplaceova transformacia
Vychadzajme z prenosovej funkcie systému:
H(s) = Y(s) / X(s) = 1 / (T · s + 1)
Po úprave:
Y(s) · (T · s + 1) = X(s)
T · s · Y(s) + Y(s) = X(s)
Použitím inverznej Laplaceovej transformacie (kde s predstavuje deriváciu) dostaneme:
T · dy(t)/dt + y(t) = x(t)
Týmto získame diferenciálnu rovnicu systému v časovej oblasti.
Diskretizacia
Mikrokontrolér pracuje v diskretnom čase, preto je potrebné nahradiť derivácie rozdielmi medzi vzorkami.
Pre druhú deriváciu použijeme aproximáciu:
y ≈ (y[n] − 2y[n−1] + y[n−2]) / T_s²
Po dosadení do y + ω²y = 0:
y[n] = (2 / (1 + ω²T_s²)) · y[n−1] − (1 / (1 + ω²T_s²)) · y[n−2]
Táto rovnica predstavuje numerickú aproximáciu oscilátora, ale nie je ideálna z hľadiska stability amplitúdy.
Diskretny oscilátor (presný model)
Presnejší prístup vychádza z trigonometrických identít:
sin(A + B) = sin(A)cos(B) + cos(A)sin(B)
sin(A − B) = sin(A)cos(B) − cos(A)sin(B)
Po sčítaní:
sin(A + B) + sin(A − B) = 2 · sin(A) · cos(B)
Dosadením:
A = (n−1)θ,
B = θ
dostaneme:
sin(nθ) + sin((n−2)θ) = 2 · cos(θ) · sin((n−1)θ)
Označením:
y[n] = sin(nθ)
vznikne rekurentný vzťah:
y[n] = 2 · cos(θ) · y[n−1] − y[n−2]
Tento vzťah generuje stabilný sinusový signal bez zmeny amplitúdy.
Výpočet parametrov
Platí:
θ = ω · T_s
kde:
T = 0.5 s
ω = 1 / T = 2 rad/s
SAMPLE_RATE = 1000 Hz
T_s = 0.001 s
θ = 0.002
Aproximacia cos()
Keďže nie je dovolené použiť funkciu cos(), použije sa Taylorov rozvoj:
cos(θ) ≈ 1 − θ²/2
Z toho:
2cos(θ) ≈ 2 · (1 − θ²/2)
Inicializácia oscilátora
Pre správnu činnosť oscilátora sú potrebné počiatočné hodnoty:
y1 = 1,
y2 = 1 − θ²/2
Generovanie signalu
Oscilátor generuje hodnoty v rozsahu:
[-1, 1]
Požadovaný výstup:
x = A0 + A1 · y
kde:
A0 = 128
A1 = 100
Diskretizacia systému 1 / (sT + 1)
Zo spojitej rovnice:
T · dy/dt + y = x
Použitím Eulerovej metódy:
dy/dt ≈ (y[n] − y[n−1]) / T_s
Po úprave:
y[n] = x − (T / T_s) · y[n] − (T / T_s) · y[n−1]
y[n] · (1 + (T / T_s)) = x − (T / T_s) · y[n−1]
y[n] = (T_s / (T + T_s)) · x + (T / (T + T_s)) · y[n−1]
Po zavedení:
α = T_s / (T + T_s)
dostaneme praktický tvar:
y[n] = α · x + (1 − α) · y[n−1]
y[n] = y[n−1] + α · (x − y[n−1])
Algoritmus a program
Algoritmus programu využíva diskretny oscilátor a numericku aproximáciu systému 1 / (sT + 1). Základné výpočty prebiehajú v prerušení Timer1 s frekvenciou 1 kHz:
- výpočet oscilátora
- generovanie vstupu x
- výpočet výstupu systému
- výstup cez PWM
- odosielanie dát cez UART
#define F_CPU 16000000 UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include "uart.h"
#define SAMPLE_RATE 1000.0
#define T 0.5
#define A0 128
#define A1 100
float OSC_COEFF;
volatile float y = 0;
volatile float y1 = 0;
volatile float y2 = 0;
volatile float y_sys = 0;
float alpha;
FILE mystdout = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE);
ISR(TIMER1_COMPA_vect) {
y = OSC_COEFF * y1 - y2;
y2 = y1;
y1 = y;
float x = A0 + A1 * y;
y_sys = y_sys + alpha * (x - y_sys);
OCR0A = (uint8_t)(y_sys);
printf("%d,%d\n", (int) x, (int) y_sys);
}
void timer1_init() {
TCCR1B |= (1 << WGM12);
OCR1A = 15999;
TCCR1B |= (1 << CS10);
TIMSK1 |= (1 << OCIE1A);
}
void pwm_init() {
DDRD |= (1 << PD6);
TCCR0A |= (1 << COM0A1) | (1 << WGM01) | (1 << WGM00);
TCCR0B |= (1 << CS01);
}
int main(void) {
uart_init();
stdout = & mystdout;
pwm_init();
timer1_init();
float Ts = 1.0 / SAMPLE_RATE;
alpha = Ts / (T + Ts);
float theta = (1.0 / T) * (1.0 / SAMPLE_RATE);
y1 = 1.0;
y2 = 1.0 - (theta * theta) / 2.0;
OSC_COEFF = 2.0 * (1.0 - (theta * theta) / 2.0);
sei();
while (1) {}
}
#define set_bit(ADDRESS, BIT)(ADDRESS |= (1 << BIT))
#define clear_bit(ADDRESS, BIT)(ADDRESS &= ~(1 << BIT))
#ifndef UART_H_
#define UART_H_
#include <stdio.h>
#define BAUD_PRESCALE(((F_CPU / (BAUDRATE * 16 UL))) - 1)
void uart_init(void);
int uart_putc(char c, FILE * stream);
void uart_puts(const char * s);
char uart_getc(void);
void delay(int delay);
#endif /* UART_H_ */
#include <avr/io.h>
#include <util/delay.h>
#include "uart.h"
void uart_init(void) {
#include <util/setbaud.h>
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
#if USE_2X
UCSR0A |= (1 << U2X0);
#else
UCSR0A &= ~(1 << U2X0);
#endif
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);
UCSR0B = _BV(RXEN0) | _BV(TXEN0);
}
int uart_putc(char c, FILE * stream) {
if (c == '\n') {
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = '\r';
}
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = c;
return 0;
}
void uart_puts(const char * s) {}
char uart_getc(void) {
loop_until_bit_is_set(UCSR0A, RXC0);
return UDR0;
}
void delay(int delay) {
for (int i = 1; i <= delay; i++)
_delay_ms(1);
}
Zdrojový kód: MykytaOleksandr_sources.zip
Overenie
Funkcia systému bola overená pomocou výpisu dát cez UART. Do sériového portu sa posielajú dvojice hodnôt:
x, y_sys
Tieto hodnoty je možné zobraziť napríklad pomocou SerialPlot, kde je viditeľný vstupný sinusový signal a výstup systému.
Namerené priebehy vstupu a výstupu zo Serial Plot
-
x_max = 227 -
x_min = 28 -
y_max = 198 -
y_min = 57
Pri overovaní bolo sledované, či:
- amplitúda výstupu zodpovedá očakávaniu
- systém vykazuje fázový posun
- priebeh signalu je stabilný bez driftu
- výstup reaguje správne na vstupný sinus
Zo signalov je možné pozorovať zmenu amplitúdy a fázový posun, čo predstavuje bod frekvenčnej charakteristiky systému.
Video:
Video demonštruje reálne správanie systému. Je na ňom viditeľné generovanie sinusového signalu, jeho spracovanie systémom a výstupné dáta zobrazené v SerialPlot. Video slúži na overenie, že implementácia funguje podľa očakávania.
Čo by som urobil inak
Na ďalšíkrát by som si lepšie rozmyslel, ako presne riešiť diskretizáciu, lebo ten jednoduchý prístup funguje, ale nie je úplne ideálny.
A ešte by som si skôr urobil priamo meranie aj na mikrokontroléri v kode, aby som nemusel všetko riešiť cez SerialPlot.