Go Down

Topic: Problema con vehículo que esquiva obstáculos (Read 188 times) previous topic - next topic

FrancoARossi

Hola gente!

Me estoy iniciando con Arduino Uno y como primer proyecto decidí hacer un "vehículo" que avanza y al encontrar un obstáculo gira. El problema es que este funciona en un comienzo pero luego de toparse con obstáculos un par de veces se detiene y no realiza ninguna acción, aveces al moverlo vuelve a andar pero eventualmente se detiene y es necesario desconectar la corriente y volver a conectarla (pulsar el botón Reset de la placa no ayuda en nada).

Los elementos que utilizo son:
#Arduino Uno R3
#Modulo L298N
#Sensor HC-SR04
#Mini Protoboard
#4 Pilas AA en Serie (6V y aproximadamente 2500 mAh)
#Una rueda 360 (para la parte posterior)
#2 motores de corriente continua con rueda como este:


Les adjunto el código:

Code: [Select]
#define IN1 8
#define IN2 7
#define IN3 4
#define IN4 2
#define disparador 13
#define receptor 12

float tiempo;
word espacio;
byte repeticion;
byte aleatorio;

void setup()
{
pinMode(IN1, OUTPUT); /* IN1, IN2 y ENA corresponten a un motor */
pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT); /* IN3, IN4 y ENB corresponden a otro motor */
pinMode(IN4, OUTPUT);
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, LOW);
pinMode(disparador, OUTPUT); /* disparador es el Trig */
pinMode(receptor, INPUT); /* receptor es el Echo */
Serial.begin(9600);
randomSeed(analogRead(5));
}

void loop()
{
Distancia();

if (repeticion < 1)
{
aleatorio = byte(random(0, 2)); /* Genero un valor aleatorio para la direccion de giro pseudo-aleatoria */
}

if (espacio > 23)
{
Avanzar();
}
else /* Que gire */
{
Distancia();
while (espacio <= 23)
{
Retroceder();
}
switch (aleatorio)
{

case 0:
Izquierda();
repeticion++;
if (repeticion == 3)
{
repeticion = 0;
}
delay(1176);
break;

case 1:
Derecha();
repeticion++;
if (repeticion == 3)
{
repeticion = 0;
}
delay(1176);
break;
}
}
}

void Distancia()
{
digitalWrite(disparador, LOW);
delayMicroseconds(2);
digitalWrite(disparador, HIGH);
delayMicroseconds(10);
digitalWrite(disparador, LOW);

tiempo = float(pulseIn(receptor, HIGH) / 2); /* Tomo el tiempo de recepcion Echo */
espacio = word(0.03432 * tiempo);

Serial.print("Obstaculo a: ");
Serial.print(espacio);
Serial.println(" cm");
}

void Avanzar()
{
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
}

void Retroceder()
{
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
}

void Izquierda()
{
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
}

void Derecha()
{
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
}


Gracias por su tiempo!

ArduMyth

El problema es que no es un error de una línea o dos o una función que esté mal.
Es el conjunto del código que está mal planteado.

Aparte estos proyectos muy comunes en vídeos de youtube no muestran qué ocurre cuando hay un obstáculo en torno a la base (por eso siempre son en suelos de una casa sin nada que pueda frenar las ruedas) Nótese que los sensores salvo un gran rango (angulo/distancia) deben estar situados a lo largo de la altura. Tenlo en cuenta porque tus expectativas pueden ser otras.

Ahora vamos a analizar el código:

  • ¡¡¡Quita los DELAY!!! Enserio que manía, que daño ha hecho hacer los ejemplos con esta función. Usa timers con millis().
  • Teniendo un L298N falta por algún lado el analogWrite() con el pin enable del motor y la velocidad del mismo ¡Importantísimo! (A tener en cuenta qué pines son PWM)
  • Dado esos motores y por una cuestión de espacio/tamaño, un l293d habría sido aceptable antes que el L298N, e igual tendrías que usar el analogWrite(), y mejor un NANO a un UNO, hasta por consumo.
  • Dado que es un frame con dos ruedas y que los motores no van a girar exactamente a la misma velocidad, se comprueba cambiando el segundo parámetro de analogWrite() ¿Cómo? Fácil. programa para que se mueva hacia delante, partiendo con la misma velocidad. Si el "robot" va curvándose hacia la izquierda el motor derecho va un poco más rápido, si hace lo contrario, será el otro. Es cuestión de ajustar los valores hasta que camine recto. Esto no suele comentarse en los tutoriales, ignoro por qué.
  • Al random no le veo la lógica. El robot debe iniciarse siempre en un sentido, habitualmente yendo de frente, si el detector de obstáculos es correcto, cambiará desde que se produzca la "colisión". No hablo de golpearse, sino del "evento" ;) cómo los colisionadores en una IA que a fin de cuenta de esto se trata y en Arduino poco o nada se ve :( parece un lenguaje destinado a niños en el mundillo hispano/castellano hablante.
  • Esta línea: while (espacio <= 23) es estresante ya que llevo días viendo cómo se están dedicando a poner while() donde va un simple if().
    Si lo pones cómo un bucle (que recuerda está dentro de un loop), hasta que salga del mismo, el código no continua, lo que hará que el movimiento de los motores sea totalmente incorrecto.
  • La variable contador tampoco tiene sentido. El planteamiento es enrevesado.
  • Todas las funciones para según qué movimiento se pueden aunar en una sola función, donde el primer parámetro sea el pin del motor y el segundo un boolean. No hay sino ver que la diferencia entre atrás y delante es el inverso, lo mismo con izquierda y derecha.

Y repitiendo lo de los sensores, ángulos y distancia, para gustos colores, sé que tú has preferido el de ultrasonidos y yo el de infrarrojos, pero he aquí mi planteamiento (este fue de mis primeros proyectos, no seas muy crítico con su apariencia).
Funciona e incluso no hace un suicidio cuando llega a las escaleras, véase el sensor en el medio de la base, el cual hubo que ajustar, inclinando lo necesario, para que no diese falsas lecturas con la rueda de 360º.



Si te fijas uso los sensores sólo en la parte frontal, no los muevo porque para eso gira el "robot" mientras dure el colisionador, en estos, cómo su ángulo sólo es hasta 30º y el ancho de la circunferencia del frame es mayor, uso dos en los extremos. A su vez como un obstáculo puede estar en la base, el medio o sólo arriba (por ejemplo puede que la base esté libre pero la parte superior supere en altura el hueco de un sillón), los sensores están distribuidos en base a la altura del robot.

Si me preguntas respecto a mi código, no es que tenga copyright, es que uso punteros y clases propias, así que más que ayudarte te liaría.

Este es un ejemplo de cómo usar los motores correctamente con el analogWrite(), https://www.prometec.net/l298n/ , pero discrepo en el resto del código, aunque por supuesto, es sólo una guía, si introdujese; timers, estructuras, punteros, etc, quienes empiezan no entenderían.


Espero haberte guiado en algunas cosas, ve mirando lo de analogWrite, poniendo timers, cambiando la estructura y esas cosas.
Saludos y suerte con el proyecto.

FrancoARossi

#2
Mar 11, 2018, 08:40 pm Last Edit: Mar 11, 2018, 11:49 pm by surbyte
Moderador: No repitas lo que se lee arriba

Gracias por responder!

No veo como estos consejos podrían solucionar mi problema más que para ordenar el código.
Utilizando la función Distancia() en el ciclo While debería solucionar esa parte ya que lo que quiero es que retroceda hasta estar a 23 cm del obstáculo (no veo como podría hacer eso con un if).
Estuve informándome un poco sobre la función millis() pero no veo como me seria de utilidad aquí, el tiempo que puse en el delay es aproximadamente lo que toma en realizar un giro de 90° a la rueda sin cambiarle la potencia, por ejemplo, ejecuto la función para girar a la Izquierda y luego detengo el programa el tiempo suficiente para que gire 90° mientras que el timer con millis(), hasta donde yo sé, sirve para ejecutar una acción cada cierto tiempo.
El problema perciste, si me equivoco en algo de lo que escribí aquí agradecería una corrección :)

ArduMyth

Gracias por responder!
No veo como estos consejos podrían solucionar mi problema más que para ordenar el código
Ah ¿No te sirven? Interesante, de todo has extraído que he dicho que ordenes el código...
Vamos que te he escrito por gusto. :smiley-roll-blue:
Se te ha puesto un enlace, te falta el analogWrite() y usas delay() lo que detendrá el motor. Y me reitero en tu while(), tu random es innecesario ¿Qué más quieres?
Si estás buscando código hecho: No (al menos por mi parte porque ya indiqué tus fallos).

que retroceda hasta estar a 23 cm del obstáculo (no veo como podría hacer eso con un if).
No veo cómo no entiendes que en Arduino estás ejecutando todo dentro del loop, que ya de por si es un bucle... :smiley-confuse: y ya expliqué que al hacer esto detienes tu código por lo que los motores se moverán a "trompicones" si es que llegan a funcionar un rato ¿Nunca has hecho una función recursiva? Pues en algunos aspectos en Arduino tienes que entender que el if que pones volverá a entrar al llegar a la última línea del loop().
Tu while() esperando un valor de un sensor va a trabar tus motores, porque arduino se mantendrá en esa línea esperando la salida. En cambio un if lo evalúa y da igual el bool que dé, continua con la siguiente línea. Es muy sencillo de entender. PROGRAMACIÓN BÁSICA.

el timer con millis(), hasta donde yo sé, sirve para ejecutar una acción cada cierto tiempo.
Lo siento no voy a explicar millis() porque es tema harto repetido en el foro. Ve a la documentación o sigue en tus trece y sigue usando delay, pero no te va a ir bien tu proyecto, así de simple.
Intenta hacer funcionar tus motores en un código simple y a la vez haz un blink de un led de dos segundos, prueba y nos comentas el resultado, a ver si estás equivocado o no.

El problema perciste, si me equivoco en algo de lo que escribí aquí agradecería una corrección :)
Se te ha corregido. A tu código le faltan cosas, le sobran otras y tienes fallos básicos en los aspectos ya mencionados. No se te puede reescribir el código porque no es nuestro proyecto. El problema persiste y lo hará porque en general está mal. Lo siento pero es así.
Al menos quita los delay y cambia el while, pero si te empeñas nada, sigue usándolo, pero no abras otro hilo dentro de dos días sobre esto.

Sólo una nota, cómo yo digo funciona, a tu manera no. Razona.

IgnoranteAbsoluto

Independientemente de lo mucho que se pueda mejorar el programa, un problema que tienes con el while (espacio <= 23) es que cuando se cumple la condición, y entra en el bucle, el valor de la variable espacio no cambia dentro del bucle. Con lo que la condición siempre se cumplirá y nunca saldrá del bucle. Se quedará retrocediendo "para siempre". Una solución "rápida": actualizar el valor de espacio dentro del bucle llamando a la función Distancia() dentro del bucle:

Code: [Select]
Distancia();
while (espacio <= 23)
{
Retroceder();
Distancia();  // <--- Actualiza el valor de espacio, para saber cuándo salir del bucle
}

ArduMyth

Exacto, @IgnoranteAbsoluto, pero igual no va a funcionar cómo un "robot" esquiva obstáculo porque llama a retroceder(), marcha atrás, y lo que tiene que hacer el rotar porque usa dos ruedas según comenta al inicio. Debería mover un motor a la izquierda otro a la derecha. De ahí que su random tampoco fuese lógico.

Véanse las "aspiradoras inteligentes" (que realmente barren no aspiran). Cuando detectan un obstáculo giran sobre sí mismas, de esa manera el sensor vuelve a revisar si hay otro obstáculo.
Si encierras un robot de estos dentro de un cuadrado sin salida, por poner un ejemplo, el robot debe hacer 360 todo el rato.

A su manera si pasas delante y te detecta su robot se irá hasta una distancia mayor de 23 sin evaluar nada más. Si hay algo detrás pues ... ¡alegría, atropellado :D!

Por eso decía que debía revisar la lógica del mismo.

Go Up