Queste

Transcript

Queste
Laboratorio di Automatica - a.a. 2006/2007
Prof.ssa Vendittelli Marilena
TESINA SUL CONTROLLO DI UN
MOTORE IN CORRENTE CONTINUA
Gruppo:
Bongiorno Giuseppe
Galasso Fabio
Gratta Gabriele
20 Dicembre 2007
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Grazie all’infinita
pazienza e disponibilità
del personale del
laboratorio di Robotica.
2
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Sommario
Introduzione ........................................................................................................................................ 4
Cenni sul funzionamento di un motore a corrente continua ............................................................ 5
Parte elettrica .................................................................................................................................. 6
Parte meccanica ............................................................................................................................... 7
Controllo di velocità ............................................................................................................................ 9
Codice Matlab della simulazione del controllo di velocità ........................................................... 11
Controllo in posizione ....................................................................................................................... 13
Codice Matlab della simulazione del controllo in posizione ......................................................... 22
Montaggio.......................................................................................................................................... 26
Encoder .......................................................................................................................................... 26
Motor controller ............................................................................................................................. 26
Comunicazione seriale ................................................................................................................... 26
Comunicazione motor-controller ................................................................................................... 26
Pezzi sostituiti ................................................................................................................................ 27
Comando velocità al motore .......................................................................................................... 27
Lettura encoder .............................................................................................................................. 28
Algoritmo per l’implementazione del controllore P.I.D. ............................................................... 32
Codice finale .................................................................................................................................. 34
Schema elettrico e flussi dei dati.................................................................................................... 39
Appendice .......................................................................................................................................... 41
Brevi note su particolari funzioni usate nelle simulazioni Matlab ................................................ 41
3
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Introduzione
Al nostro gruppo è stato assegnato il compito di progettare e realizzare il controllo di velocità di un
motore alimentato in corrente continua. Il lavoro è stato suddiviso in tre fasi:
1. progetto di un controllo di velocità;
2. progetto di un controllo di posizione;
3. realizzazione del controllo di velocità.
Prendendo spunto da un’esercitazione svolta in laboratorio, abbiamo iniziato col progettare un
controllo di velocità di un robot mobile di piccola taglia (Khepera). Dati e specifiche del sistema
sono stati forniti insieme al materiale dell’esercitazione. Una volta completata questa prima fase
siamo passati al progetto di un controllo di posizione di un motore alimentato in corrente continua.
Basandoci sul precedente schema, abbiamo apportato le opportune modifiche per effettuare un
controllo di posizione. Una volta ottenuto il datasheet del nostro motore abbiamo modificato i
parametri del sistema e completato il controllore. Infine, abbiamo assemblato il materiale in
laboratorio, programmato il PIC, realizzato il controllo di velocità di un motore alimentato in
corrente continua.
Ci sono stati consegnati:
• un motoriduttore da 12Vdc – 200 rpm con predisposizione per l’encoder;
• un encoder da 300CPR per effettuare misure sulla posizione e sulla velocità dell’asse del
motore;
• una proto board per PICMicro a 40 pin da 20MHz, versatilissima scheda corredata di tutto
l’essenziale per il funzionamento del PIC, inclusa una porta seriale RS232 e un circuito di
regolazione della tensione con 7805;
• un PIC della Microchip, modello 18F452, da noi programmato per realizzare un controllo di
posizione sul motore suddetto;
• un Pololu dual serial motor controller, che permette anche il controllo di una coppia di motori.
4
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Cenni sul funzionamento di un motore a corrente continua
In linee generali possiamo considerare il “sistema motore” come un insieme di tre componenti:
alimentatore, motore vero e proprio, carico. Come mostrato in figura:
Il motore è a sua volta suddiviso in due parti: una elettrica ed una meccanica. La parte elettrica,
alimentata da un generatore di tensione, è costituita da due spazzole che poggiano su un
commutatore avvolto da un folto numero di spire e libero di girare attorno al proprio asse. Queste
spire sono immerse in un campo magnetico generato da un magnete (se esso è permanente, il campo
sarà costante).
Quando la corrente elettrica, che dal generatore è stata trasmessa al commutatore attraverso le
spazzole, attraversa ogni singola spira immersa nel campo magnetico la forza magnetica (agente
perpendicolarmente sia alla spira che al campo magnetico) produce una coppia che muove il rotore.
5
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Il commutatore inverte la corrente ogni mezzo giro per mantenere costante il verso di rotazione del
rotore.
Il rotore, infine, è collegato all’asse del motore. In questo punto, dunque, la parte elettrica diventa
l’alimentazione della parte meccanica.
Per la costruzione del modello del motore schematizziamo quanto appena descritto.
Parte elettrica
Il circuito elettrico equivalente di un motore in corrente continua a magneti permanenti è
dal quale possiamo ricavare le seguenti relazioni nel tempo
6
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
In cui Ke è la costante di velocità, R la resistenza d’armatura e L l’induttanza d’armatura.
Poi trasformando nel dominio di Laplace ed ipotizzando che I(0)=0 otteniamo
Per un motore in corrente continua vale, inoltre, la seguente relazione
dove Kt è la costante di coppia del motore.
Parte meccanica
Passiamo ora ad analizzare la parte meccanica del motore.
Come si può ben vedere in figura, la parte meccanica del motore è a sua volta suddivisa in tre parti:
motore, riduttore e carico.
La parte meccanica del motore senza carico né riduttore può essere così modellata
che in uno schema a blocchi diventa
7
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
La dinamica del carico, invece, è descritta dalla seguente equazione
E quella del riduttore (supponendo che il rapporto di riduzione sia pari a 1/n) da questa
Unendo le tre componenti ed imponendo
da cui si ricava
otteniamo la seguente relazione
che, sostituita nell’equazione della dinamica del motore, dà come risultato
Notiamo, quindi, che il riduttore riduce l’effetto dei disturbi agenti sul carico
Ponendo
e sostituendolo nell’equazione della dinamica del motore
da cui otteniamo lo schema a blocchi del motore con riduttore e con carico
Una volta realizzato questo schema abbiamo implementato il file Matlab per il controllo di velocità
del motore.
8
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Controllo di velocità
Inizialmente abbiamo realizzato lo schema in figura ed inizializzato le variabili. Una volta terminato
lo schema siamo passati alle specifiche:
1. la sovraelongazione non doveva essere superiore al 20%;
2. il tempo di salita doveva essere inferiore ai 0.3 s.
La prima si traduce in un margine di fase pari a
mf = 60*(1-20*log10(Mr)*0.1)
La seconda specifica, invece, si traduce in una pulsazione di attraversamento pari a
wt = (wtmin+wtmax)/2
dove wtmin e wtmax sono pari
wtmin = B3/2.52
wtmax = B3/1.26
A questo punto abbiamo verificato che il nostro sistema non soddisfacesse già le nostre specifiche e,
come ci aspettavamo, andava ben oltre quanto richiesto.
Abbiamo, dunque, dato vita ad un serie di tentativi con un controllore P.I.D., modificando di volta
in volta i vari parametri delle tre azioni (proporzionale Kp, integrativa Ki, derivativa Kd) per
raggiungere il soddisfacimento delle specifiche.
Primo tentativo
Abbiamo sintetizzato un controllore di tipo P.I., ossia un controllore di tipo P.I.D. con costante
derivativa Kd pari a zero. Inizialmente abbiamo posto
Ki = wt
Kp = 1
ed aggiunto il controllore in serie al sistema che avevamo già implementato. Tracciando il
diagramma di Bode del nuovo sistema ed andandone ad analizzare il margine di fase, abbiamo
notato che le specifiche su questo parametro non venivano rispettate.
Secondo tentativo
Abbiamo, dunque, provato a modificare il valore della costante proporzionale per soddisfare le
specifiche ad anello aperto, ponendo
Kp = 1.5
Le specifiche ad anello aperto erano rispettate, quindi abbiamo chiuso il sistema in controreazione
ed analizzato la risposta all’ingresso a gradino. Qui ci siamo scontrati con una sovraelongazione
superiore alle specifiche richieste, quindi abbiamo pensato di ridurre la pulsazione di
attraversamento.
9
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Terzo tentativo
Per far ciò abbiamo ridotto del 20% la costante integrativa e lasciato costante quella proporzionale
Ki = wt*0.8
Kp = 1.5
Le specifiche ad anello aperto erano ancora rispettate e, chiudendo il sistema in controreazione,
abbiamo riscontrato un abbassamento della sovraelongazione, sebbene non ancora sufficiente, ma
anche un aumento del tempo di salita che non rispetta più le specifiche.
Quarto Tentativo
A questo punto abbiamo pensato che il problema derivasse da un Kp troppo basso, dunque abbiamo
mantenuto il Ki originario ed abbiamo provato ad innalzare ulteriormente il Kp.
Ki = wt
Kp = 2.1
Abbiamo controllato le specifiche ad anello aperto, abbiamo chiuso il sistema in controreazione ed
abbiamo calcolato la risposta all’ingresso a gradino, avendo la seguente risposta:
Step Response
1.4
1.2
Amplitude
1
System: Sys
Time (sec): 0.257
Amplitude: 0.999
0.8
0.6
0.4
0.2
0
0
0.5
1
1.5
Time (sec)
Quindi rientriamo nelle specifiche imposte all’inizio del controllo anche se in quella sulla
sovraelongazione abbiamo un margine veramente minimo. Studiando i vari tentativi però non siamo
riusciti a trovare una soluzione migliore che rispondesse alle due specifiche e in cui non
intervenisse l’azione derivativa.
10
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Codice Matlab della simulazione del controllo di velocità
clear all;
close all;
disp('---Analisi e controllo in velocità di un motore a corrente continua con riduzione
e carico---')
disp(' ')
%Definizione delle costanti
Ke = 0.000093;
%costante di velocità
%rapporto di riduzione
n = 0.04;
L = 0.00003;
%induttanza di armatura
%resistenza di armatura
R = 6;
Kt = 0.00089;
%costante di coppia
Jm = 0.18*10^(-7); %momento d’inerzia rotore
%coefficiente di attrito viscoso rotore
Bm = 0.2*10^(-7);
Jl = 1.2*10^(-3);
%momento d’inerzia carico
Bl = 1.2*10^(-5);
%coefficiente di attrito viscoso carico
Jeff = Jm + n^2*Jl;
Beff = Bm + n^2*Bl;
%carico di disturbo
td = 0;
r = 0.0078;
%raggio di una ruota
d = 0.0545;
%distanza tra le due ruote
%Costruzione del modello con le funzioni di trasferimento
disp('---Modello---')
num = Kt*n;
den = [L*Jeff, (L*Beff+R*Jeff), (R*Beff+Ke*Kt)];
disp('Funzione di trasferimento del motore:')
mot = tf(num, den)
margin(mot); grid on;
[Gm, Pm, Wg, Wp] = margin(mot);
pause
%Cambio delle specifiche nel tempo ad anello aperto
disp('---Specifiche---')
disp('Specifica sulla sovraelongazione:')
s = 0.2
Mr = (1+s)/0.85;
disp('Traduzione sul margine di fase:')
mf = 60*(1-20*log10(Mr)*0.1)
pause
disp('Specifica sul tempo di salita:')
ts = 0.3
B3 = 3/ts;
wtmin = B3/2.52;
wtmax = B3/1.26;
disp('Traduzione sulla pulsazione di attraversamento:')
wt = (wtmin+wtmax)/2
pause
[mag, phase] = bode(mot, wt);
disp('In corrispondenza della pulsazione desiderata ho un margine di guadagno di:')
magdb = 20*log10(mag)
disp('e un margine di fase di:')
mphase = phase+180
pause
%Sintesi del controllore
disp('---Controllore---')
disp('---Primo tentativo---')
disp('Proviamo con un controllore P.I.')
Ki = wt
Kp = 1
Kd = 0
11
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
G = tf([Kp, Ki], [1 0])
motore = series(G, mot);
margin(motore); grid on;
pause
disp('Non rispetto le specifiche sul margine di fase.')
pause
disp('---Secondo tentativo---')
disp('Per soddisfare le specifiche ad anello aperto aumento l''azione proporzionale.')
Ki = wt
Kp = 1.5
G = tf([Kp, Ki], [1 0])
motore = series(G, mot);
margin(motore); grid on;
pause
Sys = feedback(motore, 1);
disp('La riposta al gradino del sistema chiuso in controreazione è:')
step(Sys); grid on;
pause
disp('Ho la sovraelongazione troppo alta quindi provo a ridurre la pulsazione di
attraversamento.')
pause
disp('---Terzo tentativo---')
disp('Proviamo con un Ki diminuito del 20%.')
Ki = wt*0.8
Kp = 1.5
G = tf([Kp, Ki], [1 0])
motore = series(G, mot);
margin(motore); grid on;
pause
Sys = feedback(motore, 1);
disp('La riposta al gradino del sistema chiuso in controreazione è:')
step(Sys); grid on;
pause
disp('Riducendo il Ki ho prodotto un''abbassamento della sovraelongazione (non ancora
sufficiente)')
disp('ed un''aumento del tempo di salita che così va oltre le specifiche.')
pause
disp('---Quarto tentativo---')
disp('Proviamo allora aumentando ulteriormente il Kp invece di modificare il Ki.')
Ki = wt
Kp = 2.1
G = tf([Kp, Ki], [1 0])
motore = series(G, mot);
margin(motore); grid on;
pause
Sys = feedback(motore, 1);
disp('La riposta al gradino del sistema chiuso in controreazione è:')
step(Sys); grid on;
pause
disp('Ora entrambe le richieste sono esaudite quindi ho concluso.')
12
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Controllo in posizione
Terminato il controllo del motore in velocità, il passo successivo è stato andare avanti con un altro
tipo di controllo, quello in posizione. Rispetto al modello sintetizzato prima abbiamo solamente
dovuto aggiungere in serie un integratore, cioè una funzione di trasferimento
, prima della
controreazione di controllo. Tutto questo perché velocità e posizione sono legate dalla relazione:
.
Per prima cosa abbiamo analizzato il diagramma di Bode del modello così ottenuto e rilevato i
parametri caratteristici:
− pulsazione di attraversamento = 1,7496 rad/s
− margine di fase = 53’ 41,64’’
− pulsazione in corrispondenza della fase a -180° = 73,9530 rad/s
− guadagno in corrispondenza della fase a -180° = -65,0391 dB
Per sintetizzare il controllore P.I.D. abbiamo scelto di usare il secondo metodo di Ziegler-Nichols
che prevede di portare il sistema al limite di stabilità per poi configurare dei parametri standard per
le varie configurazioni delle tre azioni. Aumentato il guadagno del sistema, siamo arrivati fino
all’instaurazione di un’oscillazione periodica alla risposta a un gradino unitario, caratterizzata da un
periodo pari a 0,0850 secondi.
13
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Ora prendiamo in considerazioni i valori di Kp, Ki e Kd consigliati dalle tabelle per le
configurazioni di P., P.I. e P.I.D..
Utilizzando solo l’azione proporzionale otteniamo una risposta che è ancora caratterizzata da
un’oscillazione molto elevata, questo per via della conformazione della fase del nostro sistema che
essendo quasi piatta nella nostra zona d’interesse non consente di aumentare il margine di fase
diminuendo la pulsazione di attraversamento rispetto al caso limite.
Aggiungendo l’azione integrativa non otteniamo benefici dall’effetto “memoria” (un’azione
integrativa permette al sistema di “ricordare” ciò che è successo negli istanti passati) ma anzi non
riusciamo più a rendere il sistema stabile visto che otteniamo una fase sempre inferiore a -180°.
14
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Con tutte e tre le azioni combinate invece otteniamo una risposta quasi accettabile visto che la
sovraelongazione è troppo alta (circa del 70%) ma il tempo di salita è più che ottimo (0,0168 s).
Quando usiamo anche l’azione derivativa, dobbiamo però ricordarci che il controllore P.I.D. venuto
così a formarsi non costituisce più un sistema fisicamente realizzabile perché abbiamo che il grado
del numeratore è maggiore del grado del denominatore:
Analizzando i tre casi studiati prima capiamo che oltre al rischio di portare il sistema al limite di
stabilità il secondo metodo di Ziegler-Nichols non fornisce dei valori validi per ogni caso ma dà più
che altro delle indicazioni generiche per configurare il controllore secondo le azioni che si vogliano
usare. Questi parametri andranno poi regolati da caso a caso.
Per tornare a valori delle specifiche accettabili, per esempio una sovraelongazione non maggiore del
20%, abbiamo provato ad aumentare l’azione derivativa ma per rientrare nei limiti imposti serviva
un incremento troppo elevato, addirittura siamo dovuti arrivare al 350% del valore indicato dalle
tabelle. Siccome nel nostro caso l’azione integrativa dava problemi rendendo il sistema instabile
abbiamo deciso di toglierla e ottimizzare il controllore con le sole azioni proporzionale e derivativa.
Procedendo per tentativi abbiamo trovato che per rientrare in una sovraelongazione minore del 20%
il Kd doveva sempre essere molto diverso da quello consigliato (questa volta “solo” 250%).
Abbiamo così dimostrato definitivamente che per il nostro sistema il secondo metodo di ZieglerNichols non forniva un settaggio atto a dare una risposta sufficientemente accettabile.
15
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Per completare l’analisi dovevamo solo definire la realizzabilità del controllore. Per non modificare
troppo la risposta che c’eravamo costruiti nelle precedenti simulazioni, abbiamo verificato il
disturbo apportato dall’aggiunta nel controllore di un “polo lontano” con valori via via crescenti. Il
minimo valore che rende indistinguibili le due risposte, quella realizzabile e quella non, è -10000,
quindi abbiamo scelto di sintetizzare un controllore con un P.D. e una funzione di trasferimento:
Una volta conclusa tutta la sintetizzazione in Matlab siamo passati all’analisi del modello in
Simulink:
dove possiamo controllare più facilmente l’effetto di saturazioni sullo sforzo di carico, che dal
parametro sul datasheet dal quale abbiamo ricavato i valori delle costanti per le simulazioni non
deve superare gli 0,1 mNm. Con il lavoro fin qui svolto il carico evolve con un comportamento
quasi impulsivo: la sua azione termina già dopo circa 0,1 s, però abbiamo un picco che raggiunge il
valore di ~1,4Nm per un tempo minore di 0,1 ms e una coda intorno agli 0,5⋅10-2 Nm, valori troppo
elevati rispetto al massimo sopportabile dal nostro sistema, quindi assolutamente inaccettabile.
16
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Abbiamo allora provato a diminuire i valori dei due parametri del P.D. per cercare di ridurre lo
sforzo. Diminuendo di molto l’azione proporzionale, portandola allo 0,075% della precedente,
abbiamo un significativo miglioramento della sovraengolazione e un peggioramento del tempo di
salita che comunque resta più che ottimo (~0,0225 s).
Questo perché il diagramma di Bode è caratterizzato da un picco di fase a ~ -90° intorno alle nostre
frequenze di lavoro e diminuendo il guadagno ci siamo posizionati esattamente nel punto centrale
del picco.
Diminuendo anche l’azione derivativa del 70% otteniamo un peggioramento significativo delle
caratteristiche della risposta, ma siccome partivamo da valori ottimi, arriviamo a specifiche
accettabili: sovraelongazione minore del 20% e tempo di salita di poco superiore ai 50 ms.
17
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Anche se abbiamo peggiorato le caratteristiche, con un Kd più basso però riusciamo a ridurre di
molto il valore dello sforzo di carico. Il valore massimo è passato da 1,4 a 0,425 Nm mentre la
durata è salita a 0,4 s ma con valori compresi sotto i 5 mNm.
Tutto questo lavoro non ci ha portato a valori sufficientemente vicini ai limiti imposti dal modello,
essendo superiori di ben tre ordini di grandezza nel valore di picco. Non possiamo allora trascurare
gli effetti di azioni non lineari quali la saturazione dello sforzo di carico. Analizzando la risposta in
Simulink del sistema vediamo che diventa assolutamente inaccettabile e quindi dovremmo cercare
di migliorare ancora il controllore
18
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Tornando in Matlab abbiamo eseguito nuovi tentativi per arrivare a valori piccoli dei due parametri
senza compromettere troppo la bontà della risposta: da una coppia Kp=80,3857 Kd=8,5372 siamo
passati a un’altra Kp*=0,0161 Kd*=0,7114.
I valori sono diminuiti di molto e la risposta peggiorata fino ad arrivare a un tempo di salita pari a
1,02 s e tempo di assestamento di 1,84 s. Pensando però a possibili applicazioni del nostro studio,
come per esempio un braccio meccanico che deve raggiungere una fissata posizione, il tempo di
salita intorno al secondo e quello di assestamento intorno ai due sono ancora accettabili, anzi un
sistema estremamente veloce potrebbe addirittura creare altri problemi meccanici non studiati nel
nostro caso. La scomparsa della sovraelongazione è anch’essa un ulteriore miglioramento del
sistema visto che il fatto di non superare il valore di riferimento durante il movimento del corpo
potrebbe per esempio evitare l’urto con vincoli presenti non ipotizzati nel percorso seguito partendo
dal punto iniziale.
19
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Tornando allo studio del carico in Simulink vediamo che siamo riusciti a ridurre il tutto di un ordine
di grandezza e a rendere l’azione molto rapida, istantanea.
Notiamo però un effetto strano nell’uscita con la saturazione: il sistema si comporta molto
diversamente da quanto ci aspettavamo perché notiamo che abbiamo un’uscita di un sistema non
stabile.
Non pensiamo sia un effetto della non linearità, per verifica controlliamo anche l’uscita senza
saturazione e vediamo che anch’essa non risulta uguale alla corrispondente simulata in Matlab.
Controlliamo i parametri e verifichiamo che è tutto in ordine. L’unico problema potrebbe sorgere
dal metodo d’integrazione e imputiamo a esso l’ambiguità, anche se diversità così marcate sono
difficili da incontrare.
20
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Per contro allora riformuliamo la sintesi del nostro controllore ma usando come sistema per
disegnare l’uscita Simulink e il suo sistema d’integrazione.
Procedendo per tentativi arriviamo a un compromesso in cui la risposta in Simulink è simile a
quella ottenuta in Matlab (anche se con una leggera sovraelongazione del ~2%) ma con un
andamento temporale nettamente più lento infatti troviamo un tempo di salita pari a 5,28 s e
assestamento inferiore ai 7 s. Certamente non sono i migliori valori possibili ma ritornando
all’esempio di applicazione pensato prima potrebbero bastare.
Tornando in Matlab per vedere le differenze tra i due metodi d’integrazione troviamo una risposta
sempre molto “lenta” (tempo di salita ~1,3 s, tempo di assestamento 5,75 s) ma comunque
accettabile.
Scegliamo quindi questa seconda configurazione del controllore P.I.D. con parametri:
− Kp = 0,2400
− Ki = 0
− Kd = 0,3971
21
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Codice Matlab della simulazione del controllo in posizione
%Laboratorio di Automatica a.a. 2006/2007
%Tesina sul motore controllato in posizione
%Gruppo formato da: Bongiorno Giuseppe, Galasso Fabio, Gratta Gabriele
clear all;
close all;
clc
disp('---Analisi e controllo in posizione di un motore a corrente continua con riduzione
e carico---')
disp(' ')
%Definizione delle costanti
%costante di velocità
Ke = 0.000093;
n = 0.04;
%rapporto di riduzione
L = 0.00003;
%induttanza di armatura
%resistenza di armatura
R = 6;
Kt = 0.00089;
%costante di coppia
Jm = 0.18*10^(-7); %momento d’inerzia rotore
%coefficiente di attrito viscoso rotore
Bm = 0.2*10^(-7);
Jl = 1.2*10^(-3);
%momento d’inerzia carico
%coefficiente di attrito viscoso carico
Bl = 1.2*10^(-5);
Jeff = Jm + n^2*Jl;
Beff = Bm + n^2*Bl;
td = 0;
%carico di disturbo
%Costruzione del modello con le funzioni di trasferimento
disp('---Modello---')
num = Kt*n;
den = [L*Jeff, (L*Beff+R*Jeff), (R*Beff+Ke*Kt)];
disp('Funzione di trasferimento del motore:')
mot = series(tf(num, den), tf(1, [1 0]))
scrsz = get(0,'ScreenSize');
figure('Position',[scrsz(3)/2, scrsz(4)/2, scrsz(3)/2, scrsz(4)/2])
margin(mot)
axis([1e-1, 1e+7, -350, -100, -271, -179])
title('Diagramma di Bode del motore senza controllo')
[Gm, Pm, Wg, Wp] = margin(mot);
pause
disp('La pulsazione di attraversamento è:')
Wp
disp('e in corrispondenza ho un margine di fase di:')
Pm
pause
disp('La pulsazione alla quale ho la fase a -180° è:')
Wg
disp('ed ho un guadagno di:')
Gm
pause
%Sintesi del controllore
disp(' ')
disp('---Sintesi del controllore---')
disp('Proviamo a costruire un controllore P.I.D. con il secondo metodo di ZieglerNichols.')
disp('Devo portare il sistema al limite di stabilità per instaurare un''oscillazione
permanente')
disp('allora moltiplico il sistema per una guadagno:')
Kpl = Gm
pause
disp('La funzione di trasferimento ad anello aperto quindi diventa:')
Lim = series(Kpl, mot)
disp('Il diagramma di Bode del motore al limite di stabilità è:')
margin(Lim)
22
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
axis([1e-1, 1e+7, -350, -100, -271, -179])
title('Diagramma di Bode a ciclo aperto del motore al limite di stabilità')
pause
disp('Quindi chiudendo in controreazione abbiamo la risposta in figura')
step(feedback(Lim, 1))
axis([-0.1, 1.5, -0.1, 2.1])
title('Risposta al gradino del motore controreazionato al limite di stabilità')
disp('caratterizzata da un periodo di oscillazione pari a:')
Tl = 2*pi/Wg
pause
disp(' ')
disp('---Primo caso---')
disp('Usando un controllore P.I.D. con solamente l''azione proporzionale avremmo:')
Kp = Kpl*0.5
Ki = 0
Kd = 0
pause
disp('Il diagramma di Bode è:')
margin(series(Kpl, mot))
axis([1e-1, 1e+7, -350, -100, -271, -179])
title('Diagramma di Bode ad anello aperto solo con azione proporzionale')
pause
disp('La risposta al gradino del motore diventa:')
step(feedback(series(Kpl, mot), 1))
axis([-0.1, 1.5, -0.1, 2.1])
title('Risposta al gradino con controllore P.')
pause
disp('Per la conformazione della fase solo un''azione proporzionale non basta.')
pause
disp(' ')
disp('---Secondo caso---')
disp('Usando un controllore P.I.D. con le azioni proporzionale ed integrativa abbiamo:')
Kp = 0.45*Kpl
Ki = Kp/(0.85*Tl)
Kd = 0
G = tf([Kp, Ki], [1 0])
pause
disp('Il diagramma di Bode è:')
bode(series(G, mot)); grid on; title('Diagramma di Bode ad anello aperto solo con
controllore P.I.')
pause
disp('Anche con le due azioni combinate non riesco a trovare la stabilità,')
disp('infatti la risposta al gradino del motore è:')
step(feedback(series(G, mot), 1)); grid on; title('Risposta al gradino con controllore
P.I.')
pause
disp(' ')
disp('---Terzo caso---')
disp('Il controllore P.I.D. questa volta avrà come parametri:')
Kp = 0.6*Kpl
Ki = Kp/(0.5*Tl)
Kd = Kp*0.125*Tl
G = tf([Kd, Kp, Ki], [1 0])
disp('Che però non costituiscono un sistema fisicamente realizzabile.')
pause
disp('Il diagramma di Bode diventa:')
margin(series(G, mot)); title('Diagramma di Bode ad anello aperto con controllore
P.I.D.')
pause
disp('La risposta al gradino del motore è quella in figura,')
step(feedback(series(G, mot), 1)); grid on; title('Risposta al gradino con controllore
P.I.D.')
23
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
disp('caratterizzata da una sovraelongazione troppo elevata (~70%).')
pause
disp(' ')
disp('---Quarto caso---')
disp('Proviamo ad aumentare l''azione derivativa (Kd=3.5*Kd):')
Kp = 0.6*Kpl
Ki = Kp/(0.5*Tl)
Kd = Kp*0.125*Tl*3.5
G = tf([Kd, Kp, Ki], [1 0])
disp('Ricordandoci sempre che però non costituiscono un sistema fisicamente
realizzabile.')
pause
disp('Il diagramma di Bode diventa:')
margin(series(G, mot)); title('Diagramma di Bode ad anello aperto con controllore
P.I.D.(2)')
pause
disp('La risposta al gradino del motore è quella in figura,')
step(feedback(series(G, mot), 1)); grid on; title('Risposta al gradino con controllore
P.I.D.(2)')
disp('in cui siamo riusciti a diminuire la sovraelongazione.')
pause
disp(' ')
disp('---Quinto caso---')
disp('Proviamo ad eliminare l''azione integrativa:')
Kp = 0.6*Kpl
Ki = 0
Kd = Kp*0.125*Tl*2.5
G = tf([Kd, Kp], 1)
disp('Sistema non fisicamente realizzabile.')
disp('Il diagramma di Bode è:')
margin(series(G, mot)); title('Diagramma di Bode ad anello aperto con controllore P.D.')
pause
disp('La risposta al gradino del motore è quella in figura,')
step(feedback(series(G, mot), 1)); grid on; title('Risposta al gradino con controllore
P.D.')
disp('in cui non abbiamo modificato troppo la sovraelongazione.')
pause
%Realizzabilità del controllore
disp(' ')
disp('Cercando di far diventare il controllore realizzabile potremmo aggiungere un polo
"lontano":')
G = series(G, tf(1, [1e-4 1]))
disp('Il diagramma di Bode è:')
margin(series(G, mot)); title('Diagramma di Bode ad anello aperto con controllore P.D.
realizzabile')
pause
disp('La risposta al gradino:')
step(feedback((series(G, mot)), 1)); grid on; title('Risposta al gradino con controllore
P.D. realizzabile')
disp('Il minimo valore per non modificare troppo il nostro sistema è -10000.')
pause
%Controllo del carico massimo applicabile al motore
disp(' ')
disp('Siccome abbiamo un ottimo tempo di salita, possiamo provare a diminuire il Kp per
ridurre lo sforzo:')
Kp = 0.075*Kp
G = tf([Kd, Kp], [1e-4 1])
disp('Il diagramma di Bode diventa:')
margin(series(G, mot)); title('Diagramma di Bode ad anello aperto con controllore P.D.
per carico minimo')
pause
disp('La risposta al gradino:')
step(feedback((series(G, mot)), 1)); grid on; title('Risposta al gradino con controllore
24
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
P.D. per carico minimo')
disp('Diminuendo l''azione proporzionale abbiamo un abbassamento significativo della
sovraelongazione')
disp('e un peggioramento del tempo di salita che resta comunque più che ottimo (~0.0225
s).')
pause
disp(' ')
disp('Proviamo anche a ridurre l''azione derivativa:')
Kd = 0.3*Kd
G = tf([Kd, Kp], [1e-4 1])
disp('Il diagramma di Bode diventa:')
margin(series(G, mot)); title('Diagramma di Bode ad anello aperto con controllore P.D.
per carico minimo(2)')
pause
disp('La risposta al gradino:')
step(feedback((series(G, mot)), 1)); grid on; title('Risposta al gradino con controllore
P.D. per carico minimo(2)')
disp('Diminuendo il Kd notiamo un peggioramento della risposta del sistema che rimane
nei parametri')
disp('riuscendo però a ridurre notevolmente il carico dell''azione di controllo,')
disp('che è ancora molto al di fuori dei limiti imposti dal sistema (0.13 invece di 1e4).')
pause
disp(' ')
disp('Analizzando in simulink la risposta del sistema con la saturazione del carico')
disp('osserviamo che la risposta al gradino non è assolutamente accettabile.')
pause
%Miglioramento con Simulink
disp(' ')
disp('Proviamo allora a ridurre ulteriormente le azioni del P.D.')
disp('senza arrivare a specifiche non accettabili.')
Kp = (1/335)*Kp
Kd = (1/21.5)*Kd
G = tf([Kd, Kp], [1e-4 1])
disp('I valori delle due azioni sono molto piccoli ora.')
pause
disp('Il diagramma di Bode è:')
margin(series(G, mot)); title('Diagramma di Bode ad anello aperto con controllore P.D.
per carico minimo(4)')
pause
disp('Risposta al gradino:')
step(feedback((series(G, mot)), 1)); grid on; title('Risposta al gradino con controllore
P.D. per carico minimo(4)')
disp('Ora il sistema ha una sovraelongazione del 20%, un tempo di salita di 1 s')
disp('e di assestamento di 5.76 s.')
pause
disp(' ')
disp('Analizzando in simulink il modello così creato ed inserendo l''effetto di
saturazione')
disp('troviamo che il sistema non viene alterato di molto.')
close all;
25
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Montaggio
Encoder
Per prima cosa abbiamo montato l’encoder al motoriduttore. Abbiamo incollato il bi-adesivo sul
motoriduttore, dalla parte opposta a quella dove va applicato il carico, e sulla base dell’encoder.
Poi abbiamo inserito il disco forato, chiuso il tutto col coperchio e controllato che il perno del
motoriduttore, su cui è stato montato l’encoder, non toccasse quest’ultimo e non causasse errori
nella lettura.
Come prova abbiamo collegato il motoriduttore all’alimentatore, regolato a 5 volt, e l’encoder
all’oscilloscopio ottenendo come uscita una buona onda quadra.
Motor controller
Come secondo passo abbiamo saldato al PCB del controllore il microcontroller e il dual H-bridge,
operazione che si è rivelata più complicata del solito ma che siamo riusciti a portare a termine con
buoni risultati.
Comunicazione seriale
Una volta saldati i componenti sulla scheda la prima prova che abbiamo fatto è stata quella di
comunicazione tra computer e microprocessore. Per una più facile programmazione, abbiamo prima
caricato sulla memoria del pic il bootloader utilizzando Tinybld 18 e la scheda con comunicazione
parallela presente in laboratorio. Così, di volta in volta, per caricare un nuovo programma sul
microprocessore bastava collegarlo con la seriale ed utilizzare il programma Epic, per scriverlo in
memoria, ed HyperTerminal, per visualizzare su schermo.
Il primo programma che abbiamo utilizzato è stato una semplice prova sia dei comandi in codice C
sia dei settaggi in ambiente Windows. Abbiamo superato facilmente questo semplice test.
Comunicazione motor-controller
A questo punto abbiamo collegato i fili necessari per il motor-controller (alimentazione esterna a 12
V, uscita per l’alimentazione al motore, canale di reset, canale seriale) e scritto il programma adatto
al suo funzionamento. Con questo programma non abbiamo avuto nessuna risposta dal motor
controller. Abbiamo ipotizzato che il problema potesse derivare da due cause: implementazione
fisica dei collegamenti; errore nella logica del software.
Le prime verifiche sono state fatte sui fili perché montati in maniera “volante” sullo zoccolo del
motor-controller. Assicurata la stabilità della connessione e la non interferenza tra l’uno e l’altro,
ulteriori tentativi hanno dato esito negativo.
Siamo, dunque, passati alla verifica del codice. Il datasheet del motor-controller afferma che per il
funzionamento dello stesso è necessario inviare una sequenza di quattro bytes così composti:
1.
2.
3.
4.
Byte di start della comunicazione (l’unico con il bit più significativo settato ad 1).
Byte di scelta del dispositivo al quale inviare il comando (tutti 0 per il motor controller).
Byte di scelta del motore (in caso di controllo di più motori) e della direzione.
Byte di scelta della velocità del motore (dal momento che il bit più significativo deve essere
settato a 0, con i restanti 7 bit possiamo comandare un range che va da 0 a 27-1=127).
Le prove effettuate inviando questi bytes non hanno abilitato l’uscita del motor controller.
Abbiamo, dunque, ipotizzato che il problema potesse derivare dal tipo di comando utilizzato per
l’invio della sequenza descritta. Provando vari comandi (printf, putc, …) e tipi di dato che
rappresentino la stessa sequenza di bit, non abbiamo risolto il problema.
26
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Pezzi sostituiti
Trovando difficoltà nel lavorare col motor controller, abbiamo deciso di sostituirlo con dei pezzi
equivalenti. Il lavoro che doveva essere svolto dal microcontrollore PIC12F629 lo abbiamo
implementato con il nostro PIC, ed abbiamo sostituito l’H-Bridge presente con un L293D,
ipotizzando che potesse essersi danneggiato il pezzo del motor controller.
L’H-Bridge comprato è caratterizzato dalle seguenti porte:
Schema dei piedini dell’H-bridge L293D
L’alimentazione del componente (a 12 V per poter alimentare correttamente il motore) è collegata
al piedino Vs (8), da Vss viene prelevata la tensione di confronto per i due input, le quattro Ground
(4,5,12,13) sono messe a massa. Il PWM entra dal piedino Enable 1. In uscita avremo i seguenti
casi:
• se i due Input (piedini 2 e 7) sono entrambi alti o bassi, dai due Output (piedini 3 e 6) uscirà la
stessa tensione (12 V se alti ed ipotizzando il PWM al massimo, 0 V se bassi). Essendo
entrambi gli Output collegati all’alimentazione del motore, in questo caso la differenza di
potenziale vista dal motore è nulla e quindi non gira;
• se Input 1 è alto ed Input 2 è basso, Output 1 darà in uscita una tensione di 12 V mentre
Output 2 sarà ancora a massa, quindi il motore sarà attivo ed avrà un senso di rotazione;
• se Input 2 è alto ed Input 1 è basso, Output 2 darà in uscita una tensione di 12 V mentre
Output 1 sarà ancora a massa, quindi il motore sarà attivo ed avrà l’altro senso di rotazione.
Comando velocità al motore
Un primo programma di prova è stato l’invio di una velocità desiderata al motore, per controllare il
corretto collegamento dei nuovi componenti.
#include
#use
#fuses
#use
#include
<18F452.h>
delay(clock=20000000)
HS, NOWDT, NOPROTECT, NOLVP
RS232(baud=9600, xmit=pin_c6, rcv=pin_c7, errors)
<stdlib.h>
/*---DEFINIZIONE DELLE VARIABILI---*/
unsigned int dci = 0;
//duty-cicle
/*---FUNZIONI---*/
#int_RTCC
RTCC_isr() { }
27
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
/*---INIZIO MAIN---*/
void main() {
//setup dei timer
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_2);
setup_timer_2(T2_DIV_BY_1, 199, 1);
//settaggio del CCP1 in PWM mode
setup_ccp1(CCP_PWM);
//inserimento del duty-cicle
getc();
printf("Benvenuto nel PIC18F452!\n\r\n\r");
printf("Prova del motore al massimo della velocità.\r\n");
dci = 1023;
//abilitazione degli interrupts
enable_interrupts(INT_RTCC);
enable_interrupts(global);
//invio velocita' all'h-bridge
output_high(PIN_A0);
output_low(PIN_B0);
output_high(PIN_B1);
set_pwm1_duty(dci);
delay_ms(10000);
}
//led OFF
output_low(PIN_A0);
//frenata motore
set_pwm1_duty(0);
}
/*---FINE MAIN---*/
Il motore con questo codice girava, quindi abbiamo verificato la validità del nuovo metodo.
Dovevamo però attuare alcune correzioni:
1. nella frenata del motore questo cominciava a rallentare ma alla fine del programma riprendeva
a girare al massimo. Abbiamo, dunque, aggiunto una frenata graduale;
2. abbiamo permesso all’utente di scegliere la velocità e la direzione immettendole da tastiera;
3. abbiamo aggiunto uno spunto iniziale al motore, perché abbiamo notato che a velocità troppo
basse il motore non riusciva a vincere l’inerzia iniziale.
Lettura encoder
Il passo successivo è stato leggere i dati dall’encoder. Come prima soluzione abbiamo pensato ad
implementare un ciclo che leggesse l’uscita dell’encoder ed incrementasse una variabile ad ogni
valore alto del segnale. Facendo prove a varie velocità, la differenza tra impulsi letti a velocità
massima e minima è solamente di 7 impulsi. Questo ci ha messo in allarme e ci ha portato a pensare
che il segnale dell’encoder fosse troppo veloce per il nostro metodo di lettura.
Leggendo le caratteristiche del nostro PIC, abbiamo trovato che il Timer 1 poteva essere settato
come contatore su ogni fronte di salita di un segnale in ingresso. Provando un nuovo codice che
sfruttasse questa potenzialità siamo arrivati a risultati migliori.
28
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Impulsi letti per velocità da 100 a 1000
100
200
300
400
0,18
0,53
1,20
5,23
12
19
30
78
0
0
0
0
Velocità
500
600
700
800
900
1000
1023
Media della misura
38,57 188,28 368,45 542,73 608,72 609,13 610,26
Massimo della misura
127
233
373
615
701
611
612
Minimo della misura
20
18
128
540
607
446
598
Come mostrano i grafici, a parte una prima fase di assestamento, che può coincidere con qualche
residuo dello spunto iniziale dato al motore, le misure si mantengono costanti su un determinato
livello. Le piccole oscillazioni che vediamo sono frutto dei vari rumori: l’encoder che può essere
arrivato a fare mezzo giro in più, errori nelle prime misure (all’inizio è quasi sempre presente un
valore anomalo), buchi nella comunicazione seriale (il picco negativo nella quinta figura).
Una volta fatta la media, l’unione dei dati avrebbe dovuto portare ad una curva assomigliante molto
ad una retta, cioè che mostrasse un rapporto lineare tra aumento della velocità espressa in dutycicle
(quindi di tensione data al motore) e numero di impulsi letti nell’unità di tempo. Invece abbiamo
notato che ciò avviene solamente in una piccola zona, esattamente tra 400 e 700, come mostrato in
figura.
29
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Rapporto tra velocità in dutycicle inviata al motore e numero di impulsi letti
Per evitare ulteriori complicazioni, abbiamo deciso di restringere il campo di variazione della
velocità da far scegliere all’utente limitandola ad una sola escursione tra 300 e 800, zona in cui ad
un aumento del voltaggio corrisponde un preciso aumento lineare delle letture.
Scelta l’escursione dei valori, il problema successivo è stato trovare una relazione che permettesse
di passare dal numero di impulsi contati dall’encoder ad un valore corrispondente di velocità.
Questo serve nell’implementazione dell’algoritmo di controllo a permettere la chiusura dell’anello
di controreazione: gli impulsi sono la misura dello stato del nostro sistema che dovrà essere poi
confrontata con la velocità desiderata. Per permettere il confronto tra le due diverse grandezze
abbiamo pensato ad una interpolazione dei dati trovati sperimentalmente. Usando la funzioni
predefinita di Matlab polyfit abbiamo calcolato delle curve interpolanti di vario grado, fino al terzo,
che minimizzassero l’errore quadratico medio.
Curve interpolanti il rapporto impulsi/velocità
La curva di grado primo ci mostra quanto l’evoluzione del nostro sistema si discosta dal caso ideale.
30
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Tra i vari polinomi calcolati dalla funzione di Matlab, il miglior compromesso tra complessità di
calcolo della curva interpolata e precisione dei risultati ci è sembrato essere la funzione di terzo
grado. Una successiva implementazione sui nostri componenti ci ha mostrato come questa scelta
desse problemi, perché i risultati sono non veritieri:
Media letture encoder
PIC
Matlab
102
102.8800
539
539.2000
582
582.9800
567
567.2000
557
557.8400
579
579.9600
579
579.3400
503
503.2400
537
537.6600
Calcolo velocità relativa
PIC
Matlab
443
449.0880
1141
697.4426
1297
749.7598
1049
729.9920
1083
717.7388
1308
745.6692
1308
745.6692
948
663.0328
1147
695.3214
Dai dati riportati in tabella si vede che l’implementazione del codice per la media delle misure
risulta corretto, mentre la parte relativa al calcolo del polinomio di terzo grado risulta errata ma
mantiene l’andamento corretto. Per risolvere l’inconveniente abbiamo attuato due rimedi: usare per
le variabili nel PIC un tipo di dato uniforme e più preciso e cambiare il polinomio di terzo grado
con uno di primo.
Il voler usare un polinomio di primo grado e il minimizzare l’errore nel passaggio da impulsi a
velocità hanno trovato soluzione comune nel dividere l’intera zona di valori della funzione in tre
parti.
Confronto delle due interpolazioni nei due sensi di marcia
31
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Algoritmo per l’implementazione del controllore P.I.D.
L’ultimo passo è stato quello di unire tutte le parti precedenti nel progetto complessivo, cioè il
controllo in velocità del motore.
L’ultimo blocco che rimane da implementare e l’algoritmo del PID che calcola la velocità da
comandare al motore in base all’errore rilevato tra velocità desiderata e velocità attuale letta. Nelle
simulazioni del nostro progetto del controllo in velocità, abbiamo soddisfatto le specifiche senza
azione derivativa quindi il P.I.D. sarà solamente un P.I..
Il contributo dell’azione proporzionale è di banale logica perché è dato dalla semplice differenza tra
due valori. L’azione integrale invece è più complessa e si può ricorrere all’analisi numerica per il
calcolo del valore, per esempio usando il metodo di Eulero o di Runge-Kutta. Per non appesantire
troppo tutto il nostro programma abbiamo deciso di usare una soluzione molto approssimativa, cioè
calcolare l’integrale dell’errore come l’area dei rettangoli ottenuti dal segnale dello stesso
campionato e tenuto. Così facendo otteniamo sicuramente grandi scostamenti tra l’integrale
calcolato e l’integrale reale che portano ad allungamenti del tempo di assestamento. Considerate
però le finalità del nostro progetto e anche il resto dell’implementazione, certamente non ottimale,
dei tempi maggiori potevano aiutare a capire come realmente stesse evolvendo la situazione.
Il calcolo dell’azione del controllore viene così sintetizzato nell’espressione:
corr = Kp*(v_des–v_letta) + Ki*(integrale+step*err)
-
corr = correzione alla velocità attuale,
v_des = velocità desiderata,
v_letta = velocità letta da encoder,
step = passo dell’integrazione numerica,
err = errore del passo precedente.
La prima prova di un codice con questa logica ha portato a risultati non di certo soddisfacenti.
v_des = 550, Kp = 0.21, Ki = 0.59524; in verde la velocità letta dall’encoder, in rosso quella desiderata
32
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Usando gli stessi parametri calcolati dalla simulazione nel caso ideale rendono instabile il sistema:
quando in un primo momento le oscillazioni intorno al valore desiderato si mantengono contenute la
situazione è ancora accettabile, ma appena l’errore comincia a crescere il valore dell’integrale
influisce troppo e la risposta diverge.
Per verificare questo problema abbiamo fatto un’ulteriore prova con gli stessi parametri ma con
un’altra velocità.
v_des = 675, Kp = 0.21, Ki = 0.59524; in verde la velocità letta dall’encoder, in rosso quella desiderata
Anche in questo caso il problema con l’integrale permane e quindi questo ci ha fatto capire che
nelle simulazioni che abbiamo studiato non erano considerati molti effetti della dinamica secondaria
che in realtà influiscono molto sul risultato finale.
Abbiamo così ripreso le prove per tentativi e trovato che una buona configurazione per il motore era
una configurazione di Kp e Ki esattamente opposta a come le simulazioni ci avevano suggerito.
v_des = 750, Kp = 0.5, Ki = 0.1; in verde la velocità letta dall’encoder, in rosso quella desiderata
33
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Codice finale
/* Header ed inclusioni e definizioni macro*/
/*---DEFINIZIONE DELLE VARIABILI---*/
/*---FUNZIONI---*/
//interrupt sull'overflow del timer0
#int_RTCC
void RTCC_isr() {
//somma della lettura corrente
//azzeramento timer1
//incremento della condizione di ciclo
}
Queste funzioni sono gli interrupt che si verificano quando va in overflow il contatore del timer su
cui sono abilitate. In particolare RTCC è collegata al Timer 0 (il principale del PIC) e la usiamo per
scandire l’unità di tempo per la lettura degli impulsi dell’encoder: ad ogni occorrenza della funzione
leggiamo gli impulsi contati, resettiamo il contatore del Timer 1 ed incrementiamo la condizione
per il conteggio della media.
/*---INIZIO MAIN---*/
void main() {
//setup dei timer
In particolare abilitiamo il Timer 1 come contatore sui fronti di salita di un segnale esterno (quello
proveniente dall’encoder).
//settaggio del CCP1 in PWM mode
Impostiamo il piedino RC2/CCP1 del PIC come uscita per il segnale PWM.
//motore spento
//inserimento velocita' desiderata
Chiediamo all’utente di immettere da tastiera la velocità desiderata in percentuale (0-100).
//conversione duty-cicle desiderato in duty-cicle motore
Convertiamo la velocità immessa dall’utente in velocità duty-cicle (0-1023).
//inserimento della direzione
//accensione led
Il led è acceso solamente quando l’azione di controllo è in esecuzione.
//scelta direzione
A seconda della scelta dell’utente abilitiamo uno dei due segnali per l’interruttore.
//abilitazione interruzioni
//inizializzazione timer1
/*ciclo di controllo*/
do {
//invio velocita' al motore
Inviamo al motore la velocità calcolata dal PID.
//inizializzazione timer1
Resettiamo il Timer 1 per il conteggio degli impulsi.
//lettura encoder
Restiamo in attesa di arrivare al numero desiderato di misure per fare la media.
34
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
//media velocita' contate
//conversione da velocita' letta dall'encoder a velocita' del motore
Come mostrato in precedenza, qui utilizziamo le tre zone lineari per convertire da velocità in
impulsi in velocità in duty-cicle.
//reset contatore impulsi encoder
//controllo assesto in un range di +-1 rispetto velocita' desiderata
Accettiamo uno scostamento dalla velocità desiderata di un’unità per correggere eventuali errori
dovuti alla conversione precedente.
//regolatore PI
Effettuiamo il calcolo mostrato in precedenza e le opportune conversioni di formato.
//controllo massimo numero di misure
Per uscire dal ciclo in caso non si raggiunga la velocità desiderata.
}
while(j<ASSESTO);
//spegnimento led
//conclusione
Specifichiamo all’utente se il programma è terminato perché è stata raggiunta la velocità desiderata
oppure perché abbiamo raggiunto il massimo numero di misure.
//frenata graduale motore
//chiusura interruttori H-Bridge
}
/*---FINE MAIN---*/
35
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
/*COMANDA AL MOTORE DI ANDARE ALLA VELOCITA' DECISA DALL'UTENTE,
LEGGE LE MISURE DELL'ENCODER E CONTROLLA LA VELOCITA' CON UN PI,
STAMPA LA MEDIA DELLE MISURE E LA VELOCITA' COMANDATA AD OGNI PASSO*/
#include
#use
#fuses
#use
#include
#include
<18F452.h>
delay(clock=20000000)
HS, NOWDT, NOPROTECT, NOLVP
RS232(baud=9600, xmit=pin_c6, rcv=pin_c7, errors)
<stdio.h>
<stdlib.h>
#define ASSESTO
#define MAXMIS
#define MEDIA
5
50
50
//uguaglianze per considerare regime permanente
//massime misure di attesa per raggiungimento velocità
//numero di conteggi di impulsi per la media
#define Kp
0.5 //costante della parte proporzionale del PI
#define Ki
0.1 //costante della parte integrativa del PI
#define step 1
//passo per l'integrazione numerica
#define
#define
#define
#define
#define
#define
d1
2.9993
d0 284.3289
e1
0.5899
e0 382.1712
f1
1.5155
f0 -122.5309
//coefficiente
//coefficiente
//coefficiente
//coefficiente
//coefficiente
//coefficiente
primo tratto grado 1
primo tratto grado 0
secondo tratto grado 1
secondo tratto grado 0
terzo tratto grado 1
terzo tratto grado 0
/*---DEFINIZIONE DELLE VARIABILI---*/
char direzione[3];
//direzione immessa dall'utente
int dir = 0;
//direzione del motore
char dc[5];
int32 dci = 0;
float dci_f = 0.;
//duty-cicle desiderato
//duty-cicle desiderato convertita in intero
//duty-cicle desiderato convertita in float
int32
int32
int32
float
impulsi = 0;
v_enc = 0;
v_enc_dc = 0;
v_enc_dc_f = 0.;
//contatore delle letture dell'encoder
//media impulsi letta da encoder
//velocita' letta da encoder convertita in duty-cicle
//velocita' encoder in duty-cicle convertita in float
float
int32
float
float
vf = 0.;
v = 0;
err = 0.;
integrale = 0.;
//velocita' calcolata dal PI
//velocita' da inviare al motore
//errore per l'integrazione numerica
//integrale per l'integrazione numerica
int m = 0;
int h = 0;
int j = 0;
//variabile per la media delle letture
//variabile per il totale delle prove
//variabile per il regime permanente
int i = 0;
//variabile per cicli for
/*---FUNZIONI---*/
//interrupt sull'overflow del timer0
#int_RTCC
void RTCC_isr() {
//somma della lettura corrente
impulsi += get_timer1();
//azzeramento timer1
set_timer1(0);
//incremento della condizione di ciclo
m++;
}
#int_TIMER1
void overflow() {
printf("Non dovresti mai leggermi!!!\n\r");
36
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
}
/*---INIZIO MAIN---*/
void main() {
//setup dei timer
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_2);
setup_timer_1(T1_EXTERNAL|T1_DIV_BY_1);
setup_timer_2(T2_DIV_BY_1, 199, 1);
//settaggio del CCP1 in PWM mode
setup_ccp1(CCP_PWM);
//motore spento
set_pwm1_duty(0);
//inserimento velocita' desiderata
getc();
printf("Benvenuto nel PIC18F452!\n\r");
printf("CONTROLLO DEL MOTORE IN CORRENTE CONTINUA CON REGOLATORE PI\n\r\n\r");
printf("Inserire la velocita' desiderata (000-100): ");
gets(dc);
dci = atoi(dc);
if((dci<0)||(dci>100)) {
printf("Velocita' errata!\n\r");
dci = 50;
}
printf("\n\rVelocita' comandata in percentuale: %ld\n\r", dci);
//conversione duty-cicle desiderato in duty-cicle motore
dci = (dci*5)+300;
printf("Velocita' comandata in duty-cicle: %ld\n\r", dci);
dci_f = dci;
v = dci;
vf = dci;
//inserimento della direzione
printf("Inserire la direzione (1 avanti/2 indietro): ");
gets(direzione);
dir = atoi(direzione);
if(dir==1) printf("Motore avanti.\r\n");
else if(dir==2) printf("Motore indietro.\r\n");
else {
dir = 1;
printf("Comando errato! Motore avanti.\r\n");
}
//accensione led
output_high(PIN_A0);
//scelta direzione
if(dir==1) {
output_high(PIN_B0);
output_low(PIN_B1);
}
else {
output_high(PIN_B1);
output_low(PIN_B0);
}
//abilitazione interruzioni
enable_interrupts(INT_RTCC);
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
//inizializzazione timer1
set_timer1(0);
37
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
/*ciclo di controllo*/
do {
//invio velocita' al motore
set_pwm1_duty(v);
//inizializzazione timer1
set_timer1(0);
//lettura encoder
while(m<MEDIA) { }
m = 0;
//
//media velocita' contate
v_enc = impulsi/MEDIA;
printf("Impulsi: %ld\r\n", v_enc);
if(v_enc<0) v_enc=0;
if(v_enc>610) v_enc=610;
//conversione da velocita' letta dall'encoder a velocita' del motore
if(v_enc<38) v_enc_dc = d1*v_enc+d0;
else if(v_enc<543) v_enc_dc = e1*v_enc+e0;
else v_enc_dc = f1*v_enc+f0;
v_enc_dc_f = v_enc_dc;
printf("Lettura %u: %ld;\n\r", h+1, v_enc_dc);
//reset contatore impulsi encoder
impulsi = 0;
//controllo assesto in un range di +-1 rispetto velocita' desiderata
if((v_enc_dc==(dci-1))||(v_enc_dc==dci)||(v_enc_dc==(dci+1))) j++;
else j=0;
//regolatore PI
vf += Kp*(dci_f-v_enc_dc_f) + Ki*(integrale+step*err);
integrale += step*err;
printf("Integrale : %f;\n\r", integrale);
err = dci_f-v_enc_dc_f;
printf("Errore: %f;\n\r", err);
if(vf<0.) vf=0.;
v = vf;
if(v<0) v=0;
if(v>800) v=800;
printf("PI: %ld;\n\r\n\r", v);
//controllo massimo numero di misure
h++;
if(h>=MAXMIS) break;
}
while(j<ASSESTO);
//spegnimento led
output_low(PIN_A0);
if(h<MAXMIS) {
printf("Velocita' raggiunta al passo %d!\n\r", h+1);
delay_ms(10000);
}
else printf("Troppo tempo per raggiungere la velocita'!");
printf("Fermo il motore.\n\r");
printf("Grazie per avermi usato!\n\r\n\r");
38
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
//frenata graduale motore
set_pwm1_duty(v*0.9); delay_ms(50);
set_pwm1_duty(v*0.8); delay_ms(50);
set_pwm1_duty(v*0.7); delay_ms(50);
set_pwm1_duty(v*0.6); delay_ms(50);
set_pwm1_duty(v*0.5); delay_ms(50);
set_pwm1_duty(v*0.4); delay_ms(50);
set_pwm1_duty(v*0.3); delay_ms(50);
set_pwm1_duty(v*0.2); delay_ms(50);
set_pwm1_duty(v*0.1); delay_ms(50);
set_pwm1_duty(0);
//chiusura interruttori H-Bridge
output_low(PIN_B0);
output_low(PIN_B1);
}
/*---FINE MAIN---*/
Schema elettrico e flussi dei dati
Il nostro sistema si presenta così:
39
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Troviamo la protoboard che monta al centro il PIC ed a sinistra i due connettori per l’alimentazione
in alto e per la seriale in basso. Nell’angolo inferiore destro della scheda si colloca l’H-bridge. Il
motore è collegato tramite due cavi di alimentazione e i quattro fili dell’encoder.
Passiamo ora a descrivere nel dettaglio la funzione di ogni filo:
Vss
E+
T
E2
M+
A
I1
Rx
I2
E1
PWM
S
Tx
GND
GND
M-
G-
EG+
La tensione generata dal trasformatore collegato alla rete elettrica viene stabilizzata a 5V dal
componente T e distribuita lunga la fila di piedini superiori tutti pistati. La fila inferiore è invece per
il riferimento a massa. Da qui prendiamo le tensione di alimentazione per l’encoder (E+ ed E-), la
tensione di confronto Vss e i riferimenti a massa dell’H-bridge (GND), la massa comune per il
generatore esterno (attaccheremo un morsetto su G-). Il generatore esterno ci serve per fornire i 12V
necessari al motore, la tensione positiva è collegata sempre con un morsetto al cavo G+ che la porta
al piedino Vs del ponte H.
La comunicazione seriale con il computer è già implementata sulla protoboard tramite il connettore
S e l’integrato ad esso collegato quindi abbiamo solamente dovuto collegare il piedino Tx del nostro
PIC con la ricezione dell’adattatore seriale e l’Rx con la trasmissione.
Il segnale PWM generato dal PIC viaggia sul cavo bianco fino ad arrivare all’Enable 1 dell’Hbridge. I due fili che comandano invece le due coppie di interruttori tramite i piedini di Input sono il
viola (I1) e l’arancione (I2), mentre gli Output sono poi inviati al motore con M+ e M-.
I due canali di uscita dell’encoder che portano l’informazione degli impulsi sono collegati il primo
al piedino di ingresso del Timer1 del PIC (E1), il secondo al piedino B2 del PIC (E2).
40
Controllo di un motore in corrente continua
Bongiorno, Galasso, Gratta
Appendice
Brevi note su particolari funzioni usate nelle simulazioni Matlab
disp(‘Testo’): scrive nella finestra di comando “Testo”;
plot(x1,y1,'o','g', x2,y2): disegna le due curve definite dai valori x1 e y1, x2 e y2. Si
possono specificare opzioni per il disegno come 'o' per i pallini in corrispondenza dei valori o
'g' per comandare il colore verde alla curva;
title(‘Testo’): imposta come titolo della figura corrente “Testo”;
axis([1e-1, 1e+7, -350, -100, -271, -179]): imposta ai valori passati come parametri
gli assi della figura corrente;
subplot(a,b,i): divide la figura in una matrice axb e permette di disegnare con un plot
nello slot i-esimo;
pause: pone in pausa l’esecuzione finché un utente non spinge un tasto;
tf(num, den); tf([Kp, Ki], [1 0]): crea una funzione di trasferimento usando come
numeratore il polinomio avente i coefficienti indicati nel vettore passato come primo parametro,
in ordine decrescente di esponente; stessa cosa per il denominatore e il secondo parametro;
bode(W): disegna il diagramma di Bode, modulo e fase, della funzione di trasferimento passata;
[mag, phase] = bode(W, wt): restituisce l’ampiezza e la fase del diagramma di Bode della
funzione W in corrispondenza della pulsazione wt;
margin(W): disegna il diagramma di Bode della funzione di trasferimento, mettendo in risalto il
margine di fase e il margine di guadagno;
[Gm, Pm, Wg, Wp] = margin(W): oltre ad evidenziare i margini sul diagramma di Bode,
restituisce un vettore a quattro elementi: margine di guadagno, margine di fase e relative
pulsazioni di attraversamento;
step(W): crea il grafico della risposta al gradino del sistema W;
series(W1, W2): crea la serie dei due sistemi indicati come parametri;
feedback(G, H): chiude in controreazione il sistema G che viene posto in catena diretta e il
sistema H assunto come sistema sul ramo di controreazione;
scrsz = get(0,'ScreenSize'); figure('Position',[scrsz(3)/2, scrsz(4)/2,
scrsz(3)/2, scrsz(4)/2]): funzione per collocare la finestra della figura in una determinata
zona dello schermo. Prima si acquisisce dal sistema la risoluzione dello schermo, poi si decide di
posizionare, in questo caso, una finestra larga e alta metà schermo (scrsz(3) è la larghezza,
scrsz(4) l’altezza) con il lato sinistro e quello in basso partente da metà dei pixel presenti; in
pratica si vuole collocare una finestra grande un quarto dello schermo nell’angolo in alto a
destra;
load(‘filename’): carica tutte le variabili presenti nel file specificato in filename, che deve
essere il percorso completo;
zeros(a, b): crea una matrice con a righe e b colonne di tutti zeri;
length(a): restituisce la lunghezza del vettore a;
sum(a): restituisce la somma degli elementi del vettore a;
min(a): restituisce il valore minimo del vettore a;
max(a): restituisce il valore massimo del vettore a;
interp1(x, y, xp, 'linear'): calcola il valore interpolato di una funzione
unidimensionale in corrispondenza di xp partendo dai dati x e y. In questo caso usa il metodo
linear;
polyfit(x, y, i): restituisce i coefficienti di un polinomio di grado i che interpoli
minimizzando l’errore quadratico medio i valori x,y;
polyval(c, x): calcola il valore del polinomio con coefficienti c per il valore x.
41