-

hey viewer, we're moving!

We are currently transitioning to a new web system, so we are not updating this wikisite anymore.

The public part of the new web system is available at http://www.ira.disco.unimib.it


INFIND2011/12 Motor control board

From Irawiki

Jump to: navigation, search

Students working on this project:

Contents

Introduzione

L'obiettivo di questo progetto è la realizzazione del firmware per una scheda di controllo motore. Tale scheda svolgerà la funzione di azionamento controllato da segnali digitali.

In particolare questa scheda si interfaccerà con un ponte H pololu, prevedendo un ingresso per un encoder e verrà comandata tramite comunicazione seriale.

La scheda è progettata per essere generica e adattabile e la maggior parte delle funzionalità sono configurabili dall'utente.

Relazione

Suddivisione dei compiti

Sensori/attuatori utilizzati

Motore DC

Un motore elettrico è uno strumento elettromeccanico che consente di convertire energia elettrica in energia meccanica. Nel caso specifico abbiamo utilizzato un motore dc a statore permanente, pilotabile ponendo una differenza di potenziale ai due poli corrispondenti alle terminazioni dell'avvolgimento presente sul rotore.

Encoder

L'encoder o trasduttore di posizione angolare è un sensore che permette di conoscere la rotazione di un asse rotante. Nello specifico si tratta di un sensore basato su moduli ottici (fotodiodi), accoppiati ad una ruota fonica; questa ruota è dotata di fori, grazie ai quali è possibile codificare l'angolo di rotazione semplicemente contandone il numero durante il movimento dell'asse su cui la ruota è montata. Tipicamente si sfrutta una coppia di sensori opportunamente posizionati, in modo da poter capire il verso di rotazione sfruttando la fase tra le due letture.

Ponte H

Un ponte H è un circuito elettronico che consente di comandare un motore. Esso, tramite quattro transistor, è in grado di far passare corrente attraverso i morsetti del motore in entrambe le direzioni. Il suo utilizzo è necessario poiché il microcontrollore non è in grado di erogare le correnti necessarie per il funzionamento di un motore. Il ponte H utilizzato è il Pololu High-Power Motor Driver 36v20 CS. Esso viene comandato dal microcontrollore tramite l'utilizzo di due segnali PWM e uno di direzione, secondo la tabella sottostante.

File:pololuSignals.png

Possiede inoltre due pin che segnalano eventuali fault e un sensore di corrente leggibile dal microcontrollore.

Periferiche utilizzate

ADC

Funzionamento e caratteristiche

La versione più diffusa di convertitore da analogico a digitale è quella ad approssimazioni successive. Il funzionamento è molto semplice e consiste nel valutare iterativamente un range entro cui può stare il valore da campionare. Ad ogni iterazione queste soglie si stringono permettendo di arrivare alla precisione desiderata.

Schema logico dell'adc ad approssimazioni successive

Come possiamo intuire dallo schema in figura, il funzionamento avviene in questo modo:

  1. tramite un registro S/H il valore analogico viene acquisito e mantenuto stabile
  2. il sar, cioè il registro che si occupa delle approssimazioni successive, genera un valore digitale da confrontare con quello analogico, concentrandosi sull'i-esimo bit che viene posto di default a 1 (alla prima iterazione si parte dal bit più significativo)
  3. tale valore viene convertito in analogico con un DAC
  4. il comparatore verifica quale dei due è più grande e restituisce il valore al SAR.
  5. se il valore digitale era più grande, il bit i viene settato a 0, altrimenti viene lasciato a 1
  6. si ritorna al punto 2 passando al prossimo bit

Dall'algoritmo appena descritto si evince che:

  • ad ogni iterazione (un colpo di clock) la precisione viene migliorata di un bit
  • ci si può fermare in qualsiasi momento a patto di accettare una precisione minore
  • per avere la massima precisione bisogna attendere un numero di cicli di clock pari alla risoluzione in bit del valore digitale

Un esempio di funzionamento è nell'immagine seguente

04263.png


L'ADC contenuto nell'STM32 è un convertitore analog-to-digital SAR a 12 bit. E' provvisto di analog watchdog, per monitorare quando il segnale supera una certa soglia, con la possibilità di segnalarlo tramite interrupt, così come per gli eventi di End of Conversion. La conversione può avvenire nelle modalità single, continuous, scan o discontinuous, con un tempo di conversione di 1.17 μs a 72 MHz. Una procedura di Self-calibration permette di ridurre l'errore delle conversioni dovuto allo stato iniziale dei condensatori presenti.

Collegamenti

Osservando i datasheet osserviamo la necessità di inserire un partitore resistivo che permetta di scalare l'output del sensore di corrente per adeguarlo al range di input del convertitore: il sensore di corrente è infatti alimentato a 5V e fornisce in uscita una tensione tra 0 e 5V. Il convertitore invece accetta in ingresso una tensione compresa tra Vref- e Vref+ dove Vref- equivale a Vss e Vref+ è un valore compreso tra 2.4V e 3.6V. Si potrebbero usare una resistenza da 1.5 e una da 2.7k in modo da ottenere un voltaggio massimo di 3.2V.

Nella scheda fornita però è già presente un partitore resistivo di questo tipo, formato da una resistenza da 4.7k seguita da una da 10k. Sfruttando la formula del partitore resistivo otteniamo un rapporto di 0.680272109 che permette di scalare 5V a 3.4V

Timer

Un timer, nella sua rappresentazione più semplicistica, non è altro che un registro che viene opportunamente incrementato/decrementato fino al raggiungimento di un determinato valore, per poi essere resettato.

Gli usi di questo tipo di periferica sono molteplici: possono essere utilizzati per gestire determinate procedure ad intervalli di tempo regolari, generare onde quadre a svariate frequenze, acquisire dati da encoders, ecc

Nella realizzazione di questo progetto abbiamo utilizzato i timer da 2 a 4, rispettivamente per gestire l'aggiornamento dell'encoder, il ciclo di controllo, il pwm del ponte H e leggere l'encoder del motore.

Contatore

La gestione della frequenza di controllo rappresenta il caso più tipico di utilizzo di un timer; quest'ultimo viene infatti sfruttato semplicemente per generare un interrupt adeguatamente temporizzato che attiverà la procedura di controllo.

PWM

Per utilizzare il ponte H abbiamo sfruttato la tecnica della Pulse Width Modulation (PWM) che consiste nella generazione di onde quadre a duty cycle variabile.

Questo viene fatto utilizzando un timer, in quanto abbiamo bisogno di generare dei fronti d'onda in salita a frequenza regolare, per poi andare a variarne il duty cycle e quindi gestire la "percentuale" di voltaggio massimo da mandare in ingresso ai morsetti del motore.

Encoder

Per sfruttare l'encoder abbiamo utilizzato un timer in grado di contare i ticks. Invece che incrementare/decrementare il valore del contatore del registro ad ogni ciclo macchina, sfruttiamo direttamente il segnale encoder già quadrato per valutare il numero di step che il sensore ha contato. In questo caso il termine del contatore rappresenta un problema in quanto l'overflow e l'underflow vengono segnalati dallo stesso interrupt. Per ovviare a ciò abbiamo usato una variabile che viene incrementata o decrementata ad ogni interrupt, a seconda del valore del bit di direzione.

DMA

Direct memory access è una funzionalità che permette la comunicazione tra periferiche del micro e memoria (oppure tra locazioni di memoria) senza intervento della CPU. Avere l'hardware specializzato per trasferire dati apporta il vantaggio di non dover occupare cicli di clock per queste operazioni, lasciando spazio a computazioni che richiedono il processore.

USART

Universal Synchronous Asynchronous Receiver Transmitter (USART) è la periferica che permette di comunicare in full-duplex con gli elementi esterni alla scheda attraverso il protocollo di comunicazione seriale. Per non dover gestire manualmente le comunicazioni di singoli caratteri e per evitare di sprecare cicli computazionali del processore è stato scelto di non utilizzare direttamente questa periferica ma bensì indirettamente attraverso DMA.

Durante il progetto è stato scelto di utilizzare la periferica USART3 per limitazioni della scheda in dotazione. USART3 è collegata ad APB1 con un clock massimo di 36KHz.

Struttura del firmware

Blocchi

Il firmware è suddiviso in più files, come descritto nella seguente tabella, che raggruppano funzionalità in base alla semantica. In particolare avremo files che si occupano della gestione di una periferica hardware e files che si occupano di una funzione logica, come ad esempio il controllo o la macchina a stati.

Di seguito descriviamo i singoli blocchi in termini qualitativi. Si rimanda al codice e alla documentazione doxygen, generabile dal codice stesso per una descrizione piu approfondita del funzionamento.

File desc
serial.h Si occupa della comunicazione seriale. Implementa tutti i comandi che servono per impostare parametri da pc, far eseguire azioni, restituire messaggi al pc e cose simili. Si interfaccerà con gli altri files per recuperare i loro dati.
adc.h Si occupa di gestire il sensore di corrente. Acquisisce i dati e fornisce alcuni metodi per la gestione dell'adc.
state.h Gestisce la macchina a stati del micro. Salva al suo interno lo stato e controlla le transizioni.
hbridge.h Si occupa di trasformare il controllo in un segnale pwm da mandare al ponte H. Di quest'ultimo, inoltre, gestisce i fault.
ctrl.h Controlla i set point per i tre tipi di controllo.
calib.h Si occupa della calibrazione.
enc.h Lettura encoder.
fun.h Contiene le funzioni comuni che servono a tutti, tipo delay.

I blocchi sono strutturati gerarchicamente in livelli e in particolare l'unico livello che comunica con l'esterno è quello del modulo seriale. Il livello inferiore, quello della macchina a stati, si interpone tra la seriale e i livelli inferiori, assicurando che tutti i comandi siano eseguiti al momento opportuno e che tutto venga inizializzato in modo corretto. A livello intermedio abbiamo poi i moduli di controllo e calibrazione che sfruttano l'astrazione dell'hardware offerta dai livelli sottostanti per effettuare operazioni di alto livello. Abbiamo infine il livello più basso dove vengono configurate le periferiche per interfacciarsi con l'hardware, ed in particolare con il motore attraverso il ponte-h e l'encoder.

Di seguito una rappresentazione grafica di questa gerarchia.

pub?id=1byiRDto7i07dLOcwBLCr3zMEE8ilQw652Yx9lZg-Ow0&w=1097&h=425&.png

Comunicazione via seriale

Le funzioni di supporto all'utilizzo della comunicazione via seriale sono definite all'interno del file serial.c. Il compito principale è stato quello di ridefinire l'handler degli eventi di fine trasmissione su DMA1_Channel3 implementando una classica macchina a stati per la gestione dell'input.

È stato possibile implementare anche la ricezione con dma in quanto, grazie a un semplice protocollo di comunicazione da noi stabilito, si ha il numero di byte che verranno inviati.

I dati ricevuti via seriale vengono salvati in una variabile globale chiamata rparameter che viene utilizzata come stack; wparameter viene usata in modo simile con i dati che si vogliono inviare.

ADC

Il firmware relativo all'ADC è contenuto nel file adc.c, con relativo header. Questo file mette a disposizione alcune variabili globali e alcune funzioni per impostare e utilizzare l'ADC1 Channel 1.

Il convertitore Analog to Digital è necessario per poter effettuare il controllo in corrente in quanto il ponte H pololu è equipaggiato con il sensore di corrente ACS714 che fornisce un valore di tensione analogico proporzionale alla corrente. Oltre a questo è anche utile per monitorare l'utilizzo del motore permettendo di rilevare situazioni di emergenza in corrispondenza di correnti troppo elevate.

L'acquisizione dei dati avviene in modalità continua con DMA. Questo perchè il valore utilizzato dal controllo ed eventualmente fornito all'utente sarà una media dei valori letti. Il numero di valori da mediare è definibile dall'utente.

Come consigliato dal reference manual effettueremo una calibrazione ad ogni accensione dell'ADC.

Il dato digitalizzato viene salvato in un registro a 16 bit; essendo però il dato di 12, 4 bit sono di default messi a zero.

Conversion time

Il tempo di conversione, ovvero il tempo necessario a campionare un valore, è dato dalla formula SAMPLING_TIME + 12.5 CYCLES. Il SAMPLING_TYME, configurabile dall'utente, può andare da 1.5 a 239.5 cicli con gli step descritti nel reference manual, che qui riportiamo:

  • 000: 1.5 cycles
  • 001: 7.5 cycles
  • 010: 13.5 cycles
  • 011: 28.5 cycles
  • 100: 41.5 cycles
  • 101: 55.5 cycles
  • 110: 71.5 cycles
  • 111: 239.5 cycles

Il clock che regola l'adc va a 12MHz, ottenuto da APB2 (72MHz) con prescaler 6, in quanto al massimo può essere 14 MHz, come specificato dal reference manual nella sezione adc. Pertanto il tempo di conversione va da un minimo di 1.17us a un massimo di 21us

Resolution

Il range di valori che il sensore di corrente genera sono tra 0V e 5V. Questi vengono riscalati tramite un partitore resistivo tra 0V e 3.4V.

Come abbiamo detto l'adc ha una risoluzione di 12 bit che corrispondono a 4096 valori. La più piccola unità che potrà discriminare quindi è 0.83 mV. Questo dato è però relativo a una corrente che attraversa il motore e quindi la vera risoluzione va valutata su questo amperaggio. Il sensore di corrente può misurare un range fino a 36 ampere per direzione, quindi da -36 a +36. Ne risulta che la quantità minima discriminabile è 18mA.

Macchina a stati

Il funzionamento del controllore sarà pilotato da una macchina a stati. La macchina a stati si occuperà di gestire i comandi che arrivano alla seriale. Sarà la FSM, infatti, a decidere se un comando possa essere eseguito o meno. In particolare, solo alcune variabili possono essere scritte in stati diversi da quello di Init.

Stato Descrizione
INIT All'accensione ci troviamo in questo stato. È possibile settare tutti i parametri del software. Il motore è frenato.
RUNNING Loop di controllo attivo; è comunque possibile mettere il motore in modalità coasting. Solo il set point può essere modificato.
CALIB Il motore viene frenato, il controllo viene fermato e parte la calibrazione. Durante la calibrazione il motore si può muovere settando il set point (che verrà passato, moltiplicato per bridge_ctrl2pwm, direttamente al ponte H) oppure in modalità coasting.

pub?id=14tE-i_CIlX47lsg2W4nVXbXliJ8lRDQFlXqOqjas--4&w=960&h=320&.png

PonteH

Per pilotare il ponte H sono necessari due segnali: PWMH e PWML. PWML viene tenuto costantemente alto, mentre su PWMH viene mandato il segnale PWM. Per generarlo è stata utilizzata la modalità PWM1 del Timer3 sul canale 1. Per settare il duty cycle è quindi sufficiente scrivere nel registro CCR1. La funzione bridge_set si occupa proprio di questo. Il controllo inviato dal controllore, prima di essere trasformato in duty cycle, viene moltiplicato per un fattore ctrl2pwm. È inoltre possibile mandare il motore in modalità "coasting", in cui le ruote sono libere di girare. Per ottenere ciò sia PWMH che PWML vengono settati "bassi". Questo modulo si occupa anche di gestire i fault del ponte H. Quest'ultimi vengono rilevati mediante degli external interrupt, che si occupano di settare i bit corrispondenti nella variabile state_error. La gestione dell'errore viene effettuata dal controllore in base ad un parametro configurato dall'utente. Le possibili azioni sono: non fare nulla, passare in modalità coasting, frenare il motore. Per ripristinare il normale funzionamento del sistema è necessario scrivere nella variabile state_error. Ciò farà sì che venga resettata e che il controllore rincominci a lavorare normalmente. I possibili errori del polulu sono:

File:pololuFault.png

Da notare, infine, che gli ingressi del pololu necessitano di un segnale logico a 5V, mentre il micro è in grado di erogare al massimo 3V. Per questo motivo è stato utilizzato un level shifter (attivabile/disattivabile via software, manipolando il pin !OE).

Controller

Il modulo di controllo implementa le formule necessarie al controllo del motore DC

Sono stati sviluppati tre diversi tipi di controllori: - Posizione - Velocità - Corrente

Queste tre tipologie, pur avendo la medesima legge di controllo, si differenziano nella fase di aggiornamento dello stato del sistema.

Si è quindi deciso di implementare un unico loop di controllo comune a tutte e tre le tipologie, preceduto da una fase specifica per ogni tipo di controllore.

Controllo in posizione

In questo caso lo stato da controllare è rappresentato dalla posizione angolare dell'asse motore.

L'aggiornamento dello stato avviene quindi sfruttando l'encoder e, dato che siamo interessati a regolare una posizione, avverrà cumulativamente moltiplicando i tick encoder per la variabile stepToAngle;

Impostando correttamente questa variabile tramite la fase di calibrazione è possibile ottenere buoni risultati di precisione.

Controllo in velocità

In questo caso lo stato da controllare è rappresentato dalla velocità angolare dell'asse motore.

L'aggiornamento dello stato avviene quindi sfruttando l'encoder. Non è possibile aggiornare in modo cumulativo come nel caso precedente, in quanto siamo interessati alla velocità istantanea; sarà quindi necessario sfruttare la lettura encoder, moltiplicata per stepToAngle ed un'ulteriore variabile deltaT.

Mentre la prima viene acquisita tramite calibrazione come nel caso del controllo in posizione, la seconda rappresenta il periodo corrispondente alla frequenza di funzionamento del ciclo di controllo.

Controllo in corrente

In questo caso lo stato da controllare è rappresentato dall'assorbimento in corrente del motore.

Per questa particolare tipologia di controllore non è possibile sfruttare l'encoder ma andrà utilizzato il modulo adc che, leggendo i dati provenienti dal sensore di corrente del ponte H, è in grado di stimare l'assorbimento del motore.

In questo caso i dati vengono valutati obbligatoriamente rispetto ad un offset calcolato in fase di inizializzazione sfruttando la media di svariate letture provenienti dal sensore di corrente, in modo da ridurre al minimo il rumore.

Loop principale

Nel loop principale viene implementata la legge di controllo, comune a tutte e tre le tipologie di controllori.

La legge viene calcolata come segue:

Il primo passaggio è il calcolo dell'errore, come semplice differenza tra lo stato desiderato e lo stato attuale;

Questo errore viene poi usato per calcolare le componenti proporzionali, integrative e derivative.

La proporzionale è stata implementata come la semplice moltiplicazione dell'errore per la costante KP definita dall'utente.

La integrativa è implementata cumulando ad ogni ciclo di controllo la moltiplicazione tra l'errore ed il periodo del controllore.

La derivativa è implementata come la costante KD moltiplicata per (err - err_1)/deltaT dove err_1 rappresenta l'errore al tempo passato e deltaT il periodo di controllo.

Grazie a queste tre componenti è possibile considerare, oltre al semplice dato fornito dall'errore attuale, anche l'andamento temporale del controllo.

Encoder

L'encoder viene utilizzato da altre classi (ad esempio il controllo per poter aggiornare lo stato del sistema)

Tramite il metodo getData() è possibile ottenere il numero di tick compiuti dall'ultima volta che il metodo è stato chiamato

Inoltre mantiene lo storico di tutti i ticks compiuti a partire dall'accensione, resettabili tramite il metodo resetData()

E' importante notare che, al fine di mantenere consistenti le letture odometriche, non è possibile far usare la classe encoder a più moduli contemporaneamente

Calibrazione

I moduli relativi alla calibrazione permettono di calcolare automaticamente, tramite una procedura semplice, il valore di conversione tra step contati dall'encoder e l'angolo effettivamente sotteso dal movimento del motore. Questo può essere utile nel caso in cui ad esempio non si conosca il rapporto di riduzione esatto della trasmissione.

È definita una variabile float stepToAngle che rappresenta un valore di conversione da step encoder ad angolo. La sua unità di misura è quindi angle/steps in modo tale che moltiplicandola per un numero di step esso venga convertito in radianti. Da notare il fatto che non è espressa l'unità di misura dell'angolo: essa può essere infatti decisa a piacere dall'utente. L'unico vincolo è la coerenza tra la variabile stepToAngle e i setPoint impartiti.

Le funzioni messe a disposizione da questo modulo sono due:

  • Inizia la calibrazione
  • Termina la calibrazione

Affinchè la calibrazione avvenga correttamente è necessario impostare il valore di calib_angle durante la calibrazione. Al termine della calibrazione viene calcolato il nuovo valore di stepToAngle, come angolo/step, usando il valore specificato.

La procedura di calibrazione richiede i passi riportati in figura

pub?id=1kDGhrvztzj2YrqO2MI2wO2xgkj71PgwIqUEoeuCVLc0&w=450&.png
  • Viene inviato il comando di inizio calibrazione che verrà controllato dalla macchina a stati
    • Questo comporta l'azzeramento degli step letti fino ad ora dall'encoder
  • Il motore viene mosso di un numero prestabilito di giri/gradi
  • Tale numero viene inviato alla scheda tramite seriale
    • Il valore letto dall'encoder viene usato congiuntamente con la misura dell'angolo effettuato per computare il valore step\_to\_angle=\frac{angle}{steps} e la calibrazione viene terminata

Alcune questioni pratiche circa la procedura di calibrazione verranno discusse nella sezione relativa del manuale utente. Si rimanda invece al codice per i dettagli implementativi.

Workflow

Il flusso del programma è gestito unicamente tramite eventi. In particolare la maggior parte dei moduli sono disattivati finchè non si passa nello stato di running. Questo perchè nello stato di init, quando la board viene collegata, si possono configurare tutte le periferiche.

Dopo aver configurato tutti i moduli si può passare nello stato di running, in cui è possibile modificare il setpoint, leggere le varie configurazioni e dati sensoriali e passare nello stato di calib (o tornare nello stato di init)

Nello stato di init sarà possibile configurare nuovamente i parametri dei moduli, mentre nello stato di calibrazione sarà possibile procedere alla calibrazione di stepToAngle.

Altri eventi che possono scattare, oltre ai comandi della seriale, modificano il flusso di esecuzione del programma:

  • Errori: comportano la modifica della variabile di stato di errore e l'esecuzione dell'azione definita dall'utente
  • Aggiornamento dell'encoder: fa scattare una procedura che intercetta il verificarsi di un giro completo dell'encoder, per accumulare i dati in una variabile temporanea
  • Ciclo di controllo: calcola e genera una azione di controllo per mantenere il setpoint desiderato

Di seguito riportiamo le priorità dei relativi interrupt

Evento Descrizione Priorità
ADC1_2_IRQn Watchdog ADC 10
EXTI1_IRQn Fault 1 pololu 10
EXTI2_IRQn Fault 2 pololu 10
TIM4_IRQn Lettura giro completo encoder 13
TIM2_IRQn Ciclo di controllo 15
DMA1_Channel2_IRQn USART TX 20
DMA1_Channel3_IRQn USART RX 20

Protocollo di comunicazione seriale

Il protocollo di comunicazione seriale prevede un ristretto set di comandi, che possono essere raggruppati nelle seguenti categorie:

  • Lettura/scrittura
  • Gestione dello stato
  • Comandi ausiliari

Tramite i comandi di lettura e scrittura è possibile gestire alcune locazioni di memoria definite all'interno del microcontrollore. Alla scrittura segue solitamente una risposta del micro che provvede a configurare il valore settato dall'utente (ad esempio setPoint, costanti del controllore, temporizzazioni e quant'altro).

Il secondo gruppo di comandi permette di variare lo stato della FSM che governa la board.

Infine altri comandi permettono azioni dirette come il reset e la gestione del coast/uncoast

Di seguito l'elenco completo dei comandi disponibili

Define Carattere Parametri Descrizione
CMD_WHOIS w - Restituisce una stringa di descrizione del firmware (terminata da dal carattere \0)
CMD_SET S mask + vals Scrive i valori vals nelle variabili determinate dalla maschera mask
CMD_READ R mask Legge le variabili indicate da mask
CMD_READ_SINGLE_VARIABLE r position Restituisce il valore della variabile indicata da position
CMD_SET_SINGLE_VARIABLE s position + val Scrive il valore val nella variabile indicata da position
CMD_STATE_START x - Serve per passare da Init a Running
CMD_STATE_GORUNNING a - Serve per passare da Calib a Running
CMD_STATE_COAST j - Serve per passare in modalità coasting
CMD_STATE_UNCOAST u - Serve per uscire dalla modalità coasting e ritornare nella modalità controllata
CMD_STATE_GOCALIB c - Serve per passare in modalità calibrazione e far partire la stessa
CMD_STATE_RESET r - Serve per resettare la schedina

Come abbiamo detto vi è la possibilità di impostare via seriale svariati parametri caratterizzati da una posizione in memoria. Alcuni comandi permettono di leggere/scrivere più valori contemporaneamente. Per far questo si invia una maschera come parametro. Per ogni bit settato a 1 seguirà una lettura/scrittura della locazione corrispondente: per esempio un comando di read con maschera 0b101 restituirà i valori in posizione 0 e 2. Ricordiamo che la maschera deve essere di quattro byte e che, a causa dell'implementazione interna della maschera, per ora è possibile mostrare all'esterno solamente 32 variabili. È possibile invece specificare unicamente la posizione, in caso di lettura/scrittura di una singola locazione, in un byte. In quasi tutte le locazioni è possibile scrivere solo nello stato di INIT.

Di seguito tutte le locazioni di memoria definite

Define Position Type Access Description Default value
MASK_STATE 0 int r Lo stato in cui ci si trova Init
MASK_STATE_SETPOINT 1 float rw Il set point 0
MASK_STATE_BOARD_ID 2 char rw Id della board (non utilizzato) 0
MASK_STATE_ERROR 3 char rw Errore verificatosi. Scrivere qualsiasi valore per resettare. La variabile è composta da tre bit. I due meno significativi rappresentano i due fault del pololu, mentre il rimanente indica se l'adc abbia rilevato una corrente maggiore della soglia. 0
MASK_STATE_ERROR_REACTION 4 char rw Politica di gestione dell'errore 0
MASK_ADC_DATA 5 float r Contiene l'ultimo valore calcolato dall'adc, come media degli ultimi campioni Random values
MASK_ADC_WATCHDOG_LOW 6 short rw Soglia inferiore del watchdog sul sensore di corrente 0
MASK_ADC_WATCHDOG_HIGH 7 short rw Soglia superiore del watchdog sul sensore di corrente 4095
MASK_ADC_SAMPLE_TIME 8 int rw Adc sample time 7
MASK_ADC_EXP_SAMPLES_TO_AVERAGE 9 char rw Numero di campioni rilevati dall-adc con cui fare la media 10
MASK_CALIB_ANGLE 10 float rw Angolo misurato esternamente durante la calibrazione. Questo valore può essere settato solo nello stato di calibrazione. 0
MASK_STEP_TO_ANGLE 11 float rw Rapporto di conversione tra step dell'encoder e angolo percorso 1
MASK_CTRL_TYPE 12 char rw Tipologia di controllo, 0 posizione 1 velocità 2 corrente 0
MASK_CTRL_FREQUENCY 13 float rw Frequenza operativa del controllore in Hertz 50
MASK_CTRL_KP 14 float rw Costante proporzionale del controllore PID 1.0
MASK_CTRL_KI 15 float rw Costante integrativa del controllore PID 0
MASK_CTRL_KD 16 float rw Costante derivativa del controllore PID 0
MASK_CTRL_MIN_ERR 17 float rw Errore minimo, valori minori di errore non genereranno azioni di controllo 1.0
MASK_CTRL_INIT_POSE 18 float rw Offset per lo stato del controllore 0
MASK_CTRL_BOUND_STATE 19 char rw Limitazioni sullo stato, se 1 vengono abilitati maxState e minState 0
MASK_CTRL_MAX_STATE 20 float rw Se lo stato supera questo valore il controllore inverte l'azione di controllo fino a valori inferiori 0
MASK_CTRL_MIN_STATE 21 float rw come maxState ma limita inferiorimente 0
MASK_HBRIDGE_CTRL2PWM 22 int rw Trasforma il controllo nel duty cycle del pwm 1
MASK_HBRIDGE_FREQUENCY 23 int rw Frequenza del segnale pwm 20000
MASK_CTRL_ERR 24 float r Errore residuo dell'azione di controllo 0
MASK_CTRL_CONTR 25 float r Valore di intensità dell'azione di controllo 0
MASK_CTRL_STATE 26 float r Stato attuale del sistema ricostruito tramite letture encoder/sensore di corrente 0
MASK_CTRL_DELTAT 27 float r Periodo del ciclo di controllo, derivato da ctrl_fequency 1.0

Parser dei comandi

Il parser dei comandi si comporta come una classica macchina a stati. Questo componente viene richiamato ogniqualvolta DMA1_Channel2 termina il suo compito di ricezione e lancia l'interrupt DMA1_Channel2_IRQHandler. Di seguito una rappresentazione grafica della macchina a stati

pub?id=1yR4zNyQzxNqF5dzSmSVpOsmsmR_6tf0M-mZDaIdzwmc&w=460&.png
  • INIT_BYTE: si attende l'arrivo del carattere 0x55 per abilitare la ricezione di comandi su seriale. Questo stato è utile perché, nel caso in cui la seriale abbia dei problemi e rimanga in uno stato inconsistente permette di perdere solamente l'attuale comando e quello successivo (che verrà parzialmente interpretato come comando attuale)
  • CMD: stiamo aspettando che ci arrivi un comando, quindi il prossimo byte che ci arriva verrà interpretato come comando. Lo stato successivo è determinato dal comando ricevuto
  • MASK: in questo stato aspettiamo di ricevere una maschera di 4 byte che indica quali parametri verranno letti o scritti. Alla fine della lettura della maschera essa verrà gestita in modo diverso a seconda che il comando sia di input o output
    • input: ci aspettiamo di ricevere un numero di byte dipendente dalla maschera. Passiamo nello stato VALUES
    • output: dobbiamo inviare dei parametri; lo facciamo e torniamo ad aspettare un nuovo comando nello stato CMD
  • POSITION: in questo stato ci aspettiamo di ricevere la posizione della variabile che deve essere letta o scritta. La riceviamo e gestiamo la READ direttamente; per la SET passiamo nello stato VALUES in modo analogo alla gestione della maschera
  • VALUES: in questo stato siamo in attesa di ricevere dei valori da inserire nei parametri corrispondenti. Una volta ricevuti li processiamo e torniamo in attesa di un nuovo comando.

Prove sperimentali

Video di dimostrazione:

Risultati e conclusioni

Dalle prove sperimentali illustrate precedentemente e dai test effettuati in laboratorio rileviamo il corretto funzionamento del progetto.

Siamo soddisfatti sia dal punto di vista della qualità del codice prodotto, sia dalla semplicità dell'interfaccia lato pc. Oltre a questo il progetto ci sembra sufficentemente robusto e generico da poter essere utilizzato in diverse applicazioni.

Durante lo sviluppo del firmware abbiamo comunque riscontrato una serie di problemi. Uno dei più rilevanti è stato il fatto che, benché tutti i registri risultassero configurati correttamente, il motore partisse da solo appena passati in modalità "running" (anche con set point nullo). Questo comportamento ci è parso inspiegabile dato che, dando successivamente un set point diverso da zero, la schedina funzionava correttamente; inoltre, rimandando un set point nullo il motori effettivamente si fermava benché la configurazione dei registri fosse uguale a quella iniziale. Comunque sia abbiamo risolto questo problema tramite un work-around, cioè dando al momento di passare in modalità running un set point diverso da zero, aspettando un piccolo intervallo di tempo (sufficiente ad una transizione del PWM) e poi dando zero, il tutto con il level shifter che pilota il pololu disattivato.

Un'altro problema è dato dal fatto che l'errore di "under voltage" continua a scattare. Ciò è dovuto al fatto che il condensatore di alimentazione del motore è troppo piccolo e che l'alimentatore non è in grado di fornire i picchi di corrente richiesti durante le repentine accelerazioni. Su alcuni motori, inoltre, anche il fault "over temperature" scatta in continuazione.

Abbiamo implementato nel firmware anche una funzione per fare il reset del microcontrollore. Per far ciò viene chiamata la funzione NVIC_SystemReset() delle librerie ST. Purtroppo tutto ciò sembra non funzionare correttamente.

Un ultimo problema riscontrato è nel software di test sviluppato in ruby. In particolare nella ricezione dei byte inviati dalla scheda capita spesso di perdere dei byte (uno o due) impedendo la corretta lettura dei dati. Per risolvere questo problema abbiamo semplificato la procedura di lettura in modo da velocizzarla il più possibile, introdotto una seconda istanza di oggetto porta seriale unicamente per la lettura, ma anche questo non ha risolto il problema. Infine si è optato per un timeout sulla ricezione in modo da non pregiudicare il corretto funzionamento della parte di scrittura che, invece, non ha presentato alcun problema.

Sviluppi futuri

Durante lo svolgimento del progetto sono stati individuati numerosi punti di miglioramento e nuove funzionalità che possono essere implementate per rendere più completa la scheda

Backup dei dati

Attualmente i parametri devono essere inseriti ad ogni accensione della scheda. Nonostante questo non sia particolarmente vincolante in quanto, una volta inseriti nel codice che utilizza la scheda, non sono necessarie altre azioni, pensiamo potrebbe essere utile salvare tali parametri sfruttando la memoria flash del microcontrollore. In questo modo all'accensione si potrebbero caricare tali parametri senza doverli specificare ogni volta.

Alternativamente è possibile usare i registri di backup offerti dalla scheda usando ad esempio una procedura che si occupi di salvare i valori necessari, anche se questo presuppone di mantenere la scheda alimentata.

Sincronizzazione

È opportuno prevedere una sincronizzazione tra l'utilizzatore e la scheda di controllo motore. In particolar modo serve per apporre un timestamp ai dati sensoriali che la scheda invierà all'utilizzatore.L'obiettivo è quello di far si che l'utilizzatore possa sapere a che istante di tempo "esatto" si riferisce un dato.

La sincronizzazione consta in una procedura in cui l'utilizzatore che chiamiamo U invia un messaggio al device D (la nostra scheda). Nel contempo U salva il timestamp di quando ha inviato il messaggio: questo timestamp è molto importante e lo chiameremo ReferenceTime. Appena D riceve il messaggio azzera il suo orologio interno e risponde a U comunicando l'avvenuta sincronizzazione.

Di seguito ci sarà uno scambio veloce di messaggi con l'obiettivo ci calcolare il delay introdotto dalla comunicazione seriale:

  1. U segna il timestamp e immediatamente manda un messaggio a D
  2. D riceve il messaggio e immediatamente risponde a U
  3. U riceve il messaggio e immediatamente segna il nuovo timestamp
  4. U calcola quanto ci è voluto per avere l'eco. Dividendo questo valore per 2 otterremo il tempo di invio del comando

Questa procedura può essere ripetuta più volte e usando la media dei valori per stimare il delay

Ogni qual volta a D viene richiesto un valore per cui deve fornire un timestamp, aggiungerà il valore del suo orologio, azzerato in fase di sincronizzazione. U sarà in grado di ricostruire il tempo a cui il dato è stato generato semplicemente sommando il timestamp ricevuto al ReferenceTime e al delay stimato in fase di sincronizzazione.

Ovviamente il timestamp non deve essere catturato da D al momento della richiesta del dato ma al momento della generazione del dato stesso.

Comunicazione multi slave

Attualmente la scheda è descritta da un identificativo numerico impostabile dall'utente. Questo può essere sfruttato per comandare più schede uguali con la stessa porta seriale.

Per fare questo è necessario però modificare il parser dei comandi in modo che li accetti unicamente se l'id specificato nel messaggio corrisponde al proprio. oltre a questo è anche necessario pensare a un meccanismo di arbitrazione del bus.

Read tramite timer

Una funzionalità che può essere utile in fase di test è l'output temporizzato di parametri, ovvero la possibilità di impostare un timer per inviare continuamente uno stream di dati scelti dall'utente. Ad esempio l'utente potrebbe essere interessato a monitorare la posizione rilevata dall'encoder a 10Hz, oppure la corrente misurata tramite l'apposito sensore del ponte-h

Watchdog per reset temporizzato

Un'utile aggiunta è rappresentata da un watchdog per il reset temporizzato. Questo è utile nelle situazioni in cui la comunicazione cade o l'esecuzione si blocca per un errore di programma non individuato in fase di test. Il reset automatico permetterebbe di ripristinare la situazione, garantendo una maggiore robustezza del sistema a errori e situazioni anomale. Il controllo potrebbe essere fatto sui comandi impartiti dall'utente: se entro un certo tempo non arriva nessun comando viene eseguito il reset. Questo comporta all'utente di inviare un comando ogni tot, unicamente per tenere attiva la scheda. Oppure si potrebbe controllare il tempo di esecuzione dell'interrupt della seriale, in modo da avere un controllo sugli errori di comunicazione.

Irrobustimento del protocollo di comunicazione

Il protocollo di comunicazione può essere migliorato in vari modi, ad esempio aggiungendo lo start_byte anche nei valori restituiti dalla scheda. Oltre a questo potrebbe essere utile aggiungere un byte indicativo del comando con cui l'utente ha richiesto tali dati (read singolo, read con maschera, read temporizzato) in modo da permettere una migliore interpretazione dei dati. Inoltre si potrebbero introdurre messaggi di ack, in modo da rendere il protocollo più robusto ad una eventuale perdita di messaggi.

Allegati

Manuale utente

Alla pagina Motor control board: Manuale utente è possibile trovare il manuale utente, che spiega in semplici passaggi come mettere in funzione la board e come utilizzarla.

Codice sorgente del Firmware

File:Firmware.zip

Codice sorgente di eventuali applicazioni lato PC

File:RubyMcbTest.zip

Materiale utile

link ai datasheet utilizzati, a documenti che illustrano il funzionamento dei sensori o le tecniche di filtraggio, bibliografia, etc etc..

Personal tools