Trenovelox

Ciao a tutti…
In anticipo vi ringrazio per la collaborazione…e visto che il post rischia di diventare piuttosto lungo, passo subito all’argomento.
Il progetto che sto realizzando è un “trenovelox” per ferromodellismo. Cosa significa: misurare la velocità del treni sul prastico e visualizzare la stessa in Km/h rispettando la scala.

Come è realizzato il tutto:
ATMEGA328P
3x2 display 7 segmenti
2 fotoresistenze.
più altre varie per poter selezionare la scala tra HO ed N (1:87 - 1:60).

Il tutto funziona, il codice di base è scritto, ma in fase di test sto verificando un fastidioso problema che non riesco a capire.
I test sono eseguiti su un ovale di circa 3.5 metri, e prendendo i tempi a mano sul giro, ed utilizzando il “treno-velox” [TV], vedo una differenza tra la velocità calcolata da me e la velocità presa dal dispositivo.

Premetto che TV (TrenoVelox) ad ogni giro del trenino continua ad indicarmi la stessa velocità (con una variazione di 1-2 Km/h).
La differenza tra TV e la misurazione manuale aumenta non linearmente con il diminuire della velocità.

Visto che sono piuttosto sicuro che il problema sia legato al sw scritto, riporto ancora qualche dettaglio:

  1. lettura fotoresistenze:
    “ogni tanto” nel codice viene misurata la luce ambiente attraverso una media di 20 letture consecutive, poi alla media viene sottratto il 5% e questo valore diventa la soglia che mi fa capire se il treno è in transito davanti a una fotocellula o mano.
    2)angolo lettura fotoresistenze:
    per ridurre l’angolo di “eccitamento” è stata applicata davanti alle fotoresistenze una maschera in materiale platisco di 1cm forata. Ripeto che lasciando girare il treno costantemente, la velocità riportata rimane stabile (o comunque oscilla di pochi Km/h).
  2. la distanza tra le fotoresistenze è di circa 6.2cm. Questo implica che il tempo di lettura tra l’apertura del cancello e la chiusura passa un tempo compreso tra 2secondi e 0.1 secondi.

La lettura delle fotoresistenze è legata una un trigger scatenato dal timer1. …e credo che il maggiordomo in questa storia sia proprio il timer1 (intendo dire: penso che il colpevole sia lui!).
Il timer è configurato come:

cli();//stop interrupts

			//set timer1 interrupt at 1kHz
			TCCR1A = 0;// set entire TCCR1A register to 0
			TCCR1B = 0;// same for TCCR1B
			TCNT1  = 0;
			// set timer count for 1khz increments
			OCR1A = 1999;// = (1/1000) / ((1/(16*10^6))*8) - 1
			// turn on CTC mode
			TCCR1B |= (1 << WGM12);
			// Set CS11 bit for 8 prescaler
			TCCR1B |= (1 << CS11);   
			// enable timer compare interrupt
			TIMSK1 |= (1 << OCIE1A);
  
			sei();//allow interrupts
			//END TIMER SETUP

Confesso che la parte legata alla configurazione del timer è la parte che ho compreso meno, nonostante l’aver letto diversi tutorial…
In particolare: la tc dell’atmega 328p, è correttamente settata? (8MHz)
Il timer configurato in questo modo, sta effettivamente contanto i microsecondi?

Riporto per completezza il codice eseguito dal trigger:

ISR(TIMER1_COMPA_vect) {
	if (iWaitDsp>0 )
     {
                iWaitDsp--;												//se sto ancora scrivendo il valore precedente esco.
				if (iWaitDsp ==1)										//ad ogni nuova lettura, aggiorno la scala e il delta dei sensori
				{
					 UpdateScale();
					 GetRange();
				}
                return;
     }
     int iD1 =analogRead(read1);										//leggo i valori della fotocellula 1
     int iD2 =analogRead(read2);										//leggo i valori della fotocellula 2
     if (iD1<iRange1 || iD2<iRange2)									//se uno dei valori letti è inveriore alla soglia
     {
        if (iGate==-1)													//se il cancello è chiuso, lo apro impostando alla variabile il valore del sensore inferiore alla soglia
        {
          if(iD1<iRange1)
          {
            iGate = 1;
          }
          else
         { 
            iGate=2;
          }
          ulTime = iTimer;													//assegno alla variabile ulTime il valore dei millisecondi letto a inizio funzione
          return;
        }
        if (iGate ==1 && iD2 < iRange2)										//se il cancello è aperto e il sensore 2 è sotto la soglia, chiudo il cancello
        {
          ulTime = iTimer-ulTime;											//calcolo il delta tra i tempi
          iGate = -1;														//chiudo il cancelo
          iWaitDsp=iNextMeasure;											//imposto una pausa di 20secondi per la lettura del prossimo sensore
          return;
        }
        if (iGate ==2 && iD1 < iRange1)										//se il cancello è aperto e il sensore 2 è sotto la soglia, chiudo il cancello
        {
          ulTime = iTimer-ulTime;											//calcolo il delta tra i tempi
          iGate = -1;														//chiudo il cancelo
          iWaitDsp=iNextMeasure;											//imposto una pausa di 20secondi per la lettura del prossimo sensore
          return;
        }
     }
	 if(iGate != -1)														//se il ca
	 {
		if (timer > iMaxTime){												//se sono passati troppi secondi, abbiamo perso la chiusura
			ulTime = iMaxTime;
		}
		else{
			iTimer += 1;													//incremento il tempo
		}	 
	 }
	 else iTimer =0;														//se il cancello è chiuso, tengo fermo il tempo.
}

Il significato del codice sopra dovrebbe essere qualcosa del tipo:

  1. se ho appena misurato una velocità, lascio il tempo al display di mostrarla (funzione scritta all’interno del mainloop), e nel frattempo aggiorno i valori ambientali e leggo lo switch della scala;
    2)se una delle due fotoresistenze riporta valore inferiore alla soglia, cerco di capire se si sta aprendo o chiudendo il cancello di lettura ed opero di conseguenza.
  2. se il cancello è stato chiuso, azzero il timer, altrimenti lo incremento.[/i]

Nel mainLoop se il valore di iGate è = -1 viene visualizzata la velocità calcolata in base al valore delta scritto all’interno la variabile ulTime.

Il fattore di errore che sto rilevando è compreso tra 1.2 e 1.5.

Qualcuno riesce ad intravedere il problema??

Ringrazio in anticipo…
Ciao a tutti…

Solo un suggerimento "meccanico" ... davanti alle fotocellule, al posto della "maschera", usa un tubetto di plastica nera lungo almeno 1 o 2 cm, le renderai molto meno sensibili alla luce esterna (ovviamente tutta la fotocellula deve stare nel tubetto, e chiusa anche dietro) ... poi dipende daòl diametro della tua fotocellula, ma se e' piu larga di 5mm, puoi mettere in fondo al tubetto una mascherina con un foro da 4 o 5 mm, se invece e' piccola, basta il tubetto dello stesso diametro della fotocellula (va bene anche un pezzo di quelle cannucce da bar nere larghe, se non trovi di meglio)

Cosa usi come fonte di luce, dei led ?

Fotoresistenze non vanno bene per questo scopo, sono troppo lente nella risposta. devi usare dei Fototransistori. Ciao Uwe

Grazie per il consiglio. Come detto, il problema non penso sia legato all'angolo di lettura delle fotoresistenze. Se così fosse, le variazioni sarebbero maggiori considerando la scala e quindi le letture di TV al passaggio costante del treno sarebbero cangianti.

Non sto illuminando le fotoresistenze con alcuna fonte di luce esterna, rilevando la luce ambientale e usando questa come soglia non perde un passaggio. Le fotoresistenze sono all'interno di una scatola, quindi non ricevono alcuna luce se non dal foro del materiale plastico. I fori su questa maschera sono esattamente del diametro della fotoresistenza.

Grazie ancora, ciao

Allora probabilmente, come dice uwe, la fotoresistenza e' troppo lenta nella reazione ... tieni presente che per calcolare la velocita', lavori in millisecondi (anche in microsecondi, se serve precisione), e che le fotoresistenze in genere hanno un ritardo di risposta di circa 50 millisecondi da buio a luce (possono andare da 35 a 90), e di circa 10 millisecondi da luce a buio ... questo e' il "ritardo" nella risposta, poi la curva di salita e discesa e' ancora peggio (quelle "standard" da 5mm con 1Mohm di resistenza al buio, possono metterci fino a 5 secondi dalla rimozione della luce, per raggiungere il megaohm :P), quindi ti possono tranquillamente sballare qualsiasi lettura di quel tipo ...

Un fototransistor e' migliaia di volte piu veloce (milioni, per alcuni modelli ;) ), quindi decisamente piu preciso ...

proverò con i fototransistor... grazie per la segnalazione. Il timer lo vedere settato correttamente? Sapete mica indicarmi come eventualmente correggerlo per avere i microsecondi e una tempo massimo di 3 secondi?

Grazie ancora.

Non potresti fare un qualcosina di più semplice? 2 barriere ad infrarossi a distanza nota e calcolo del tempo di passaggio tra la prima e seconda barriera. Potresti usare i pin 2 e 3 della UNO per gli interrupt e il contatore microseconds() per contare il tempo.

...mi pare di capire che il problema non sia la semsibilità delle fotoresistenze ma la loro velocità. Non so se passando a sensori infrarosso la velocità aumenterebbe. In ogni modo il problema è che non sempre posso mettere dei led al di la dei binari...quindi l'usare la luce ambiente come riferimento mi semplifica le cose. Grazie

Tornando al dubbio sulla configurazione del timer, tu per cosa lo usi? Che cosa vorresti che facesse? A quanti MHz lavora l'Atmega328?

bella domanda:
io vorrei che l’interrupt venisse scatenato ogni millesimo di secondo.
atmega328P viaggia a 16MHz…ma non ho mai capito per quale motivo non viene mai indicata la frequenza massima negli esempi che ho visto…

prendete questo ultimo post…con le pinze…

E con l'interrupt che vorresti farci? Potresti semplicemente fare così: prescaler a 1, TCNT1 a 49536 (che ricarichi ad ogni overflow del timer 1). Modalità normale. Intervallo di 1ms.

il body dell'interrupt è quello riportato all'inizio del thread. In sostanza ad ogni millisecondo vado a leggere il valore delle fotoresistenze.

Quanto mi scrivi è interessante (per comprendere l'uso dei timer). come hai calcolato il valore massimo per l'overflow? Grazie, ciao

Facendo 2 calcoli. Sulla modalità normale, il timer va in overflow ogni Fclk/prescaler/registro dove Fclk è la frequenza di clock, quindi 16 MHz. Prescaler l'ho impostato ad 1, essendo di base il timer 1 un timer a 16 bit, e registro è il valore max del registro, con 16 bit abbiamo 65535 valori. Ora, per ottenere 1 ms dobbiamo fare un altro conto, ossia ridurre il numero di incrementi, quindi dare un valore di partenza al registro diverso da 0, in modo che per arrivare al valore max ci impieghi meno tempo.

Quindi, (2^16)-(16000000*intervallo/prescaler): Dove intervallo è 0,001 s (1 ms) e prescaler è 1. Da cui si ottiene: 65536-16000 = 49536

Quindi, prima di riavviare il timer, tu metti TCNT1 a 49536, e poi dentro alla ISR, prima dell'uscita, rimetti sempre TCNT1 a 49536.

fnicora: ... non sempre posso mettere dei led al di la dei binari ...

Esistono anche dei sensori a riflessione, con il led dalla stessa parte del fototransistor ;) ... puoi realizzarli con un fototransistor ed un led ad infrarossi, anche da 3mm, ma ci sono anche gia fatti, pure SMD se ti servono piccoli (solo come esempio, http://www.robotstore.it/open2b/var/catalog/images/6/0-2cb8630e-300.jpg ) ... potresti addirittura montarli al di sotto, in mezzo alle traversine, che ti sentono il passaggio del treno quando gli passa sopra (in questo modo, si puo anche piazzare del riflettente sotto la motrice, se non fosse abbastanza sensibile, cosa che a lato non potresti fare per questioni estetiche) ... e se non li trovi in negozio, si possono sempre recuperare da qualche vecchio fax o stampante defunti, tanto per sperimentare ;)

Per quanto riguarda la velocita' di risposta, direi che passare da qualche decina di millisecondi a qualche decina di nanosecondi ,incrementerebbe parecchio la precisione ;) :fearful: