Problema con sensore ad ultrasuoni HC-SRO4, robot per movimento autonomo.

Salve a tutti, vado subito al punto:

Ho realizzato un robot il cui principale scopo è quello di muoversi autonomamente in un ambiente domestico, per fare questo ho deciso di acquistare dei sensori ad ultrasuoni HC-SRO4 (per capirci a 4 pin, vcc, gnd, echo, trig).

L'idea (già abbastanza collaudata) è stata quella di montare al centro del robot un sensore sopra di un servomotore che è in grado di compiere i 180 gradi, quindi, nel momento in cui il sensore trova un ostacolo di fronte a sè si "volta" a destra e a sinistra e prende una decisione sul dove dirigersi, un'altro sensore fisso è posto più in basso e serve per rilevare ostacoli più bassi che il sensore "principale" non rileverebbe.

Fin qua tutto bene, il problema è che spesso il robot si ferma e cerca una nuova strada senza che però ci sia nessun ostacolo di fronte a lui, infatti ho verificato dal seriale che i sensori spesso registrano dei "falsi zero", ovvero il valore si abbassa sotto la "soglia di allerta" o addirittura scende a 0 anche se non c'è nessun oggetto realmente vicino.

Non riesco a capire se si tratti di un problema hardware o software, l'elettronica è assemblata cosi: una power bank alimenta a 2.1 amp una Raspberry pi 3 model B (che mi serve, almeno per ora, solo a tenere sotto controllo da remoto il seriale), la Rpi alimenta a sua volta Arduino uno via USB, che si occupa di tutto il lavoro. I motori sono controllati sempre da Arduino tramite un l293n, alimentato esternamente a 12 volt (8 pile stilo per capirci)

Ho già pensato più volte di cambiare approccio: visto che ho comprato 5 di questi sensori potrei pensare di disporli "a pentagono" intorno al robot (che è circolare) ed evitare il discorso servomotore e quant' altro, mi converrebbe? o è meglio rimanere con questo sistema?
In entrambi i casi avrei il problema degli ostacoli fantasma che i sensori mi rilevano, quindi devo risolverlo per qualche verso.

Qui vi posto il codice che sto usando attualmente, mi piacerebbe anche avere un opinione su come è scritto, e ovviamente sapere se il problema sta proprio lì. Per i sensori uso la libreria NewPing che mi è sembrata la più affidabile.

#include <NewPing.h>
#include <Servo.h>

#define TRIGGER_PIN  10  
#define ECHO_PIN     11  
#define TRIGGER_PIN1  12  
#define ECHO_PIN1     13  
#define MAX_DISTANCE 200 

int dist = 0;
int dist1 = 0;
int DistanzaDestra = 0;
int DistanzaSinistra = 0;

Servo servo;



NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); 
NewPing sonar1(TRIGGER_PIN1, ECHO_PIN1, MAX_DISTANCE); 

void setup() {
  Serial.begin(115200); 
  /*Pin di controllo dei motori*/
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(7, OUTPUT);
  servo.attach(5);
  /*Il sensore guarda davanti*/
  servo.write(90);
  delay(700);
}

void loop() {
  Avanti();
  dist = (sonar.ping_cm());  //Calcola la distanza di fronte al robot (sensore alto)
  dist1 = (sonar1.ping_cm());//Calcola la distanza di fronte al robot (sensore basso)
  Serial.print(dist);
  Serial.print("||");
  Serial.println(dist1);
  Serial.flush();
  delay(500);
  
/*If principale*/
  if(dist > 10)  //Distanza di allerta        
  {
    if (dist1 > 10){
    Avanti();
    }
    else {
      /*Giochetti col seriale*/
     Serial.println("Rilevato ostacolo sul percorso");
     Serial.print("Calcolo");
     delay(100);
     Serial.print(".");
     delay(100);
     Serial.print(".");
     delay(100);
     Serial.print(".");
      TrovaStrada();
    }
    }
  if(dist <= 10)
  {   /*Giochetti col seriale2*/
     Serial.println("Rilevato ostacolo sul percorso");
     Serial.print("Calcolo");
     delay(100);
     Serial.print(".");
     delay(100);
     Serial.print(".");
     delay(100);
     Serial.print(".");
    TrovaStrada();
   }
                                  
}

 void Avanti(){
  digitalWrite(2, HIGH);
  digitalWrite(3, LOW);
  digitalWrite(4, HIGH);
  digitalWrite(7, LOW);
  return;
 }

 void TrovaStrada(){
  Stop();
  Indietro();
  GuardaDestra();
  GuardaSinistra();

  if (DistanzaDestra > DistanzaSinistra)
  {
    Serial.print("Decisione: giro a destra");
    GiraDestra(); 
  }
  else
  {
    Serial.print("Decisione: giro a sinistra");
    GiraSinistra();
  }
 }

   void Stop(){
  digitalWrite(2, LOW);
  digitalWrite(3, LOW);
  digitalWrite(4, LOW);
  digitalWrite(7, LOW);
  delay(500);
  return;
 }
   
   void Indietro(){
  digitalWrite(2, LOW);
  digitalWrite(3, HIGH);
  digitalWrite(4, LOW);
  digitalWrite(7, HIGH);
  delay(500);
  Stop();
  return;
 }
  
 
 void GuardaDestra(){
  servo.write(0);
  delay(700);
  DistanzaDestra = (sonar.ping_cm());
  servo.write(90);
  delay(700);
  return;
 }
 
 void GuardaSinistra(){
  servo.write(180);
  delay(700);
  DistanzaSinistra = (sonar.ping_cm());
  servo.write(90);
  delay(700);
  return;
 }

 void GiraDestra(){
  digitalWrite(4, HIGH);
  digitalWrite(7, LOW);
  digitalWrite(2, LOW);
  digitalWrite(3, HIGH);
  delay(1500);
  Stop();
  return;
 }

  void GiraSinistra(){
  digitalWrite(4, LOW);
  digitalWrite(7, HIGH);
  digitalWrite(2, HIGH);
  digitalWrite(3, LOW);
  delay(1500);
  Stop();
  return;
 }

Il codice non è molto commentato ma credo sia abbastanza lineare da capire.
Grazie mille per l'attenzione spero di essere stato abbastanza chiaro nel descrivere il progetto e il problema, oviamente ben vengano consigli di ogni genere.
Scusate per eventuali "irregolarità" ma è la prima volta che scrivo su questo forum.

Buongiorno,
essendo il tuo primo post, nel rispetto del regolamento (… punto 13, primo capoverso), ti chiedo cortesemente di presentarti QUI (spiegando bene quali conoscenze hai di elettronica e di programmazione ... possibilmente evitando di scrivere solo una riga di saluto) e di leggere con MOLTA attenzione il su citato REGOLAMENTO ... Grazie :slight_smile:

Guglielmo

P.S.: Qui una serie di link utili, NON necessariamente inerenti alla tua domanda:
- serie di schede by xxxPighi per i collegamenti elettronici vari: ABC - Arduino Basic Connections
- pinout delle varie schede by xxxPighi: Pinout
- link generali utili: Link Utili

Probabilmente ti conviene sempre fare più letture ravvicinate (es. 5 o 10), scartando quelle ovviamente non valide e mediando i valori letti ...
... in ogni caso gli HC-SR04 sono piuttosto scarsi tanto che è molto meglio usare i nuovi SRF05 che costano più o meno uguale e sono decisamente migliori sotto tutti gli aspetti.

Guglielmo

P.S.: Io, per quanto possibile, eviterei anche il pin 13 che è lo stesso a cui è collegata l'elettronica per il LED di Arduino

Sí in effetti mi sono reso conto che questi sensori traballano parecchio.
Grazie mille per la dritta adesso daró un'occhiata sul come fare quello che mi hai suggerito, penso proprio sia la soluzione definitiva.

Attenzione a non confondere i sensori cineseria HCqualcosa con gli originali SRFxx prodotti da robot electronics, c'è una abisso sia come qualità delle capsule che come tipologia di elettronica, ma sopratutto di prestazioni, non a caso gli SRFxx costicchiano.
In tutti i casi anche con gli SRFxx gli eco fasulli sono la norma, sopratutto se i sensori sono montati vicino al pavimento, anche se si trovano ad una altezze di 25-30 cm sono sempre troppo pochi, questo perché il cono di emissione è molto ampio, pertanto tocca il pavimento a breve distanza e questo porta facilmente ai falsi eco per riflessione sul pavimento, sopratutto se non è perfettamente liscio.
Rammento che l'eco del ping ultrasonico, dovuto all'impatto su un oggetto, si propaga in tutte le direzioni, è come l'onda creata dal sassolino in acqua, non è un raggio di luce che viene riflesso con un angolo pari e contrario a quello di incidenza, salvo le deflessioni dovute alla rugosità superficiale.

astrobeed:
Attenzione a non confondere i sensori cineseria HCqualcosa con gli originali SRFxx prodotti da robot electronics ...

... giusto, il nome esatto è SRF05, ho corretto il mio post precedente :wink:

Guglielmo

astrobeed:
Attenzione a non confondere i sensori cineseria HCqualcosa con gli originali SRFxx prodotti da robot electronics, c'è una abisso sia come qualità delle capsule che come tipologia di elettronica, ma sopratutto di prestazioni, non a caso gli SRFxx costicchiano.
In tutti i casi anche con gli SRFxx gli eco fasulli sono la norma, sopratutto se i sensori sono montati vicino al pavimento, anche se si trovano ad una altezze di 25-30 cm sono sempre troppo pochi, questo perché il cono di emissione è molto ampio, pertanto tocca il pavimento a breve distanza e questo porta facilmente ai falsi eco per riflessione sul pavimento, sopratutto se non è perfettamente liscio.
Rammento che l'eco del ping ultrasonico, dovuto all'impatto su un oggetto, si propaga in tutte le direzioni, è come l'onda creata dal sassolino in acqua, non è un raggio di luce che viene riflesso con un angolo pari e contrario a quello di incidenza, salvo le deflessioni dovute alla rugosità superficiale.

Mmmh quindi mi stai dicendo che, ad esempio, gli errori nel calcolo della direzione giusta da prendere possono derivare da un limite fisico del sensore, una volta per un progetto simile avevo usato un sensore infrarosso molto più costoso ma si notava la differenza, che mi dite sull'idea della disposizione a pentagono? Potrebbe funzionare meglio ed essere più rapido?

ciao, la libreria NewPing ritorna zero quando la distanza rilevata è superiore alla distanza massima, 200 per come la hai impostata tu con MAX_DISTANCE, quindi filtra il valore 0 nel tuo codice.

Rimane il problema delle false letture di questi sensori, un metodo semplice per ridurle lo trovi già nella libreria NewPing: sonar.ping_median(iterations).
In questo modo invece di una lettura ne fa 5 (o quante ne decidi tu con il parametro iterations) e tiene la mediana.

Se vuoi complicare il gioco, puoi usare la mediana mobile, cioè calcolare la mediana delle ultime 5 letture del sensore, ma facendo una sola lettura per ciascun loop, trovi una libreria per farlo QUI.
In questo modo non devi aspettare il tempo di 5 letture per averne una (mentre avanzi ha senso, quando guardi a destra e sinistra no).

Se vuoi complicarlo ancora di più puoi provare con un filtro di Kalman, trovi una spiegazione con esempi di codice QUI.
Con un solo valore da stimare la matematica non è complessa e neppure i calcoli da fare sono troppi.
Un vantaggio rispetto alla mediana mobile è che reagisce prima ad un cambiamento dei valori: pensa soprattutto ad un cambiamento improvviso, come qualcuno che passa davanti al robot. Se i valori letti passano di colpo e in modo stabile (perché l'ostacolo c'è davvero) ad esempio da 100 a 20, la mediana mobile per le due letture successive continua a restituire 100, solo alla terza lettura (se il campione è di 5) scende di colpo a 20.

fcavazza:
In questo modo invece di una lettura ne fa 5 (o quante ne decidi tu con il parametro iterations) e tiene la mediana.

Peccato che con la media, normale o mobile che sia, non ci fai nulla visto che serve solo per migliorare la precisione e non per eliminare le false letture, esempio pratico, ho cinque letture di cui 4 valgono 100 cm e una vale 0, la media è 400/5 = 80, un valore decisamente errato.
Idem per il Kalman che in questo caso è totalmente fuori contesto, semplicemente vanno scartati tutti i valori che sono a zero oppure oltre il massimo atteso.
In tutti i casi i sensori HCxxxx rimangono delle cineserie totalmente inaffidabili come funzionamento, come direbbe Fantozzi "sono delle cagate pazzesche". :slight_smile:
Se volete dei sensori ad ultrasuoni decenti dovete prendere gli SRFxx, e pure loro hanno qualche problemino di false letture, altrimenti tocca andare su cose di ben altro livello e costo.
Se serve precisione, affidabilità, molto meglio usare i telemetri IR di Sharp, con questi è anche possibile realizzare una sorta di lidar 2D, a basse prestazioni, low cost.

Come ti hanno già suggerito devi scartare i valori settati a 0.
Ho usato molto tali sensori, per avere una "buona" precisione servono almeno 16 letture e va usata la media mobile.
qui trovi un esempio.
Volendo puoi leggere la distanza anche in "modalità asincrona", agganciandoti ad un pin di interrupt con il pin ECHO e durante il rilevamento di cambio di stato prelevare il tempo passato.
Non è complicato, richiede poche linee di codice, ti posterei un esempio ma non ho il mio pc con me e non ho messo on-line la libreria.

Grazie mille dei consigli adesso mi guardo un po' i link che mi avete dato e vedo che riesco a tirarne fuori, vi informeró di come procede la cosa.

Per la verità il metodo della mediana, per quanto rallenti un po' il rilevamento dell'ostacolo, toglie la maggior parte dei falsi zero e migliora decisamente le cose, in più anche la scelta del percorso da prendere diventa molto più accurata rispetto a prima, il filtro kalman mi è sembrata una cosa un po' macchinosa e poco utile in questo caso, quindi credo che utilizzare la mediana, almeno per ora sembra il metodo più funzionale, almeno praticamente parlando.

Concordo che non è utile la media, proprio perché una lettura errata la sposta anche di molto. Avevo infatti suggerito di usare la mediana, ovvero il valore che sta "in mezzo" a tutti i valori letti (ordinati ovviamente). La mediana è molto meno sensibile alle false letture, una falsa lettura viene semplicemente ignorata dalla mediana, mentre la media la considera ed è molto sensibile ai valori estremi.

Controlla anche l'alimentazione che rimanga stabile ai sensori ! Ho montato il progetto anche io per l'esame di stato . Non ha funzionato nel migliore dei modi ( non ha compromesso il mio risultato finale per fortuna) per colpa dell'alimentazione ai sensori !

Delchi:
Per la verità il metodo della mediana, per quanto rallenti un po' il rilevamento dell'ostacolo, toglie la maggior parte dei falsi zero e migliora decisamente le cose

Come puoi trovare descritto in QUESTO mio vecchio post, avendo avuto io stesso esperienze analoghe, ti consiglio di BUTTARE gli HC-SR04 (non acquistandoli più) perché troppo scadenti e inaffidabili, e prendendo invece gli SRF05, ben più affidabili e, in fondo, di poco più costosi. Prova anche ad usare la libreria che trovi in quel topic e che 2 anni fa creai proprio per questo scopo.
Fammi sapere...