
Tutorial Robot Autónomo: Parte II
Ahora terminaremos con el proyecto completo!
Requisitos:
- Computadora (mac)
- Arduino UNO.
- Llantas (2)
- Motores DC (2)
- Sensor Ultrasonico
- Mini Servo
- Arduino IDE (https://www.arduino.cc/en/Main/Software)
Primero hagamos un plan de ataque, plan de negocio o plan de diseño. La idea es la misma, planificar una ruta general de la cual nos podemos salir en cualquier momento pero que al menos nos da un norte a seguir en caso de no haber excepciones que cambien el juego.
- Primero tendremos que crear 4 objetos: 2 motores DC (izquierdo y derecho), 1 motor servo para girar el sensor ultrasonico y 1 sensor ultrasonico.
- Antes de tomar la primera medición, asignamos 0 a todo y comenzamos:
- Escribímos al servo para que gire “y vea” hacia el frente.
- Revisamos el camino frente a nosotros
- Arrancamos hacia el frente
- Estos pasos los hacemos 1 sola vez al arrancar operaciones.
- Luego repetidamente necesitamos:
- Revisamos que los motores esten listos al frente en cada ciclo
- Revisamos el camino frente a nosotros en cada ciclo
- Que significa?
Este algoritmo es sencillo. Claro uno mas sencillo seria simplemente detener la marcha de ambos DC si hay un obstáculo, pero entonces no seria un robot muy emocionante. La idea es ir refinando este algoritmo.
Desglosemos las acciones concretas en 4 funciones principales:
- checkPath()
- Hacemos un sweep de 144 a 36 (izquierda a derecha)
- checkForward() se usa para asegurarnos que vemos al frente
- leemos la distancia con readPing()
- Si la distancia es < 10cm = checkCourse y salir del sweep
- Si la distancia es < 20cm = changePath
- setCourse() – Si maxAngle < 90, turnRight() y vv
- turnRight() & turnLeft() – Enceder motor1 al frente y vv
- checkCourse() – retroceder, parar, setCourse()
- changePath() – Si pos < 90, veerLeft() y vv
- veerRight() & veerLeft() – Motores girar
- readPing() – retorna distancia
- checkForward() & checkBackward()
- moveForward() & moveBackward – endereza y acelera lentamente
- moveStop() – all stops
Veremos muchos problemas en al camino que nos exigirán refinar el algoritmo. Por ejemplo, mediciones falsas. En realidad no son mediciones falsas pero que tal si la onda del sensor de distancia pega en el suelo? O que tal si pega en un material que la absorbe? Esto resultaria en distancias al obstaculo increblemente cercas lo cual detendria el robot anticipadamente o peor, distancias erróneamente largas lo cual hara que el robot se estrelle con algo. En el caso de una medición falsa momentánea la solución por ejemplo es usar promedios moviles.
Componentes

El sensor ultrasonido se puede pegar con silicon transparente escolar a la paleta del servo. El alambrado es sencillo:
- El servo se conecta a la terminal de Servo2 en la shield
- El Sensor tiene 4 conexiones
- Vcc al pin en la shield soldado a la fila 5V
- GND al pin en la shield soldado a la fila GND
- Trig al pin en la shield soldado en A5
- Echo al pin en la shield soldado en A4
- Luego se conectan los motores DC al shield
- M1 en la shield al motor derecho
- M4 en la shield al motor izquierdo

Ahora solo resta conectar la shield sobre la Arduino UNO, el cable USB a la Arduino UNO y subimos la sketch. Vamos a subir la sketch y no tendremos los motores conectados a las llantas ni mucho menos el chasis. Solo queremos ver como funciona el sketch. Una vez cargada a la UNO podremos ver como se mueven los motores. Se debería mover tanto los DC como el servo.
Ahora podemos desconectar la UNO y comenzar a conectar los motores a las llantas y la UNO/Shield sobre el battery pack para luego montarlo a un chasis sobre las llantas.

Estudiemos el código:
#include <AFMotor.h> #include <Servo.h> #include <NewPing.h> #define TRIG_PIN A4 // Ping del sensor #define ECHO_PIN A5 // Echo del sensor #define MAX_DISTANCE 200 // Maxima distancia practica del sensor #define MAX_SPEED 180 // Maxima velocidad practica de los motores #define MAX_SPEED_OFFSET 10 // Offset de traction de los motores #define COLL_DIST 10 // Distancia critica de colisión #define TURN_DIST COLL_DIST+10 // Distancia de evasion NewPing sonar(TRIG_PIN, ECHO_PIN, MAX_DISTANCE); // Objeto del sensor AF_DCMotor motor1(1, MOTOR12_1KHZ); // Objeto para motor1 en M1 AF_DCMotor motor2(4, MOTOR12_1KHZ); // Objeto para motor2 en M4 Servo myservo; // Objeto para el servo int pos = 0; // Inicializamos variables usadas en sketch int maxDist = 0; int maxAngle = 0; int course = 0; int curDist = 0; String motorSet = ""; int speedSet = 0; void setup() { Serial.begin(9600); myservo.attach(9); // Pin 9 para servo myservo.write(90); // Ver frente checkPath(); // Analizar mapa motorSet = "FORWARD"; // Flag indicador para motores Forward moveForward(); // Marcha al frente } void loop() { checkForward(); // Asegurar que vamos marcha al frente checkPath(); // Analizar mapa void checkPath() { Serial.println("Mapeando el camino..."); int curLeft = 0; int curFront = 0; int curRight = 0; int curDist = 0; myservo.write(144); // Mirar servo a la izquierda delay(120); // Esperar que llegue el servo for(pos = 144; pos >= 36; pos-=18) // Loop para mover servo en incrementos de 18 { myservo.write(pos); // Mover servo a pos delay(90); // Esperar que servo llegue checkForward(); // Asegurar que aun marchamos al frente curDist = readPing(); // Leer distancia al objeto // ALERTAS if (curDist < COLL_DIST) { // Peligro, retroceder, parar y buscar camino nuevo checkCourse(); break; // Salir del loop } if (curDist < TURN_DIST) { // Alerta, viremos para no chocar changePath(); // Cambiar rumbo } if (curDist > maxDist) { // Almacenar maxDist & maxAngle maxAngle = pos; maxDist = curDist; Serial.println("curDist > maxDist, maxDist has been set!"); } } } void setCourse() { // Nuevo curso basado en mapa if (maxAngle < 90) {turnRight();} if (maxAngle > 90) {turnLeft();} maxLeft = 0; maxRight = 0; maxFront = 0; } void checkCourse() { // PELIGRO, vamos a chocar moveBackward(); delay(500); moveStop(); setCourse(); } void changePath() { if (pos < 90) {veerLeft();} // ALERTA, virar para evadir if (pos > 90) {veerRight();} } int readPing() { // Leer distancia delay(70); unsigned int uS = sonar.ping(); int cm = uS/US_ROUNDTRIP_CM; Serial.println(cm); return cm; } void checkForward() { if (motorSet=="FORWARD") {motor1.run(FORWARD); motor2.run(FORWARD); } } // Asegurar marcha al frente void checkBackward() { if (motorSet=="BACKWARD") {motor1.run(BACKWARD); motor2.run(BACKWARD); } } // Asegurar marcha atras void moveStop() { motor1.run(RELEASE); motor2.run(RELEASE);} // Detener void moveForward() { motorSet = "FORWARD"; motor1.run(FORWARD); // Marcha al frente motor2.run(FORWARD); // Marcha al frente for (speedSet = 0; speedSet < MAX_SPEED; speedSet +=2) // Acelerar lentamente { motor1.setSpeed(speedSet+MAX_SPEED_OFFSET); motor2.setSpeed(speedSet); delay(5); } } void moveBackward() { motorSet = "BACKWARD"; motor1.run(BACKWARD); // Marcha a atras motor2.run(BACKWARD); // Marcha a atras for (speedSet = 0; speedSet < MAX_SPEED; speedSet +=2) // Acelerar lentamente { motor1.setSpeed(speedSet+MAX_SPEED_OFFSET); motor2.setSpeed(speedSet); delay(5); } } void turnRight() { motorSet = "RIGHT"; motor1.run(FORWARD); // Motor 1 al frente motor2.run(BACKWARD); // Motor 2 atras delay(400); // run motors this way for 400ms motorSet = "FORWARD"; motor1.run(FORWARD); // Ambos hacia el frente motor2.run(FORWARD); } void turnLeft() { motorSet = "LEFT"; motor1.run(BACKWARD); // Motor 1 atras motor2.run(FORWARD); // Motor 1 al frente delay(400); // run motors this way for 400ms motorSet = "FORWARD"; motor1.run(FORWARD); // Ambos hacia el frente motor2.run(FORWARD); // Ambos hacia el frente } void veerRight() {motor2.run(BACKWARD); delay(400); motor2.run(FORWARD);} // Girar solo por 400ms void veerLeft() {motor1.run(BACKWARD); delay(400); motor1.run(FORWARD);} // Girar solo por 400ms
Ese proyecto se puede ver operando en modo de prueba aquí:
Finalmente agreguemos una “llanta” frontal para ver como se mueve el robot
Nuestro robot comienza a agarrar forma:
- Comunicación de datos (GPRS)
- Reconocimiento de voz
- Camara/PIR