Operácie

Generátor harmonického signálu

Zo stránky SensorWiki

Verzia z 19:03, 17. apríl 2026, ktorú vytvoril StudentMIPS (diskusia | príspevky) (initial)

Záverečný projekt predmetu MIPS / LS2026 - Meno Priezvisko


Zadanie

Úlohou je generovať harmonický signál bez použitia funkcií sin() alebo cos(). Namiesto toho sa má použiť oscilátor realizovaný ako prenosová funkcia:

1 / ((s·T)^2 + 1)

Zároveň je potrebné zmerať jeden bod frekvenčnej charakteristiky systému:

1 / (s·T + 1)

na frekvencii ω = 1 / T, pričom T = 0,5 s.

Výstupný signál má mať tvar:

A₀ + A₁·sin(ωt + φ)

kde: A₀ = 128 A₁ = 100



Analýza a opis riešenia

Cieľom riešenia je vytvoriť sínusový signál bez použitia matematických funkcií sin() alebo cos(). Tento problém sa rieši pomocou diskrétneho 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 (napr. pružina alebo LC obvod) a jej riešením sú funkcie sin() a cos(). To znamená, že ak vieme túto rovnicu numericky riešiť, vieme generovať sínus bez použitia knižničných funkcií.


Diskretizácia

Mikrokontrolér pracuje v diskrétnom čase, preto je potrebné nahradiť derivácie rozdielmi medzi vzorkami (metóda konečných diferencí).

Použije sa aproximácia druhej derivácie:

y ≈ (y[n] − 2y[n−1] + y[n−2]) / T_s²

Po dosadení do diferenciálnej rovnice dostaneme:

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. V praxi však nie je ideálna, pretože amplitúda signálu sa môže časom meniť.


Diskrétny oscilátor

Na generovanie stabilného sínusu sa použije presný diskrétny model založený na trigonometrickej identite:

sin(nθ) = 2cos(θ)·sin((n−1)θ) − sin((n−2)θ)

Po označení:

y[n] = sin(nθ)

dostaneme rekurentný vzťah:

y[n] = 2cos(θ)·y[n−1] − y[n−2]

Tento vzťah generuje stabilný sínusový signál 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

θ = 2 · 0,001 = 0,002


Aproximácia cos()

Keďže nie je dovolené použiť funkciu cos(), použije sa aproximácia (Taylorov rozvoj):

cos(θ) ≈ 1 − θ²/2

Z toho:

2cos(θ) ≈ 2·(1 − θ²/2)

Tento výraz sa použije ako koeficient oscilátora v programe.


Inicializácia oscilátora

Pre správnu činnosť oscilátora sú potrebné počiatočné hodnoty:

y1 = 1 y2 = 1 − θ²/2

Tieto hodnoty zabezpečia vznik sínusového priebehu.


Generovanie signálu

Oscilátor generuje hodnoty v rozsahu ⟨−1, 1⟩.

Požadovaný výstup je:

A₀ + A₁·sin(...)

Preto sa signál upraví:

x = A0 + A1 · y

kde: A0 = 128 A1 = 100

Tým sa signál posunie do kladného rozsahu vhodného pre PWM.


Systém 1 / (sT + 1)

Systém je realizovaný ako filter prvého rádu:

T·dy/dt + y = x

Po diskretizácii (dopredná Eulerova metóda) dostaneme:

y_sys[n] = y_sys[n−1] + α·(x − y_sys[n−1])

kde:

α = T_s / (T + T_s)

Pre dané hodnoty:

α ≈ 0,001996


Meranie frekvenčnej charakteristiky

Na vstup systému je privádzaný sínusový signál s frekvenciou:

ω = 1 / T

Tým sa meria odozva systému presne v tomto bode frekvenčnej charakteristiky.

Porovnaním vstupu x a výstupu y_sys je možné určiť zosilnenie a fázový posun.


Realizácia v programe

Celý výpočet prebieha 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 - odoslanie dát cez UART


#define F_CPU 16000000UL

#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)
    {
    }
}


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 Serial Plotteru, kde je viditeľný vstupný sínusový signál a výstup systému.

Zo signálov je možné pozorovať zmenu amplitúdy a fázový posun, čo predstavuje bod frekvenčnej charakteristiky systému.


Čo by som urobil inak

Pri riešení by bolo možné použiť presnejšiu metódu diskretizácie systému 1/(sT + 1), napríklad bilineárnu transformáciu, ktorá by zlepšila presnosť modelu.

Taktiež by bolo možné implementovať presnejší výpočet cos(θ) bez aproximácie, napríklad pomocou lookup tabuľky.

Ďalším zlepšením by mohlo byť automatické vyhodnotenie amplitúdy a fázového posunu priamo v mikrokontroléri namiesto spracovania na PC.