Piccola guida a Matlab/Octave

Piccola guida a Matlab/Octave

Posted by giuliomoro on Dom, 02/22/2009 - 22:58

Piccola guida a Octave/Matlab

Disclaimer
beta 1: mancano completamente nozioni su for, if e else

Questa vuole essere una piccola guida incompleta e non esaustiva di introduzione all'uso di Octave/Matlab ed è rivolta agli studenti di TDS 2008, in quanto cercadi spiegare solamente ciò che è stato affrontato durante il primo semestre di TAENSM, con espliciti riferimenti a quanto fatto in classe, oltre a dei suggerimenti(rivolti soprattutto a chi "lavora" con windows XP) su come lavorare più comodamente, piacevolmente e produttivamente.
A titolo di esempio, lacunosa è la descrizione della funzione plot(si rimanda all'help della medesima per approfondimenti), non vengono inoltre introdotte le matrici, non viene dato nessun fondamento teorico del linguaggio, e molte sono le non rigorosità(ad esempio nel numero di argomenti di una funzione) ecc.
Non troverete qui procedimenti logici per raggiungere la soluzione dei problemi che ci vengono posti(spero che riusciremo a pubblicarli altrove),ma troverete solamente i mezzi necessari per implementarli in Octave.
Si farà spesso riferimento a Matlab e Octave utilizzando i due nomi come sinonimi. La differenza tra i due la trovate nelle FAQ di tecnosuono.org .
Ho scritto tutto ciò di corsa in due giorni, quindi sarà pieno di errori, vi prego di segnalarmeli.
Per insulti, repliche, suggerimenti, correzioni, donazioni ecc potete contattarmi all'indirizzo email che ha come dominio "yahoo.it" e come username "giuliomoro".


Introduzione: cosa è e perché è

Vorrei innanzitutto far chiarezza su ciò che matlab/octave è effettivamente(per quanto ci riguarda): è un programma che fa calcoli e permette di visualizzarnenumericamente o graficamente, oppure esportarne, i risultati. Per fare ciò esso interpreta i comandi che vengono scritti sulla sua finestra di terminale. In questo modo può essere utilizzato come una calcolatrice. Ad esempio se volete conoscere il risultato dell'operazione 2+6 basta scrivere al prompt

2+6

che da come risultato

ans = 8

Similmente gli si può far fare calcoli più complessi come

( -34*41/(17*(1-83)) )

che da ovviamente come risultato

ans = 1

Questi risultati possono essere assegnati a delle variabili, nel caso vogliano essere utilizzati successivamente:

a=2^4

che produce:

a = 16
Array

Matlab opera agevolmente con i vettori(array). Con vettore intendiamo un insieme ordinato di numeri, come ad esempio: 1 2 4 7 10 .
Per creare un array in Matlab si può, ad esempio, elencarne i valori, che devono essere racchiusi fra parentesi quadre:

[1 2 4 7 10]

produrrà:

ans =
1 2 4 7 10

Che è ovviamente inutile, in quanto non abbiamo assegnato questo array a nessuna variabile. Molto più utile è quanto segue:

b = [1 2 4 7 10]
b =
1 2 4 7 10

A volte si può avere bisogno di creare un array di cui non si abbia voglia di elencare tutti gli elementi. Per fortuna è possibile fare gli sfaticati nelcaso(particolare ma molto frequente)in cui si abbia bisogno di un insieme di numeri equidistanti fra loro, come ad esempio: 1 2 3 4 5 oppure 0.23 0.46 0.69 0.92 1.15
Dunque volendo creare un array "equispaziato" in Matlab è necessario indicare, tra le parentesi quadre, il valore di partenza, l'incremento(la distanza tra ciascunelemento e il successivo) e il valore finale, separati da due punti(:) :

c = [0:0.1:0.6]
c =
0 0.1 0.2 0.3 0.4 0.5 0.6

è comprensibile che quando gli elementi dell'array iniziano a diventare molti comincerà a darci fastidio vedere ogni volta sullo schermo l'elenco di tutti i valori degli elementi del neonato array. Per evitare ciò si aggiunge alla fine del comando un ; (punto e virgola) :

d = [0:0.001:1];

che ci eviterà di passare il pomeriggio a visualizzare sullo schermo i 1001(!) elementi dell'array.
Il punto e virgola dunque evita che sullo schermo ci venga mostrato il risultato dell'elaborazione. Spesso, per il motivo appena citato, ci tornerà comodo far sì che le cose vadano così, quindi consiglio di metterlo sempre a fine riga, tranne nei casi in cui ci sia utile visualizzare il risultato(ad esempio durante il debugging).
È possibile visualizzare il valore di un certo elemento(il primo, il secondo...) di un array in questa maniera:

d(1)
ans = 0
d(2)
ans = 0.001
d(3)
ans = 0.002

Si noti che il primo elemento di un array è l'elemento 1 e non l'elemento 0, come succede in altri linguaggi di programmazione.

Sì ma 'sti array a cosa mi servono?
Con Matlab è facile utilizzare gli array per effettuare la stessa operazione su un insieme di numeri. Ad esempio, volendo il risultato della moltiplicazione dei numeri1 3 5 7 per 3 si può fare così:

f = [1:2:7];
g = f*3
g =
3 9 15 21

Notare che quando si fa un'operazione tra uno scalare e un array il risultato è un array contenente il medesimo numero di elementi dell'array di partenza.

Nel codice Matlab possono essere inseriti dei commenti anteponendovi il simbolo % (percento):

h = [1:0.001:10]; %questo array è molto lungo, fortuna che mi sono ricordato di mettere il punto e virgola!



Cenni sulle funzioni

In Matlab esistono delle funzioni built-in(costruite-dentro) utili per la maggior parte degli scopi che possono interessarci.
Il modo corretto per richiamare una funzione è il seguente:

func (arg)

dove func è il nome della funzione e arg è l'argomento della funzione.Ad esempio volendo indicare il logaritmo naturale di 5 scrivereste abitualmente su un pezzo di carta:
log 5
in questo caso il nome della funzione è "log" e l'argomento è "5".
In Matlab questo si traduce in:

log (5) %potreste volerlo assegnare ad una variabile,come faccio nella riga successiva
i = log(5);

Il numero degli argomenti non è uguale per tutte le funzioni: si va da 0 a qualcuno.
E come faccio a sapere quanti sono e in che ordine devo scriverli?
Matlab include un utile ed esaustivo help in linea: è possibile consultare la documentazione relativa a ciascuna funzione built-in semplicemente scrivendo sul terminale:

help func %questo restituirà un messaggio di errore in quanto non esiste nessuna funzione chiamata "func"
help log %questo invece ci indicherà come utilizzare la funzione "log".
%Se guardate bene alla fine dell'help c'è anche un'utile lista di funzioni correlate.
%Noterete ad esempio che esiste una funzione chiamata"log10"
help log10 %ci spiega che "log10" serve per fare il logaritmo in base 10 di un numero

Spesso ci tornerà comodo utilizzare come argomenti di una funzione non dei valori numerici, ma il contenuto di qualche variabile precedentemente calcolata, oppureeseguire il calcolo direttamente negli argomenti della funzione:

j = 45;
k = 55;
l = j + k;
m = log10(l)
% o, facendo eseguire il calcolo direttamente negli argomenti della funzione:
m = log10(j+k);
m = 2

Questa è una lista incompleta di funzioni built-in in Matlab:

return error input keyboard menu pause zeros ones eye rand randn linspace logspace meshgridans eps realmax realmin pi i j inf NaN flops nargin nargout computer clock cputime date etimetic toc diag fliplr flipud reshape rot90 tril triu compan hadamard hankel hilb invhilb magicpascal rosser toeplitz vander wilkinson abs acos acosh angle asin asinh atan atan2 atanh ceilconj cos cosh exp fix floor imag log log10 real rem round sign sin sinh sqrt tan tanh besselellipj ellipke erf gamma rat cond norm rcond det trace null orth rref chol lu inv qr nnlspinv eig poly hess qz rdf2csf cdf2rdf schur balance svd expm logm sqrtm funm max min meanmedian std sort sum prod cumsum cumprod trapz diff gradient del2 corrcoef cov fft fft2 ifftifft2 abs angle fftshift roots poly polyval polyvalm residue polyfit conv deconv interp1interp2 interpft griddata ode23 ode45 quad quad8 fmin fmins fzero fplot plot loglog semilogxsemilogy fill polar bar stairs errorbar hist fplot title xlabel ylabel zlabel text gtext gridplot3 fill3 contour contour3 mesh meshc surf surfc waterfall view hidden axis cylinder spherefigure gcf clf close subplot axes gca cla axis hold figure axes line text patch surfaceset get reset delete drawnow print printopt orient abs setstr isstr str2mat eval strcmp upperlower num2str int2str str2num sprintf sscanf

a volte i nomi sono self-explicative, altre volte danno solo un indizio di ciò di cui tratta la funzione, altre volte sono incomprensibili.Divertitevi a scoprirle...help aiuta.
Prestate particolare attenzione a : plot, length, sin

Lavorare con comodità

Il prof Nicola ha provato più volte a farci notare la stupidità dei nostri stupidi sistemi operativi monoutente, mouse-driven, stupid-programmed, crash-oriented etc etc...
Non entro nel merito della discussione, ma qui di seguito mostro come sia possibile lavorare con comodità anche con Windows XP.

Dopo un po' che scriviamo righe di codice dal prompt capiterà che nel bel mezzo dell'impegnativa risoluzione di un'importante problema facciamo partire un loop infinitooppure cancelliamo per sbaglio una variabile che avevamo calcolato con somma fatica o in qualche altra maniera crashamo Octave etc etc, con conseguente perdita di tuttoquanto abbiamo elaborato fino a quel punto
Potrebbe anche succedere che uno preferisca scrivere il codice da qualche parte in modo da avere chiaro tutto quanto mentre lavora e avere il codice pronto per eventuali modifiche senza doverlo ridigitare ogni volta...
Per fare ciò è possibile scrivere le righe di codice con un editor di testo PURO(that is, per voi(noi) Microsoftcentrici: NO Microsoft Word, NO Microsoft Wordpad, ma SÌ Microsoft Blocco note), e poi salvarle in un file con estensione .m .
Evviva! Abbiamo fregato il prof Nicola: esiste un programma Microsoft fornito col nostro sistema operativo stupido che ci permette di scrivere addirittura degli script in Matlab!
Sì certo, ma non lo fa nella maniera più comoda.
Consiglio dunque a chi è ancora stuck con il vecchio notepad di digitare il comando "edit" dal terminale di Octave. Ne uscirà un editor di teso fantastico, con le non indifferenti capacità di:

  • colorare le keywords di matlab(for, if, sin, plot, length etc), i numeri e i commenti con bei colori diversi
  • evidenziare le parentesi
  • mostrare i numeri delle righe(per evitare di sbagliare ogni volta contandole a mano su blocco note)(per farlo: view->Line numbers)
  • essere software libero

    I file salvati col VOSTRO editor di testo preferito potranno poi essere aperti con un praticissimo tasto destro-apri con-octave, ammesso che funzioni, solo che alla fine dell'esecuzione dello script, Octave verrà chiuso e non sarà possibile visualizzare i dati elaborati, né interagire con essi da terminale.
    Abbiamo sperimentato in classe più volte una situazione del genere e avevamo provveduto a ""risolverla"" inserendo la funzione pause() (questa funzione non richiede argomenti)alla fine del codice nel file .m . In questa maniera il grafico che i nostri script producevano rimaneva visibile fino alla pressione di un tasto(in effetti pause() blocca l'esecuzione del programma finché non viene premuto un tasto), salvo poi chiudersi insieme ad Octave nel momento in cui il tasto veniva premuto.Questo metodo ci permette sì di visualizzare a lungo il grafico, ma non di interagire con le variabili calcolate dal programma, per scopi ad esempio di debugging o di controlloo di "improvement on the fly" dello script.
    È dunque molto più comodo, una volta lanciato Octave, utilizzare il comando "run" per lanciare il file .m da noi elaborato. Ma come si fa a dirgli dove si trova sto file?
    Il vostro programma mousclickautoinstallante vi avrà lasciato sul desktop un pratico doppioclickcollegamento a Octave. Bene, lanciandolo da lì Octave considererà come cartella homela cartella che si trova in c:/programmi/octave/bin dunque se avete un file .m in quella cartella basterà un semplice
    run nomefile.m

    È vero però che è ben scomodo mettere i nostri propri file .m lì dentro, in una cartella distante dal desktop e già piena di file...si rischia di fare confusione. Il nostro sistemaoperativo stupido ci viene però in aiuto un'altra volta:
    Fate click(aaargh) col tasto destro sul suddetto collegamento, scegliete proprietà, nella maschera che si apre digitate(oppure copia incolla, tutto rigorosamente col mouse)nel campo "DA" il percorso completo della cartella in cui bramate tenere i vostri elaborati. Io ad esempio tengo una cartella sul desktop , quindi nel mio caso il percorso è:"c:/documents and settings/giulio/desktop/scriptoctave".
    Ora quando riaprirete Octave utilizzando questo collegamento egli riconscerà come home la directory che avete appena settato, quindi con un pratico

    run nomefile.m

    dal prompt di octave potrete aprire il file nomefile.m che si trova nella vostra pratica cartella sul desktop, o dove più vi aggrada.Inoltre, una volta terminata l'esecuzione del medesimo, potrete continuare a lavorare sulle variabili che sono state calcolate da Octave, oppure lanciare un altro script, senza bisogno di riavviare octave(che pure, almeno sul mio computer) non è immediato a partire.

    Ho inoltre recentemente scoperto che è possibile incollare il testo nel prompt di octave, il che si rivela spesso utile e pratico. Per farlo non funzionano ctrl V , ctrl shift V,shift ins o qualunque altra combinazione vi venga in mente, in Windows XP si può fare(a quanto ne so io)solo nel seguente modo:cliccando col tasto destro sul pulsante della finestra di octave nella barra delle applicazioni, selezionando modifica e poi incolla.
    È inoltre molto utile la caratteristica di Octave di ricordarsi i comandi inseriti dal terminale. Questo significa che se avete appena scritto "A=1*4-log(10)/(4-a+b^2)"e vi serve riscriverlo e/o modificarlo(e farlo rieseguire a Octave), potete premere la frecciainsu, e riappariranno così al prompt i comandi che avete inserito recentemente.Ancora più utile può essere scrivere le prime lettere del comando che state cercando e premere la freccia insu, in questo modo appariranno solo i comandi già digitati che iniziano con quelle lettere.

    Funzioni: un po'di pratica, finalmente

    Vediamo ora nel dettaglio le funzioni che abbiamo usato in classe:

    - length(arg)

    Il numero degli elementi contenuti in un array si chiama "lunghezza dell'array". Per conoscerlo è possibile usare la funzione "length(arg)", dove l'argomento "arg" èl'array di cui vogliamo conoscere la lunghezza:

    sinc=0.001;
    dur=5;
    T=[0:sinc:dur-sinc];
    numerodicampioni=length(T)
    numerodicampioni = 5000
    - sin(arg)

    La funzione sin(arg) restituisce il seno dell'argomento "arg", che è l'angolo espresso in radianti:

    sin(0)
    ans = 0
    sin(3.14)
    ans = 0.0015927

    Esiste in matlab la variabile di sistema "pi" che vale PIgreco (3.141592653589793...), per cui:

    sin(2*pi)
    ans = -2.4493e-016
    %questa è la notazione esponenziale di matlab: è equivalente a -2.4493*10^(-16)
    %si nota che questo valore è molto prossimo a zero, come è giusto che sia

    Volendo calcolare al tempo 0.04 secondi il valore di una funzione sinusoidale di ampiezza 1 e frequenza 237:

    frq=237;
    A=1;
    t=0.04;
    valore=A*sin(2*pi*frq*t)
    valore = 0.12533

    Abbiamo visto più sopra che è possibile far eseguire con un solo comando la stessa operazione su più valori, facendola eseguire non su uno scalare, ma su un array:

    n = [0 1/6 1/2 5/6 1];
    o = sin(pi*n)
    %voglio calcolare il seno di 0,prigrecosesti,pigrecomezzi,cinquesestipigreco,1
    o =
    0 0.5 1 0.5 0
    %ecco nell'ordine i risultati che mi aspettavo

    Vediamo a cosa ci può servire questo per i nostri scopi:
    abbiamo più volte avuto necessità di calcolare il valore di una funzione sinusoidale con determinate frequenza e ampiezza in un certo intervallo di tempo, per poi poterla elaborare in qualche maniera(sovracampionare, sottocampionare, sottoquantizzare, puslwitmodulare etc etc)
    La prima cosa da stabilire è la frequenza di campionamento. L'inverso di questa è poi il sample-increment, ovvero l'intervallo di tempo che intercorre tra un campionee il successivo. Conoscendo il sample-increment e la durata che vogliamo che abbia il nostro campione possiamo crearci un vettore contenente tutti gli "istanti" in cuivogliamo calcolare il valore della sinusoide. Una volta creato questo array possiamo agevolmente calcolare i valori come abbiamo appena visto, basta decidere la frequenzache deve avere la nostra sinusoide:

    fc=44100; %frequenza di campionamento
    sinc=1/fc; %sample increment
    dur=5 %durata
    T=[0:sinc:dur-sinc];%vettore contenente gli "istanti"
    A=1; %ampiezza
    frq=100 %frequenza della sinusoide
    y=A*sin(2*pi*frq*T);
    length(T) %verifichiamo ora che y è un array della stessa lunghezza(cioè contenente lo stesso numero di elementi) di T
    ans = 220500
    length(y)
    ans = 220500
    - plot(x, y), axis(arg), figure(n), hold

    Per verificare che la nostra sinusoide è effettivamente una sinusoide possiamo rappresentare graficamente i dati ottenuti, utilizzando la funzione"plot(x,y)"."x" è il vettore che contiene i valori delle ascisse a cui corrispondono sulle ordinate i valori di "y".
    In pratica verra plottato un grafico i cui punti avranno le coordinate:
    P1 ( x(1), y(1) )
    P2 ( x(2), y(2) )
    P3 ( x(3), y(3) )
    ecc
    ecc
    ecc
    Ovviamente la lunghezza dei due array dovrà essere la medesima, sennò si verificherà un errore.
    Ad esempio:

    plot(T, y) %notate che dovete avere già calcolato T e y , come abbiamo fatto nel paragrafo precedente

    produrrà il seguente grafico:

    A questo punto uno si spara in bocca oppure riflette sul fatto che 220500 campioni non possono venire rappresentati molto bene sui 1024 pixel del vs schermo.
    A questo punto quindi zoomiamo nella presunta sinusoide con la funzione axis(arg). L'argomento "arg" dev'essere un vettore.I primi due elementi di questo array saranno rispettivamente l'inizio e la fine del range che vogliamo visualizzare sull'asse x. Se aggiungiamo altri due valori,questi saranno l'inizio e la fine del range che vogliamo visualizzare sull'asse y.

    axis([1 1.05])

    Qui possiamo vedere che effettivamente c'è una sinusoide, e che siccome vengono visualizzati 5 periodi in 50 millisecondi, essa avrà un periodo di 10ms, ovvero unafrequenza di 100Hz.

    È possibile decidere il colore e lo stile di un grafico aggiungendo un altro argomento dopo i due array. Questo argomento dovrà essere una stringa(quindi racchiuso tra' ' oppure " ") e contenere alcuni dei seguenti caratteri(ne elenco pochi, una lista completa è nell'help):

    r colore rosso
    g colore verde
    b colore blu (default)
    . un punto in corrispondenza di ogni punto(x(n), y(n))
    * un asterisco in corrispondenza di ogni punto (x(n), y(n))
    - una linea spezzata congiunge i punti (default)

    Esempio:

    plot(T,y,".r")
    axis([1 1.001 0 0.5])

    Avrete notato che ogni volta che chiamate la funzione plot, il grafico viene stampato in Octave in una finestra intitolata "figure 1.0" . Potrebbe essere comodo visualizzare contemporaneamente più grafici. Per fare ciò è necessario utilizzare la funzione figure(n) , dove l'argomento "n" è un numero intero. L'esecuzione di questo comando provoca l'apertura di una nuova finestra di grafico, intitolata "Figure n.0". (di default è figure(1))

    plot(T,y) %questo grafico viene plottato nella figura 1
    figure(2)
    plot(T,y,"*r")%questo grafico viene plottato nella figura 2
    axis([0 0.001]) %questo comando è riferito alla figura attiva , ovvero la "Figure 2.0", e non influenza la "Figure 1.0"
    plot(T,y,".g") %questo viene plottato in figura 2, e cancella quanto vi era stato precedentemente rappresentato
    axis([0.2 0.201])

    Abbiamo visto che ad ogni chiamata della funzione plot, il grafico precedentemente presente nella figura attiva viene cancellato e sostituito con quello nuovo.
    Per plottare più grafici su una stessa figura invece, è necessario utilizzare il comando "hold on".
    Esempio:

    figure(3)
    plot(T,y,"r-")
    hold on
    plot(T,y,"b*")
    axis([1 1.001 0 0.5])
    hold off

    Ricordatevi di digitare "hold off" quando non volete più che il grafico "persista". Consiglio di aggiungere "hold off" alla fine di ogni script in cui si utilizza "hold on"per evitare strani comportamenti quando viene rieseguito.
    In alternativa, è possibile plottare più grafici nella stessa figura utilizzando plot con un maggior numero di argomenti.

    plot(T, y, "r-", T, y, "b*") %questo darà come risultato lo stesso ottenuto poco sopra con i due plot e hold on

    Se non vengono specificate opzioni di formato il grafico della prima coppia di vettori verrà rapresentato in "-b", mentre quello della seconda coppia in "-g"

    plot(T, y, T, y) %questo è abbastanza inutile, in quanto viene plottato prima un grafico blu e poi sopra un altro identico ma verde
    %quindi noi vedremo solo quello verde(perché sta sopra)

    Come detto, la trattazione di plot qui è incompleta, e rimando all'help della funzione per approfondimenti.

    - wavread(filename), wavwrite(y,fc,bps,filename)

    L'espressione wavread richiede l'argomento "filename" che è il nome del file riff/wave che si vuole caricare all'interno di Octave. Valgono le considerazioni fatte sopra a riguardo della home directory di octave ecc. Se avete seguito le mie indicazioni, sarà sufficiente avere il file wave nella vostra comoda directory sul desktop(o dove piùvia aggrada) e digitare

    y = wavread("ooops.wav");

    Così facendo nel vettore y vengono caricati i valori dei campioni contenuti nel file "ooops.wav".Vi consiglio di lavorare con file monofonici, è molto più semplice e non è necessario introdurre le matrici.
    Spesso è comodo il seguente utilizzo di wavread, che permette di assegnare alle variabili y, fc, bps rispettivamente i valori dei campioni, la frequenza di campionamentoe il numero di bit per sample. In questa maniera potremo ad esempio usare fc e bps nei nostri calcoli, qualora ci sia necessario.

    [y,fc,bps] = wavread("ooops.wav");
    plot(y) %notare che specificando un solo vettore tra gli argomenti di plot, esso lo interpreta come vettore di ordinate.
    %come vettore di ascisse utilizza [1:length(y)]
    %notate anche nel grafico che il vettore è già normalizzato tra -1 e 1

    Potremo fare poi su questo vettore di valori tutti i conti che ci interessano, alla fine produrremo probabilmente un vettore, magari chiamato out, di dati elaborati,che vorremo in qualche maniera salvare come un file audio per poterlo ascoltare.
    Con la funzione wavwrite(y,fc,bps,filename) ciò è possibile. "y" dev'essere un vettore contenente i campioni, "fc" è la frequenza di campionamento, "bps" il numero di bit persample con cui si vuole salvare il file, filename è una stringa contenene il nome del file di destinazione.

    wavwrite(out,fc,bps,"risultato.wav")
  •