2D ERER range scanner imaging

Ciao ragazzi! Dopo aver visto il robottino di Alegiaco (gioblu.com is for sale | HugeDomains) mi è venuta voglia di utilizzare il sistema di Tilt/Pan.
Alla fine ho avuto una sorta di illuminazione e in una notte ho realizzato questo sistema. Niente di poi tanto complesso in effetti:

Grazie al nostro sistema ERER eseguo uno scan dell'ambiente rappresentando le letture di distanza ottenute con pixel in scala di grigi:

  • Pixel bianco distanza minima osservabile (0.5 / 1 cm)
  • Pixel nero distanza massima o out of range (2m, ma si puo' raggiungere fino a 5 o 6 metri, chiaramente perdendo risoluzione)

Il sistema di tilt/pan sposta il sensore in x y e Arduino si occupa di pilotare i LED, calcolare il risultato e spedirlo al computer tramite seriale. Grazie a Processing disegno pixel per pixel trasformando il valore di distanza tra 0 e 255 (0 bianco 255 nero), posizionandoli correttamente. Come nell'occhio umano l'immagine viene acquisita ribaltata, quindi va successivamente girata :grin: cosa che il nostro cervallo fa in real time a 30fps (magari mentre pilotiamo una moto :grin:).

Un altro esempio:

Sto scrivendo l'articolo, entro oggi pomeriggio potrete vedere il codice e tutto il resto. Il sensore è il nostro ERER!!
Due semplici led IR racchiusi da un case appositamente studiato per ridurre le interferenze di luce ambientale e massimizzare la luce riflessa all'interno del ricevitore (purtroppo è studiato per essere un range sensor e non per ricevere solo i raggi perpendicolari alla superficie sensibile del ricevitore, come fa una qualsiasi macchina fotografica, per questo l'immagine è sfocata e di qualità ridotta).

Secondo voi come si puo' migliorare la resa del sistema??
Usando un laser?
Avvicinando emettitore e ricevitore?

Sarebbe carino avere centinaia di Ghz per poter usare un solo led e calcolare con la velocità della luce la distanza, purtroppo siamo limitati a 16mhz e ci accontentiamo :grin:
Qui topic correlato HugeDomains.com

Una buona risoluzione credo possa essere ottenuta con gli ultrasuoni, ho visto qualche sonar con arduino in giro e l'immagine veniva creata con processing.
http://hacknmod.com/hack/make-a-diy-sonar-system-using-arduino/

beh che dire? davvero complimenti!!! considerato il costo contenuto dei due sensori direi che il risultato ottenuto è già parecchio buono! hehe io la chiamerei cheap vision!

Si riesce già a distinguere abbastanza nettamente gli oggetti, una curiosità: quanto ci mette a fare una scansione completa?

aggiungo il mio interesse: sarebbe bello un sistema simile per magari un visore notturno da pochi euro :wink:

Beh diciamo che non è un razzo ad effettuare una lettura completa!! Pero' devo dire che non ho visto niente di cosi' "risoluto" fatto con 2 microservi da pochi euro e due LED in effetti :grin: :grin:

Semplicemente Fantastico Complimenti!!!!!!!
Due targhet centrati in pieno: divertirsi e risparmiare!!!!!

PS hai provato ad invertire i colori!!Secondo me l'immagine diventa ancora più bella!!

credo che per avere un'immagine postelaborabile bisognerebbe provare con due piccoli stepper per fare il pan tilt, inquanto hanno uno step più piccolo dei servi che invece vanno ad un grado per volta.
poi co l'immagine così ottenuta si potrebbe provare a arla in pasto a qualche plugin di gimp tipo deblur e despecle, e vedereche ne esce :stuck_out_tongue:
se funziona si può pensare di farlo fare a processing direttamente.

secondo me per evitare l'effetto "lente":
usa le coordinate sferiche! Tu hai le due angolazioni e la distanza ovvero:
distanza = ?
angolo pitch = ? = servo su-giù
angolo yaw = ? = servo destra-sinistra

trasformi gli angoli in radianti (moltiplichi l'angolo per PI/180), e poi per ottenere la posizione precisa X Y Z usi le formule:


dove z e x sono la posizione nell'immagine, e y è la distanza reale (ovvero senza effetto distorsione sferica)

maggiori info: Sistema di coordinate polari - Wikipedia

che dite, promosso?

ps. dovresti notare che sia i bordi sia i "pixel" non saranno più così ben delimitati, ma è tutto dovuto alla "desferificazione" dell'immagine

Scusate il ritardo ragazzi, sto sviluppando un software eseguibile che vi permetterà di salvare l'immagine ottenuta (è stato un incubo) e vi permetterà di selezionare comport e baudrate!! :grin: Si chiamerà image scanner 1.0. Lesto io non sono un matematico quindi mi devo dare un occhio a cio' che dici ma credo che salvando in un txt tutti i valori poi con Processing senza troppi problemi posso ricostruire l'immagine variandola secondo l'algoritmo che hai proposto in modo dinamico.

se per ogni pixel salvi le inclinazioni dei servi e il valore di distanza il tutto si può fare in processing.

evvai son felice come una pasqua!!!
oggi son a fare acquiti, o preso 2 coppie di led ir tx(trasmittente) e rx(ricevente), e visto che avevano finito le rx altri 4 tx...
così posso mettere a confronto la qualità delle tx usate come rx contro le reali rx.

sui led ricevitori, e stampigliata una scritta, 817v19 bpw41n, stasera cerco qualche datasheet corrispondente.

ora però mi sorgono dei dubbi. ovviamente i led tx usati come tx avranno bisogno di una resistenza (ho preso quelle da 2.2kOhm), ma le tx usate come tx o le rx hanno bisogno di resistenza?

stasera mi butto nella costruzione e nei test, gbm se mi posti il codice processing mi fai un favore, per quello arduino pensavo di usare il puro interrupt per i test di misurazione di distanza, usare la pulseln() per l'acquisizione di mappe di profondità con un sola coppia tx/rx (usando lato processing le formule descritte) e infine ricreare una pulseln() multipin(per supportare più led tx/rx), che però avrà lo sbattimento che rispetto al pin singolo, il centro di fuoco non ruota e basta, ma trasla (o almeno, in modo più evidente)... pero dovrebbe essere risolvibile, magari sfruttando qualche matrice di rototraslazione (unisco utile al dilettevole, così mi preparo l'esame di geometria e algebra lineare)

Ciao Lesto, scusa il ritardo nella risposta.
Il sistema ERER utilizza due normali LED ir come emettitore e ricevitore. L'emettitore ha il wiring di un normale LED, il ricevitore va connesso con il - a una ditigale e il + a GND. Per il codice ti consiglio di partire da questo aggiungendo gli interrupt visto che tu sai usarli bene:

#include <Servo.h> 
Servo tiltservo; 
Servo panservo;  
 
int hometilt = 80;
int homepan = 5;
int tiltvalue = 25;
int panvalue = 20;
// SENSORE ERER
#define irEmitter 13   // polo positivo emettitore - positive emitter pin
#define irReceiverN 3  // polo negativo ricevitore - negative receiver pin
volatile long ambient = 0;
volatile long emission = 0;
volatile long distance = 0;
volatile long lightTime = 0;
volatile int currentInit = 0;
volatile long gain = 4;       //gain crescita valore distanza
volatile long maxRangeValue = 666666;  //valore range massimo
volatile long maxRange = 2000; //distanza range massimo in mm
int advisor = 0;
int index = 0;
int mode = 1;




 
void setup() { 
  tiltservo.attach(9);
  panservo.attach(10);
  Serial.begin(115200);
} 

///////////////////////////SENSORE ERER////////////////////////////////////////
void focus() { 
 if(currentInit == 0){if(digitalRead(irReceiverN) == LOW){ ambientLightCheck();}}
 if(currentInit == 1){if(digitalRead(irReceiverN) == LOW){ reflectionLightCheck();}}
}

void ambientLightCheck() {
 ambient = micros() - lightTime;
 init(1);
}

void reflectionLightCheck() {
 emission = micros() - lightTime;
 distance = emission * (ambient / (ambient - emission));
 distance = map(distance, 0, maxRangeValue, 0, maxRange);
 if(distance <= 0) distance = 0;
 if(distance > 15) distance = sqrt(distance) * gain;
 advisor++;
 emission = 0; 
 ambient = 0;
 init(0);
}

void init(int emitter) {
 if(emitter == 0)digitalWrite(irEmitter, LOW);   
 if(emitter == 1)digitalWrite(irEmitter, HIGH); 
 lightTime = micros();
 pinMode(irReceiverN, OUTPUT);
 digitalWrite(irReceiverN, HIGH); //carico ricevitore di induttanza
 pinMode(irReceiverN, INPUT);
 digitalWrite(irReceiverN, LOW);
 if(emitter == 1) currentInit = 1;
 if(emitter == 0) currentInit = 0;
}
//////////////////////////////LOOP////////////////////////////////////////////////

void loop() { 
    focus();
    tiltservo.write(tiltvalue); // 125 // 35
    panservo.write(panvalue);
    if(advisor == 1) { 
    Serial.print(distance); 
    Serial.print("/"); 
    Serial.print(panvalue); 
    Serial.print("/"); 
    Serial.print(index);  
    Serial.print("/"); 
    Serial.println(mode);
    if(mode == 1){ tiltvalue = tiltvalue + 1;  index++; } 
    if(mode == 0){ tiltvalue = tiltvalue - 1;  index--;} 
    advisor = 0;
    if(index == 0 && mode == 0) { mode = 1; panvalue++;  panservo.write(panvalue); } 
    if(index == 109) { mode = 0; panvalue++;  panservo.write(panvalue); }
  }
}

max range value è il valore che ottieni a massimo range con distance senza trasformazioni

ecco il file che contiene sorgenti ed eseguibile di Image Scanner 0.1 x linux 32
http://www.gioblu.com/software/image-scanner-linux32.tar.gz

sei fantastico, ora mi metto subito al lavoro :slight_smile:
ho scoperto che le RX che mi hanno venduto sono dei fotodiodi, ma dal datasheet http://docs.google.com/viewer?a=v&q=cache:o3-bCeSSlUoJ:www.bucek.name/pdf/bpw41.pdf+bpw41n&hl=en&pid=bl&srcid=ADGEEShhhrNuq_K1mKCOQ_6YMPhrVA6vLpZJBd6PBMPz2um4uEx2jKHpSJhq-iYQctYJ6jMq3srwPh_A1fxvcdc459seMq2-YPN8uuxtTFT7scQH_3EFMMJjpc0psJKCDOCPUXVLZJvC&sig=AHIEtbTZUimXL94R0Fjm-y4N4tsTkB51yw non riesco a individuare l'anodo e il catodo, i piedini son pure lunghi uguali...
vabbè inizio con i led, ti faccio sapere

Testa alla luce il fotodiodo e troverai il +, alla fine sono piccoli pannelli solari :grin:
(se drawer viene aperto come foglio di testo, tasto destro apri con aggiungi "sh" e dovrebbe funzionare :grin:

allora, dopo essere impazzito un poco con i led, ho scoperto che la mia lampada emette un sacco di IR!

ho fatto un piccolo sketch che conta i millisecondi (tramite interrupt) impiegati dal ricevitore a diventare LOW...
la distanza dei due led (non ho usato i fotodiodi per ora) è di 4cm

letture senza lampada, la f significa che il trasmettitore è spento, la t che è acceso, il valore il tempo a diventare LOW in microsecondi:

f 5838804
t 4678612
f 5858260
t 4677588
f 5859284
t 4697044
f 5816276
t 4698068

i valori sembrano abbastanza stabili.

Ora stessa cosa con lampada accesa:

f 47068
t 52888
f 47684
t 53196
f 51696
t 48220
f 52460
t 47448

:astonished: :astonished: :astonished: :astonished:
uhm, non saranno pericolosi gli infrarossi, ma una dose così massiccia.. io cambio lampada per sicurezza :grin:

edit: usando un separé tra i due led (ovviamente lampada spenta), si notano nelle ultime letture che tolgo il separé e i valori si ristabiliscono

f 4978644
t 4839380
f 4998100
t 4836308
f 4976596
t 4396592
f 5638520
t 4417104
f 5278044
t 4437972
f 5278676
t 4419540

edit2: mi dimenticavo il codice, ancora embrionale... purtroppo non ho ancora i servo (me li devo far portare da mio fratello), altrimenti ero già a provare il codice di gbm :slight_smile:
per farmi perdonare ho stracommentato il codice

int ricevitore = 2;
int emettitore = 9;

void setup(){
Serial.begin(115200);
pinMode(ricevitore, INPUT);
pinMode(emettitore, OUTPUT);
delay(1000);
}

unsigned long ris;
boolean statoEmettitore=true;
void loop(){
//accende e spegne il led emettitore, e ne stampa lo stato
if (statoEmettitore){
statoEmettitore=false;
Serial.print("f ");
}
else{
statoEmettitore=true;
Serial.print("t ");
}
digitalWrite(emettitore, statoEmettitore);

//legge la durata del segnale HIGH sul ricevitore e la stampa a video
Serial.println( durataHigh() );
}

//FUNZIONE DI LETTURA BLOCCANTE
//per implementare un time-out basta modificare il while
//non è gestito l'overflow di micro()
unsigned long timeHigh;
volatile unsigned long timeLow;

unsigned long durataHigh(){
timeHigh=micros();
timeLow=0;//inizializza la lettura a un valore noto, che fungerà da "tappo" per il while
digitalWrite(ricevitore, HIGH);//attiva pullUP, per caricare il ricevitore
attachInterrupt(0, tempoLow, FALLING);//vogliamo intercettare quando il digitalPin2 diverrà LOW ("cade" il segnale da 1 a 0)
digitalWrite(ricevitore, LOW);//disattiva pullUP, il ricevitore è carico

//attende che la lettura sia completata
while ( timeLow==0 )//finchè c'è il valore di tappo (il ricevitore è ancora HIGH)
;//non fare niente

detachInterrupt(0);
return timeLow-timeHigh;
}

//FUNZIONE DI INTERRUPT
//più minimale di così non mi viene :slight_smile:
void tempoLow(){
timeLow=micros();
}

Ciao Lesto mi fa piacere che ti sia interessato a questo sistema, vediamo cosa ne viene fuori.
Se hai tempo prova questo:

// SENSORE ERER
#define irEmitter 13 // polo positivo emettitore - positive emitter pin
#define irReceiverN 3 // polo negativo ricevitore - negative receiver pin
volatile long ambient = 0;
volatile long emission = 0;
volatile long distance = 0;
volatile long lightTime = 0;
volatile int currentInit = 0;
volatile long gain = 4; //gain crescita valore distanza
volatile long maxRangeValue = 666666; //valore range massimo
volatile long maxRange = 2000; //distanza range massimo in mm

void setup() {
Serial.begin(115200);
}

///////////////////////////SENSORE ERER////////////////////////////////////////
void focus() {
if(currentInit == 0){if(digitalRead(irReceiverN) == LOW){ ambientLightCheck();}}
if(currentInit == 1){if(digitalRead(irReceiverN) == LOW){ reflectionLightCheck();}}
}

void ambientLightCheck() {
ambient = micros() - lightTime;
init(1);
}

void reflectionLightCheck() {
emission = micros() - lightTime;
distance = emission * (ambient / (ambient - emission));
distance = map(distance, 0, maxRangeValue, 0, maxRange);
if(distance <= 0) distance = 0;
if(distance > 15) distance = sqrt(distance) * gain;
Serial.print("/ distance /");
Serial.print(distance);
Serial.print("/ ambient /");
Serial.print(ambient);
Serial.print("/ emission /");
Serial.print(emission);
Serial.print("/ ambient / (ambient - emission)/ ");
Serial.println(ambient / (ambient - emission));
emission = 0;
ambient = 0;
init(0);
}

void init(int emitter) {
if(emitter == 0)digitalWrite(irEmitter, LOW);
if(emitter == 1)digitalWrite(irEmitter, HIGH);
lightTime = micros();
pinMode(irReceiverN, OUTPUT);
digitalWrite(irReceiverN, HIGH); //carico ricevitore di induttanza
pinMode(irReceiverN, INPUT);
digitalWrite(irReceiverN, LOW);
if(emitter == 1) currentInit = 1;
if(emitter == 0) currentInit = 0;
}
//////////////////////////////LOOP////////////////////////////////////////////////

void loop() {
focus();
}

E il massimo dove sono arrivato, sono fermo qui...senza sole diretto, ti da la distanza in cm abbastanza precisa dopo averlo settato con i tre valori appositi..
La formula di base che trasforma le due letture, a emettitore spento e acceso nella distanza in mm, è questa:
distance = emission * (ambient / (ambient - emission));
if(distance > 15) distance = sqrt(distance) * gain;
che dovrebbe consistere in:
distanza in mm = tempo scarica con tx on * (tempo scarica tx off / tempo scarica tx off - tempo scarica tx on)
se superiore a 15mm radice quadrata della distanza * gain
(Questo valore credo sia dato dal cono di emissione / ricezione dei LED)
Se devo essere sincero come gia vi dicevo nei primi post, l'ho trovata facendo prove pseudo-random del posizionamento dei termini. Ogni tanto serve :grin:. Funziona piuttosto bene perchè hai 3 termini con cui regolare l'output, che sono maxRange maxRangeValue e gain, in poche parole quello che fai per settare il sistema è inserire in maxRangeValue il valore di distance che ottieni al massimo range percepito dal sistema (commentando le trasformazioni di distance) e poi misuri la distanza effettiva tra l'oggetto e il sensore e la inserisci in maxRange, gain serve per regolare la crescita del valore di distanza. Non capisco come mai, ma funziona piuttosto bene, magari tu capisci come mai :grin:
Come puoi vedere dai prints, l'ultimo valore cioè: ambient / (ambient - emission) puo' tornare davvero utile, perchè sembra che possa essere considerato come indice di attendibilità del valore di distanza, infatti se lo punto a un oggetto a + o - 2m ottengo un valore vicino a 10, se punto oltre i 2 metri ottengo valori o negativi o molto superiori a 10. Una semplice if(ambient / (ambient - emission) < 10 && ambient / (ambient - emission) > 0) .... puo' eliminare tutti i valori oscillanti che si ottengono se si punta oltre il range massimo. Al buio totale sente il palazzo di fianco dalla finestra di casa mia :grin:...
Avevo quasi finito ERER tester, un programmino grafico per mostrare le varie parti dell'equazione e rapportare le 4 curve, distanza, lettura ambientale, lettura con tx e ambient / (ambient - emission), si è inchiodato l'eseguibile e io ho intelligentemente killato processing senza aver salvato. Ora dormo e domani lo rifaccio =(