attenuazione risultato funzione map[risolto]

Ciao a tutti, lavorando sul mio quadrupode mi sono imbattuto in un problemino, in pratica io gestisco l’angolo in cui posizionare i servo passandogli un valore in gradi che viene rimappato a seconda del servo e scritto in un array, il problema è che ora mi serve lavorare con dei delta angoli e per fare questo uso la funzione map al contrario cioè data la posizione attuale del servo devo ottenere l’angolo.
Ho scoperto però che la funzione su int tronca i decimali attenuando sempre di più il valore.
ho scritto il breve codice qui di seguito per verificare la cosa

void loop() {
  // put your main code here, to run repeatedly:
  int a=200;
  int c;
  int delta=50;
  int cambio;
  int segno=-1;
  
  for (int i=1; i <= 100; i++){
  delay(1000);
  c=map(a,100,500,0,180);  
  Serial.println(c);
  a=map(c,0,180,100,500); 
  Serial.println(a); 
  delta=delta*segno;
  a=a+delta;
  Serial.print("a modificato "); 
  Serial.println(a); 
    
  }

}

dove ad ogni passo incremento o decremento di 50 la variabile iniziale e in effetti ottengo questo:
45
200
a modificato 150
22
148
a modificato 198
44
197
a modificato 147
21
146
a modificato 196
43
195
a modificato 145
20
144
a modificato 194
42
193
a modificato 143
19
142
a modificato 192
41
191
a modificato 141
18
140
a modificato 190
40
188
a modificato 138
17
137
a modificato 187
39
186

mentre chiaramente mi aspetterei di avere sempre gli stessi valori alternati.
esiste un sistema semplice per aggirare il problema o devo scrivermi la funzione io a mano?
grazie

long map(long x, long in_min, long in_max, long out_min, long out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

Chi ha sviluppato la libreria ha fatto una scelta particolarmente discutibile, per primo non impostando questa funzione come inline, secondo scegliendo come tipo in ingresso il long che di per se ha la risoluzione di un intero. Basando sul fatto che map non è che un semplice rapporto di proporzionalità, ricavati le formule ad hoc e usi dei float invece di int, e il problema dei decimale dovrebbe ridursi drasticamente.

RobertoBochet: Chi ha sviluppato la libreria ha fatto una scelta particolarmente discutibile ...

... discutibile o meno, [u]è chiaramente indicato[/u] nella pagina della map() :

The map() function uses integer math so will not generate fractions, when the math might indicate that it should do so. Fractional remainders are truncated, and are not rounded or averaged.

Quindi ... ::)

Guglielmo

infatti non discutevo la cosa, le scelte sono per definizione più o meno discutibili, la mia domanda era la soluzione migliore per risolvere il mio problema, applicando più volte la funzione map invertendo i valori l’approssimazione si somma rendendo il risultato sepre diverso dalle aspettative.
la funzione che è stata postata in effetti funziona ed era quella indicata anche in diversi siti con lievi modifiche, se è il meglio uso quella.

è un semplicissimo rapporto, conla particolarità che gli estremi possono differire da 0. 0:50=0:200 Nel primo intervallo identifico il valore x e voglio il corrispettivo y nel secondo intervallo. x:50=y:200 y=(x*200)/50 Aggiungi il piccolo livello di difficoltà di non avere gli estremi vincolati in 0 e hai 50:100, 100:300 che posso riscrivere come 50+0:50+50, 100+0:100+200 per tanto (x-50):(100-50)=(y-100):(200-200) inverti la formula e questa non è altro che quella della funzione map.

ok grazie, pensavo ci fosse una funzione apposita apposita, ho modificato la formula canonica per i miei scopi, sto lottando con la memoria disponibile e mi interessava la soluzione più ottimizzata possibile per arrivare al mio scopo. ora sembrerebbe tutto ok

RobertoBochet: e il problema dei decimale dovrebbe ridursi drasticamente.

Fare la map con dei float è volersi fare del male da soli :D Questo perché i float a 32 bit sono molto imprecisi per via della loro natura esponenziale, molto meglio lavorare con i long, o gli unsigned long se non servono i valori negativi, visto che offrono la massima precisione e sono valori a 32 bit, oppure 31 più il bit di segno. Sugli AVR gli interi sono a solo 16 bit, la map lavora con valori fino a 32 bit. Quando serve l'equivalente dei decimali conviene sempre usare valori interi preventivamente moltiplicati per 10-100-1000, compatibilmente con la precisione necessaria e la massima dimensione del valore, invece che con i float. La conversione tra int e float si fa solo quando è necessario rappresentare i valori, oppure nel caso sia necessario fare calcoli trigonometrici dove l'uso dei float è obbligatorio, idem per i logaritmi e calcoli simili.

alla fine ho optato per moltiplicare per 100 e dividere riesco a ottenere una buona precisione grazie

E astro come al solito ha ragione, però qui bisogna mettersi d'accordo, quando porto all'attenzione una soluzione performante salta sempre su qualcuno a dire e va beh su Arduino le performance non sono da considerare, quando invece mi adeguo e propongo una soluzione rapida senza considerare l'ottimizzazione e viene giustamente ricordato quanto la MCU non sia prestante, ci mettiamo d'accordo? E guai a chi salta su con qualcosa del tipo la verità sta nel mezzo che sono già pronto con il seghetto per tagliargli le ditina :grinning:

La soluzione è sempre scritta nel sacro testo, il K&R, che deve essere letto completamente almeno una volta alla settimana, al rogo l'eretico C++, C Ansi for ever. :D

bhe, il k&r è solo la punta dell'iceberg. Ad esempio sugli AVR la divisione è sempre da evitare se non per potenze di 2. La moltiplicazione è accettabile e si può sommare un valore immediato ad un int fino a 64 con soli due colpi di clock. In base a queste considerazioni si effettua l'ottimizzazione del codice.