Go Down

Topic: La función MAP me devuelve un valor que no esperaba (Read 183 times) previous topic - next topic

Esfinterman

Hola foreros!! Por diversos motivos, como un trabajo temporal, proyectos musicales, vacaciones y demás impedimentos, he estado ausente en el foro, pero ahora estoy a punto de acabar un proyecto y ya vuelvo a tener más tiempo. :D

Estoy haciendo un pequeño sensor crepuscular cuyo objetivo es que a menos luz solar, se encienda una salida. No es un "todo o nada", sino que la intensidad de la luz variará en función de la luz solar o ambiente que reciba. Esto se implementará en un pequeño Tiny 85 debido a la sencillez del proyecto y al excedente que tengo en casa. :D Para el caso, estoy probando este ejercicio en una placa DUE. ;)

Después de programarlo y, aparentemente funcionar como quiero (tapo el LDR con la mano y se va iluminando más el LED de prueba), reviso el código y con la ayuda del monitor serie, me doy cuenta de que la función MAP me devuelve un valor que no me esperaba. :-\

Code: [Select]
int pulso; //Valor de la fotorresistencia
int valor; //Valor de PWM

void setup() {

pinMode (13, 1);
}

void loop() {

pulso = analogRead (0); //Lectura directa del LDR desde A0
valor = map (pulso, 120, 160, 255, 0); //Valor recalculado para la salida 13 por PWM

if (pulso > 160){
  digitalWrite (13, 0);
}else{
  analogWrite (13, valor);
}
}


Por aquello de optimizar memoria, empecé por declarar variables BYTE en vez de INT pero tenía desbordamientos. Para hacerlo funcionar, las cambié por INT y ahora funciona, sin embargo, la función MAP, con el código adjunto, me devuelve valores por encima de 255.

No soy experto, pero leyendo sobre la función MAP leo que hace una especie de regla de tres entre cuatro valores. Teniendo como base este código adjunto, si recibe un valor de 120 por A0, el valor devuelto debería ser 255 y si recibe 160, debería devolver un cero. Sin embargo, me encuentro con que el valor devuelto sobrepasa los 255. Para exagerar la lectura, apunto con un puntero láser directamente a la fotorresistencia y obtengo fácilmente valores de más de 900. Es más, ambas variables tienen el mismo valor. :smiley-eek:

Si bien es cierto que el proyecto funciona, es evidente que me estoy equivocando en algo con la función MAP: o algo mal programado o un concepto erróneo sobre el funcionamiento de esta función. ;)


Saludos!!

surbyte

Sutilezas al margen, en tu código no veo como lees valores porque no hay ningún Serial.begin() y menos impresion de datos pero me dirás que no lo copiaste y tienes otro código.


Que es esto?

Code: [Select]
pinMode (13, 1);

Debe ser
 
Code: [Select]
pinMode(13, OUTPUT);

vayamos resolviendo pequeños problemas.



Esfinterman

#2
Nov 13, 2018, 02:15 pm Last Edit: Nov 13, 2018, 02:17 pm by Esfinterman
Omití la parte del monitor serie para que no sea largo, pero aquí te la pongo:

Code: [Select]
int pulso;
int valor;

void setup() {

pinMode (13, 1);
Serial.begin (9600);
}

void loop() {

Serial.print ("Pulso: ");
Serial.print (pulso);
Serial.print (" / Valor: ");
Serial.println (pulso);

pulso = analogRead (0);
valor = map (pulso, 60, 120, 255, 0);

if (pulso > 120){
  digitalWrite (13,0);
}else{
  analogWrite (13, valor);
}
}


Los valores que cambian es porque voy calibrando en el momento el programa.

Lo de pinMode (13, 1), como imagino que sabrás, es establecer el pin 13 como salida. Funciona exactamente igual que OUTPUT y es mucho más sencillo establecer pines y modos desde el teclado numérico sin pasar al teclado de texto: no tengo que mover la mano. Aún así, lo cambiaré, pero no tengo problema con la salida en sí, que va bien, sino con que el valor que devuelve MAP no es lo que me esperaba. ;)

EDITO: Añado una lectura del monitor serie. "Pulso: 146 / Valor: 146"

surbyte

Bien, luego lo pruebo y te digo. No veo por ahora que hayas hecho algo mal. No ahora.. pero cuando lo pruebe tal vez aparezca algo y le encuentre explicación.

Esfinterman

#4
Nov 13, 2018, 02:22 pm Last Edit: Nov 13, 2018, 03:58 pm by Esfinterman
Gracias. Acabo de encontrar un error. En la única línea Serial.println que hay, he puesto la variable "pulso" y debería ser "valor". Ahora sí me muestra resultados distintos. No obstante, sigo obteniendo valores por encima de 300 y valores negativos de -100 y más ;)

surbyte

Bueno mira esto

La funcion map trabaja con long no con int de modo que hay que tener cuidado con eso.

Hice esta modificación y se comporta mas o menos bien pero.... hay fallas

Code: [Select]

long pulso;
long valor;

void setup() {
 pinMode (13, 1);
 Serial.begin (9600);
}

void loop() {
 char buffer[30];

 randomSeed(analogRead(A0));
 //pulso = analogRead (A0);
 pulso = random(30, 200);
 valor = map (pulso, 60, 120, 255, 0);

 sprintf(buffer,"Pulso = %4ld valor = %4ld", pulso, valor);
 Serial.println (buffer);

 if (pulso > 120){
  digitalWrite (13,0);
 }else{
  analogWrite (13, valor);
 }
}


OBtuve este resultado

Quote
Pulso =  144 valor = -102
Pulso =   66 valor =  230
Pulso =  112 valor =   34
Pulso =   61 valor =  251
Pulso =  126 valor =  -25
Pulso =   38 valor =  348
Pulso =  149 valor = -123
Pulso =   89 valor =  132
Pulso =  190 valor = -297
Pulso =   39 valor =  344
Pulso =   75 valor =  192
Pulso =   62 valor =  247
Pulso =  103 valor =   73
Pulso =  103 valor =   73
Pulso =   76 valor =  187
Pulso =  135 valor =  -63
Pulso =  122 valor =   -8
Pulso =  117 valor =   13
Pulso =  163 valor = -182
Pulso =  113 valor =   30
Pulso =  195 valor = -318
Pulso =  159 valor = -165
Pulso =  131 valor =  -46
Pulso =   53 valor =  284
Pulso =  104 valor =   68
Pulso =   39 valor =  344
Pulso =   49 valor =  301
Pulso =  145 valor = -106
Pulso =   90 valor =  128
Pulso =   95 valor =  107
Pulso =   53 valor =  284
Pulso =   63 valor =  243
Pulso =  113 valor =   30
Pulso =  104 valor =   68
Pulso =   63 valor =  243
Pulso =  191 valor = -301
Pulso =   31 valor =  378
Pulso =   35 valor =  361
Pulso =  118 valor =    9
Pulso =   31 valor =  378
Pulso =  182 valor = -263
Pulso =   68 valor =  221
Pulso =  173 valor = -225
Pulso =  132 valor =  -51
Pulso =  123 valor =  -12
Pulso =  150 valor = -127
Pulso =  183 valor = -267
Pulso =  141 valor =  -89
Pulso =  123 valor =  -12
Pulso =   68 valor =  221
Pulso =  118 valor =    9
Pulso =  151 valor = -131
Pulso =   63 valor =  243
Pulso =  137 valor =  -72
Pulso =  183 v
Por ahora la mejor solución es esta luego de map

Code: [Select]
valor = map (pulso, 60, 120, 255, 0);
if (valor < 0)
valor = 0;

Esfinterman

Gracias por el tiempo dedicado. He visto que has modificado el código con random y randomSeed. Tras leer acerca de estas funciones, veo que has querido obtener una serie de datos sin montar el esquema. Son funciones que nunca he usado. :D No entiendo aún qué hace sprintf, no lo encuentro en la referencia, pero creo que tiene algo que ver con char buffer[30]. Es programación que aún no me ha hecho falta o la desconozco. ;)

Por los resultados que te da y por los que me da a mí, creo que puedo llegar a una conclusión, y es que MAP no se limita al rango que establezcamos. Me explico. Usaré números sencillos.

Si tenemos un MAP (0, 50, 0, 100) éste nos devolverá 0 si le metemos un 0, y si le metemos un 25 nos devolverá un 50. Y si le metemos un 50, que es el valor máximo, MAP nos devolverá un 100. Hasta aquí creo que vamos bien. Ahora, si le metemos un 100, que está fuera de rango, veo que MAP no se limita al rango y sigue calculando el "equivalente", por lo que nos devolverá un 200. ¿Es eso cierto? ¿Es este su funcionamiento correcto? De ser su funcionamiento correcto, es decir, que no se limita al rango establecido, entonces sí entiendo que me devuelva valores que no me esperaba, pues tenía en mente que MAP se limitaba básicamente al rango de números que establecemos. ;)

surbyte

Tampoco habia analizado las cosas como tu las planteas pero esta claro que map hae lo que puede porque no deja de ser una regla de 3 simple

Yo he usado random para generar números al azar en el rango que tu mencionas.
No voy a montar un sensor que haga lo que tu quieres.
Luego veo como consigo los resultados o que estoy interpretando bien o mal.

Esto es map

Code: [Select]
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;
}


Asi que pongamos un valor  y veamos que hace.
Code: [Select]
valor = map (pulso, 60, 120, 255, 0);

x = pulso
in_min = 60
in_max = 120
out_min = 255
out_max = 0

(pulso - 60)*(0-255)/(120-60)+255 => (pulso - 60)(-255)/60+255
=> (60-pulso)*255/60 + 255

usa esto por ahora

Code: [Select]
char buffer[40];

 randomSeed(analogRead(A0));
 //pulso = analogRead (A0);
 pulso = random(0, 1023);
 valor = map (pulso, 60, 120, 255, 0);
 if (valor < 0)
 valor = 0;
 if (valor > 120)
    valor = 120;

 sprintf(buffer,"Pulso = %4d valor = %4d", pulso, valor);
 Serial.print (buffer);


Yo replantearía el problema, truncando los valores para que solo ingresen aquellos que estan en el rango 60, 120 y lo demas los trunco a 0 o 255 lo haces antes o despues y asunto terminado.

_jose_

si solo te interesan los valores de 60 a 120 puedes usar la funcion constrain antes de mapear: https://www.arduino.cc/reference/en/language/functions/math/constrain/

Esfinterman

Gracias a ambos!! Necesito leer esta solución con calculadora en mano y con tiempo para comprender todos los cálculos y entenderlos. No es lo mismo "copiar y pegar" un código que entenderlo. En un primer momento no me afecta que MAP me calcule valores fuera de rango, pues un analogRead (600) es interpretado por analogWrite por 255, su valor máximo, que es lo que interesa. Sin embargo, en otras aplicaciones puede ser útil truncar los resultados. Estudiaré qué hace la función constrain, que nunca la había usado y me pongo con el código. Gracias a ambos ;)

Go Up