PID motor control

Sono due giorni che razzolo per il web, devo dire che ho trovato molto sul PID, ho studiato un pò il problema e ho capito (grazie a gli esempi) che più o meno quello che in elettronica si chiama circuito closed loop.

Gli anelli di reazione sono quasi sempre usati negli amplificatori audio, dove una porzione del segnale amplificato entra nell'aplificatore differenziale.

Ora io, nonostante gli esempi, e la teoria, non riesco ad immaginare come procedere con il PID nel playground.

Io ho un carrello di stampante con motore DC encoder in quadratura con timing strip, quindi posso ricavare più facilmente la distanza in mm. L'encoder mi conta da 0 a 5760, sono in grado di muovere il carrello posizionandolo dove voglio, anche se quando il duty cycle e alto l'inerzia del carrello porta a superare il punto in cui fermo il motore.

Gli esempi che ho visto con il PID, non sono direttamente utilizzabili con l'encoder incrementale, in quanto ho visto il controllo motore tramite potenziometro + PID, il PID come controllo di fenomeni lenti come la variazione di temperatura, ma nessuno che prenda in considerazione diretta la lettura dell'encoder e in base a questa aumenti e riduca il duty cycle.

Vorrei almeno iniziare con il sono controllo proporzionale, ma sono indeciso su come procedere, e come sia possibile implementare ciò che credo si debba fare.

Dovrei campionare la larghezza degli impulso dell'encoder o ricavare la frequenza, in tutti i casi come se pò fà. Ho provato con pulseIn ma è bloccante mi pare.

La routine ISR viene chiamata su pin changed PORTD.

ISR(PCINT2_vect) 
{
    //PCint(0);
    // Pin Arduino 4 (PD4), 5 (PD5)
    // se lo stato di encoder0PinA è cambiato, registra lo stato corrente 
    // e chiama encoder0ChA()
    if ((PIND & 0b00010000)// != stateEncoder0PinA) 
    //if (digitalRead(encoder0PinA) != stateEncoder0PinA) 
    {
        //widthEncoder0PinA = pulseIn(encoder0PinA, (PIND & 0b00010000));
        stateEncoder0PinA = (PIND & 0b00010000);
        //stateEncoder0PinA = digitalRead(encoder0PinA);
        encoder0ChA();
    }
    // se lo stato di encoder0PinB è cambiato, registra lo stato corrente 
    // e chiama encoder0ChB()
    if ((PIND & 0b00100000) != stateEncoder0PinB) 
    //if (digitalRead(encoder0PinB) != stateEncoder0PinB) 
    {
        stateEncoder0PinB = (PIND & 0b00100000);
        //stateEncoder0PinB = digitalRead(encoder0PinB);
        encoder0ChB();
    }
}

ciao MauroTec

Ci sono certe curve di accelerazione e decelerazione dato dalla massa del motore/carello. Per questo devi incominciare di frenare prima di arrivare al punto dove vuoi arrivare. Il "quanto" é il problema di trovare. Siccuramente se freni al punto di arrivo e fai x passi in piú vuol dire che devi incominciare x passi prima per arrivare giusto. Questo é una frenata brusca. Per avere una decelazione piú dolce devi incominciare la frenata prima usando una curva (retta) di decelazione piú lenta di quella di massima frenata. Il punto di partenza della decelerazione dipende dalla velocitá di movimento.

Per il regolatore PID: non so se é necessario percorrere quella strada.

Per la misura dei passi usa l' interrupt per incrementare/decrementare la posizione.

ciao Uwe

Ci sono certe curve di accelerazione e decelerazione dato dalla massa del motore/carello.

Esatto, se ci fate caso alcune stampanti a inkjet hanno motori DC ed encoder ed ho potuto notare durante l'inizzializzazione dei comportamenti particolari del carrello. Vero alcuni di questi sono duvuti alla pulizia delle testine, altri no.

La mia idea è che se posso misurare la distanza percorsa dopo che ho tolto alimentazione al motore allora posso anche ricavare l'inerzia e quindi la massa, ma questo viene per ultimo.

Per il regolatore PID: non so se é necessario percorrere quella strada.

Io suppongo che la strada sia proprio quella, tendo verso il PID da quando ho scoperto che l'integrato LM628/629 ha un PID integrato. Se siete interessati alla movimentazione tramite motore DC, non potete non dare un'occhiata al datasheet.

Per la misura dei passi usa l' interrupt per incrementare/decrementare la posizione.

Si fatto, il codice in ISR viene chiamato ogni volta che i pin 4 e 5 di arduino cambinano stato. Ora ho modificato il codice in ISR per contare quanto è un periodo.

Esempio : Entra la prima volta in ISR, lo stato del pin 4 è cambiato allora salva micros() in startTime. Entra la seconda volta, in ISR, lo stato del pin 4 è cambiato, va avanti. Entra la terza volta in ISR, lo stato del pin 4 è cambiato cioè, ha lo stesso stato che aveva la prima volta in ISR, quindi ricavo il tempo che è trascorso così:

larghezza_impulso = micros() - startTime se faccio 1/larghezza_impulso ricavo la frequenza.

Ora questo dato l'ho usato con la classe PID nel playground, con esito negativo, forse il limite minimo di di campionamento 1ms è troppo alto.

Tirando le somme sono alla frutta, sto pensando di abbandonare ed ordinare un IC LM629, comunque sia se ne parla sempre dopo le feste, e quindi ci sbatto ancora un pò il muso.

Ok ciao, almeno una risposta l'ho ricevuta, è triste vedere 60 letture e neanche una risposta. :( Ok Salutoni e Aguri di Buone Feste a tutti gli arduinisti o arduinari, insomma a tutto il Forum.

ci sono 2 vie per fare quello che vuoi: 1: la parte "corretta & difficile", ovvero calcolare massa, inerzia ecc.. e vedere quando e con che forza iniziare a frenare 2: la parte "semplice", ovvero un algoritmo che autonomamente trovi dei valori più o meno corretti. Uno di questi (il più usato) è il PID. Nel tuo caso dai in input distanza e velocità e lui dovrebbe tirare fuori la nuova velocità, o qualcosa di simile (dipende da come implementi l'algoritmo). poi per settare bene i valori di output devi smanettare con i 3 valori P I D, magari usando dei potenziometri per far prima

poi per settare bene i valori di output devi smanettare con i 3 valori P I D, magari usando dei potenziometri per far prima

Oltre quei 3 valori (Proporzionale Intregativo e derivativo) c'è ne sono altri. Il fatto di usare dei potenziometri non l'ho capito o meglio non mi pare sia utile.

Ho utilizzato arduino per misurare il periodo degli impolsi dell'encoder, imponendo un duty cycle costante di 400, ogni valore viene sparato tramite seriale e con python ne ricavo una lista di valori in microsecondi.

I valori inizziali sono alti, diminuiscono e si stabbilizza nell'intorno di un periodo di 1 ms.

Quindi ricavo l'errore 1ms - periodo corrente, se periodo corrente e maggiore di 1ms l'encoder sta ruotando pià lentamente 1000us - 2000us = errore = -1000us ora devo aumentare il duty per ridurre l'errore a valori prossimi a zero.

Sarà così? Ciao.

nelle applicazione che ho VISTO (non programmato), gli unici valori che vengono modificati sono i pid, attraverso dei potenziometri per evitare di riprogrammare ogni volta l'arduino, ovvio che se mantieni il collegamento USB può essere più comodo modificarli via seriale.

occhio alla seriale! ha una velocità di invio che è fissata all'inizio, ed essendo una chiamata bloccante e lenta, può sballarti i valori letti (soprattutto la sottrazione tra millis), a meno che l'input e il calcolo non lo fai con interrupt, però a questo punto possono nascere problemi di parallelismo (esempio interrupt che cambia il dato mentre la serial ne ha già scritto parte..)

Perché misuri la velocitá del motore? non Ti basta misurare i passi? e regolare la velovitá del motore alla ceca senza retroazione? Ciao Uwe

Ciao Mauro! Interessante progetto. Sto proprio lavorando a un sistema che mi permette di avere un feedback rotativo dato da un encoder e uno elettrico dato da due porte analogiche, che connesse ai poli del motore, permettono di percepire la tensione nel circuito di alimentazione e quindi permette possibilità infinite :D. Avevo pensato anche io all'idea di smettere di alimentare il motore e utilizzarlo come dinamo è un'ottima idea. Guarda questa res che spiega il feedback su alimentazione applicato ai servomotori: http://www.gioblu.com/tutorials/azionamenti/170-modifica-rotazione-continua-microservi-parte-iii

Qui invece per il PID applicato al controllo di un robot selfbalancing molto semplice: http://www.gioblu.com/tutorials/programmazione/102-che-cose-lalgoritmo-pid (in realtà in questo tutorial è un PID a tempo variabile, tu lavori a tempo fisso?)

Spero possa esserti utile per la tua sperimentazione. Nel mio caso la cosa a cui sto lavorando è utilizzare nel PID, non solo il valore di inclinazione del robot, ma anche il feedback ottenuto con la modifica dei microservi, per migliorare la qualità della correzione e testare la differenza.

Nel tuo caso, se hai un encoder rotativo / di movimento preciso, basta quello credo, anche se è di certo interessante sperimentare il PID con piu' input.

@uwefed:

Perché misuri la velocitá del motore? …

Cioè dici perchè non provare senza retroazione?
Si penso proprio che volevi dire questo e infatti l’ho fatto.

#include "pins_arduino.h"

#define encoder0PinA 4  //PIND 0b00010000
#define encoder0PinB 5  //PIND 0b00100000

volatile long encoder0Pos = 0;

volatile unsigned int stateEncoder0PinA;
volatile unsigned int stateEncoder0PinB;

ISR(PCINT2_vect) 
{
    
    //PCint(0);
    // Pin Arduino 4 (PD4 PIN6 PCINT20), 5 (PD5 PIN11 PCINT21)
    // se lo stato di encoder0PinA è cambiato, registra lo stato corrente 
    // e chiama encoder0ChA()
    if ((PIND & 0b00010000) != stateEncoder0PinA) 
    //if (digitalRead(encoder0PinA) != stateEncoder0PinA) 
    {
        // misura il periodo della frequenza generata dall'encoder 
        stateEncoder0PinA = (PIND & 0b00010000);
        //stateEncoder0PinA = digitalRead(encoder0PinA);
        encoder0ChA();
    }
    // se lo stato di encoder0PinB è cambiato, registra lo stato corrente 
    // e chiama encoder0ChB()
    if ((PIND & 0b00100000) != stateEncoder0PinB) 
    //if (digitalRead(encoder0PinB) != stateEncoder0PinB) 
    {
        stateEncoder0PinB = (PIND & 0b00100000);
        //stateEncoder0PinB = digitalRead(encoder0PinB);
        encoder0ChB();
    }
}


void encoder0ChA()
{
    if (stateEncoder0PinA) { 

        // check channel B to see which way encoder is turning
        if (PIND & 0b00100000) 
        //if (digitalRead(encoder0PinB) == HIGH) 
        {  
            encoder0Pos--;         // CCW
        } 
        else 
        {
            encoder0Pos++;         // CW
        }
    }
    else
    { 
        // check channel B to see which way encoder is turning  
        if (PIND & 0b00100000) 
        //if (digitalRead(encoder0PinB) == HIGH) 
        {   
            encoder0Pos++;          // CW
        } 
        else 
        {
            encoder0Pos--;          // CCW
        }
    }
    
}

void encoder0ChB()
{
    // check state changed on channel B
    if (stateEncoder0PinB) 
    {   
        // check channel A to see which way encoder is turning
        if (PIND & 0b00010000) 
        //if (digitalRead(encoder0PinA) == HIGH) 
        {  
            encoder0Pos++;         // CW
        } 
        else 
        {
            encoder0Pos--;         // CCW
        }
    }
    else
    {
        // check channel A to see which way encoder is turning  
        if (PIND & 0b00010000) 
        //if (digitalRead(encoder0PinA) == HIGH) 
        {   
            encoder0Pos--;          // CCW
        } 
        else 
        {
            encoder0Pos++;          // CW
        }
    }
}

#define ledPin 13
#define pwmPin 9

void setup()
{
    // prescaler 8 credo?
    TCCR1B |= (1<<CS11);


    Serial.begin(115200);
    Serial.flush();
    pinMode(ledPin, OUTPUT);
    pinMode(encoder0PinA, INPUT);
    pinMode(encoder0PinB, INPUT);
    pinMode(2, OUTPUT);
    pinMode(3, OUTPUT);
    pinMode(pwmPin, OUTPUT);
    stateEncoder0PinA = (PIND & 0b00010000); 
    //stateEncoder0PinA = digitalRead(encoder0PinA);
    stateEncoder0PinB = (PIND & 0b00100000); 
    
    // PIN CHANGED ENABLED
    PCICR |= (1 << PCIE2);    // Abilita l'iterrupt su PORTD
    PCMSK2 |= (1 << PCINT20);    // Abbilita il vettore PCINT20 PD4 ARDUINO(4)
    PCMSK2 |= (1 << PCINT21);    // Abbilita il vettore PCINT21 PD5 ARDUINO(5)
    
    
    
}
int position;      
int byteCommand;
#define onWards 1
#define backWards -1
#define motorOff 0
#define motorOn 1
int startSlow;
int goMotor;
int motorPower = 0;
int dutyc;
void command(int comm) 
{
  
    if (comm == 'A') 
    {
        position = 0;
        startSlow = 200;
        goMotor = backWards;
        motorPower = motorOn;
        dutyc = 400;
        
    }
    if (comm == 'S') 
    {
        position = 5760;
        startSlow = position - 200;
        goMotor = onWards;
        motorPower = motorOn;
        dutyc = 400;
    }
}


void serialTx()
{
    //Serial.println(error); 
    //Serial.println(widthEncoder0PinA); 
    //Serial.println(dutycpluserror); 
}



void loop() {

    if ((encoder0Pos == 0) || (encoder0Pos == 5760)) 
    {
        digitalWrite(ledPin, HIGH);
    }
    else
    {
        digitalWrite(ledPin, LOW);
    }  
  
    if (encoder0Pos == position) 
    {
        digitalWrite(3, LOW);
        digitalWrite(2, LOW);
        analogWrite(pwmPin, 1023);
        delay(20);
        analogWrite(pwmPin, 0);
        motorPower = motorOff;
        
    } 
    
    if (motorPower == motorOn)
    {
        if (goMotor == onWards) 
        {   
            digitalWrite(3, HIGH);
            digitalWrite(2, LOW);
            analogWrite(pwmPin, dutyc);
        }  
        
        if (goMotor == backWards) 
        {    
            digitalWrite(2, HIGH);
            digitalWrite(3, LOW);
            analogWrite(pwmPin, dutyc);
        }
    }
    if (Serial.available() > 0) 
    {
        byteCommand = Serial.read();
        command(byteCommand);
    }  
//    if ((encoder0Pos < 5760) && (encoder0Pos > 0))
//    {
//        serialTx();
//    }
}

Difetti di questa soluzione:
Si ferma sempre dopo la posizione in cui azzero il duty cycle, Newton aveva ragione. :wink:
Il movimento non è fluido e la velocità non è costante, perchè c’è un’attrito volvente che non è costante (cinghia, boccole ecc)

@lesto:
Che bello un nick facile da ricordare.

occhio alla seriale! ha una velocità di invio che è fissata all’inizio, ed essendo una chiamata bloccante e lenta, può sballarti i valori letti (soprattutto la sottrazione tra millis), a meno che l’input e il calcolo non lo fai con interrupt, però a questo punto possono nascere problemi di parallelismo (esempio interrupt che cambia il dato mentre la serial ne ha già scritto parte…)

Quindi, come si può ovviare, ne avevo il sospetto ora come lo hai esposto tu mi ha dato la certezza, cioè non mi posso fidare, ho letto che le routin ISR interrompono in qualunque momento, con la possibilità molto frequente di rovinare i calcoli effettuati nel loop, in pratica sulle operazioni che non sono atomiche conviene spegnere l’interrupt “fai operazioni non atomiche” riaccendi interrupt.

@gbm:
Si si, pico l’ho già visto, ho seguito senza fiatare, è incredibile che con così poco codice riesca a stare in equilibrio.

Nel tuo caso, se hai un encoder rotativo / di movimento preciso, basta quello credo, anche se è di certo interessante sperimentare il PID con piu’ input.

Solo che un encoder da informazioni solo quando è in movimento, un potenziometro anche quando sta fermo.

[edit]Spero possa esserti utile per la tua sperimentazione.[/edit]
Si mi sarà di sicuro utile, ultimamente non ho più seguito lo sviluppo di pico, il problema che io sono uno a leggere e voi siete in centomila, ma se pò fà sta vita. :stuck_out_tongue:

Ok grazie per gli interventi che spero possano essere utili anche ad altri.

Ciao.

chiariamo un attimo le cose. le ISR sono interrupt, quinado avviene un'interrupt, il codice attuale vien bloccato e viene eseguito il codice dell'interrupt. l'unico problema che può dare è se l'interrupt modifica variabili "in comune" con loop o altri interrupt. (inconsistenza dei dati)

per ovviare alla velocità su seriale c'è poco da fare se non limitare le serial (meno caratteri e meno spesso possibile) inviando solo medie o facendo i controlli direttamente via arduino, oppure registrando i dati in array e inviandoli a fine "processo", ovvero quando lo schetch ha terminato il suo lavoro

@MauroTec

Il movimento non è fluido e la velocità non è costante, perchè c'è un'attrito volvente che non è costante (cinghia, boccole ecc)

Ho dei dubbi che l'atrito é cosi tanto variabile da influenzare il movimento del carello. Ciao Uwe

Ho dei dubbi che l'atrito é cosi tanto variabile da influenzare il movimento del carello. Ciao Uwe

Appunto, anche io non mi credevo che con un duty cycle costante ci fossero sti problemi, a quardare la stampante quando ancora funzionava (cioè prima dello sciacallaggio), questi problemi non si vedevano.

Per credo che le stampanti utilizino il controllo locked anti-phase, almeno in questa il carrello non puoi muoverlo con le mani. Le altre che ho cannibalizato non ricordo. Questa e una Dell 725.

Ho anche: Canon 4000 e qualcosa, motori dc ovunque Canon bjc anni 90, no motore DC, PP Lexmark z11, no motore DC, PP 2 HP multifunzione guaste che mi hanno regalato, motore DC ovunque Epson, parallela, motori PP.

Ok ciao.

ciao MauroTec

Io vedo che l'encoder di posizione ( in alcuni modell é un striscia tirata sopra il carello) serve "solo" per sapere quando sono da sparare le goccioline di inchiostro sulla carta. In questo modo non é necssario ne avere una velocitá al 100% costante ne tenere la velocitá costante con una regolazione a retroazione.

Come in pratica viene fatto non so dirlo.

Ciao Uwe

Io vedo che l'encoder di posizione ( in alcuni modell é un striscia tirata sopra il carello) serve "solo" per sapere quando sono da sparare le goccioline di inchiostro sulla carta.

Bingooo !! Proprio quel tipo ho collegato ad arduino tramite L293NE. Io alimento i motori con tensioni tra 12 e 18Volts ma l'alimentatore della stampante esce 30Vdc, nella scheda ci sono un ARM CR03, una GAL, e un chip made in china di cui è impossibile trovare datasheet.

Devo fare la prova ad alimentarlo a 24vdc.

Ah, sto studiando un pò, tra le cose interessanti ho trovato questa tesi: http://robot2.disp.uniroma2.it/~zack/tesisti/DeSimoneRoberto/TESI_ROBERTO_DE_SIMONE.pdf

Ciao.

Ho introdotto il PID usando la libreria PID_Beta6 che c'è nel playground e sembra funzionare, però è difficile scegliere i valori giusti per P e I.

Ho messo un video per mostrare le anomalie, che credo si possano risolvere usando la forma trapezoidale, solo che non so quale parametro fare variare per aumentare e ridurre la velocità.

Nel video si vede che il carrello si blocca nonostante la guida sia unta con olio al silicone.

Bloccando il carrello con la mano si sente aumentare progressivamente la forza, questo mi da la certezza che il controllo proporzionale sta lavorando, quanto bene però non lo so. http://www.youtube.com/watch?v=bn90bm4wsTI

Ciao Buone Feste.

ma perchè non usi semplicemente due rampe? cioè, se il carrello deve andare dal punto A al punto B e supponiamo che debba compiere 100 passi per esempio, puoi programmare la velocità come una rampa crescente fino a metà del percorso previsto (50 step) e poi invertire il trend fino a zero nella parte restante da percorrere. praricamente solo la P del P.I.D. ::) anche se magari l'hai già visto, comunque linko qui sotto la pagina di wiki che tratta la teoria del controllo e le tecniche usate a seconda dei campi di applicazione, magari interessa anche ad altri :) http://en.wikipedia.org/wiki/Control_theory ;)

ma perchè non usi semplicemente due rampe? cioè, se il carrello deve andare dal punto A al punto B e supponiamo che debba compiere 100 passi per esempio, puoi programmare la velocità come una rampa crescente fino a metà del percorso previsto (50 step) e poi invertire il trend fino a zero nella parte restante da percorrere.

Si quella si chiama onda triangolare e va benissimo per brevi spostamenti a velocità ridotta. Poi c'è l' onda trapezoidale inizia con l'accelerazione, passa a velocità costante, e inizia la decelerazione, fino al punto di posizionamento.

A prescindere dalla curva da scegliere, quale parametri devo modificare per ottenere accelerazione decelerazione sotto controllo proporzionale.

Non posso certo ridurre il valore del duty cycle fornito dal PID (variabile Output), perchè equivarrebbe a mettere un resistenza tra uscita e motore, perdendo la controreazione, quello che dovrei modificare è invece il guadagno, fino a portarlo a zero.

Grazie per il link, gli avevo dato una sbirciatina, ma c'è da leggere per giorni, ma la vera difficoltà è capire e passare alle vie di fatto.

Ora sto continuanto a studiare e al momento posso solo dire che i motori DC possono essere controllati in corrente (Amper) o in tensione (Volts). Dal momento che la coppia del motore è funzione della corrente, misurando la corrente nel micro posso tentare di mantenere la coppia costante.

Ok grazie, e alla prossima.

bè... wiki è enciclopedica :) questo è più riassuntivo http://newton.ex.ac.uk/teaching/CDHW/Feedback/ControlTypes.html e questo è per le vie di fatto http://abigmagnet.blogspot.com/2008/10/dc-motor-control-part-one.html spero che aiutino :)

Anche se siamo sotto le feste per me non è arrivata ancora l'ora di stappare la fatidica bottiglia di spumante.

Però sono comunque contento del risultato, il PID_Beta6 funziona, Eureka.

Ora chiedu consiglio a chi è più esperto di me con il software, perchè voglio settare il PID tramite seriale e aggi bisogno di Aiuto, Aiuto. :o

Dunque, un pò di codice:

void operativeMode(int comm) 
{
     if (comm == 'I') 
    {
        position = 0;
        startSlow = 50;
        goMotor = backWards;
        motorPower = motorOn;
        digitalWrite(2, HIGH);
        digitalWrite(3, LOW);
        
    }
    if (comm == 'A') 
    {
        position = 5760;
        startSlow = position - 50;
        goMotor = onWards;
        motorPower = motorOn;
        digitalWrite(3, HIGH);
        digitalWrite(2, LOW);
    }
    if (comm == 'V') printVersion();
    if (comm == 'S') mode = settings;
}

void settingsMode(int comm)
{
    if (comm == 'V') printVersion();      // print software version
    

    if (comm == 'X') mode = operative;    // Exit from settings mode, return to operative mode 
      
}

void loop() 
{
    if (Serial.available() > 0) 
    {
        delay(100);
        byteCommand = Serial.read();
        Serial.flush();
        if (mode == operative)
        {  
          operativeMode(byteCommand);
        }
        if (mode == settings)
        {  
          settingsMode(byteCommand);
        }
    }  
}

Di default la modalità è: operative Se invio tramite seriale 'S' passa alla modalità: settings In questa modalità devo inviare i valori di P, I, D, di tipo double. Questo lo devo fare con Python, come li devo inviare con pyserial e come interpretare i dati in arduino?

La prima idea è: P0.3\n I0.7\n D0.0\n

Si ma poi come li interpreto in arduino.

Nota che uso ancora la versione 18 di Arduino, e quindi non ho le String Object.

Rimane ancora da sistemare la partenza e la frenata dolce, comunque il fenomeno visto nel video non si verifica più, cioè non si blocca più in avvio.

Ok ciao.

Regalo di Anno Nuovo: Volevo postare qui il codice del controllore PID ma supera i 9500 caratteri, quindi dovrò metterlo nel playground.

Funziona non pare vero. Comandi in modalità operativa: g*value*: Si sposta fino alla posizione value v : Stampa la verisone del software S : Entra in modalità "settings"

Comandi in modalità settings: P*value* : Imposta il valore proporzionale del PID. I*value* : Imposta il valore Integrazione del PID. D*value* : Imposta il valore derivazione del PID. V*value* : Imposta la velocità massima. Z : Stampa le impostazioni correnti in modo comprensibile all'utente. z : Stampa le impostazioni correnti da interpretare via software. x : Esce dalla modalità "settings" e torna in modalità operativa. v : Stampa la versione del software. S : Salva le impostazioni in EEprom.

Da migliorare: Un migliore algoritmo per la decelerazione

Da fare: Interfaccia comandi tramite I2C e SPI. Controllo della corrente nel motore da integrare nel PID e usare come fine corsa. Un buon algoritmo per l'accelerazioni.

Chi volesse commentare migliorare estendere criticare o collaborare è il benvenuto.

ps: non c'è ancora nel playground ora ci vado a fare a botte con il playground, ma in che sezione dovrei metterlo Input/Output ?. Ciao.