coche-robot-2wd-barato-con-arduino-programacion-l298n

Coche robot 2WD Arduino- Programación L298N

Continuamos con la serie dedicada a nuestro primer proyecto de robot. Tras ver el presupuesto, montaje, y esquema eléctrico, por fin llegamos a nuestra primera sesión de programación.

Y sí, decimos primera sesión, porque va a haber más de una entrada dedicada a la programación del robot. Lógico, teniendo en cuenta la insistencia que hemos hecho en que es un robot que puede tener múltiples comportamientos y programaciones distintas.

Además, hay mucho que contar para una única sesión, así que iremos pasito a pasito. En esta entrada vamos a ver cómo controlar los motores con el controlador L298N, y nos servirá también para ver la dinámica general que vamos a seguir en estas entradas.

No me enrollo más que sé que a estas alturas estáis deseando ver código, código y código. Así que vamos a ello.

Ejemplo control del L298N

En realidad, una programación básica del robot que haga uso del controlador L298N no es en absoluto difícil. De hecho, ya vimos el código necesario en la entrada sobre el controlador L298N.

Este es el ejemplo que vimos en su día, que tiene funciones para mover adelante, y detrás, y un pequeño loop que realiza ambos movimientos.

const int pinIN1 = 5;
const int pinIN2 = 6;
const int pinIN3 = 7;
const int pinIN4 = 8;
const int pinENA = 9;
const int pinENB = 10;

const int waitTime = 2000;  //espera entre fases
const int speed = 200;    //velocidad de giro

const int pinMotorA[3] = { pinENA, pinIN1, pinIN2 };
const int pinMotorB[3] = { pinENB, pinIN3, pinIN4 };

void setup()
{
  pinMode(pinIN1, OUTPUT);
  pinMode(pinIN2, OUTPUT);
  pinMode(pinENA, OUTPUT);
  pinMode(pinIN3, OUTPUT);
  pinMode(pinIN4, OUTPUT);
  pinMode(pinENB, OUTPUT);
}

void loop()
{
  moveForward(pinMotorA, 180);
  moveForward(pinMotorB, 180);
  delay(waitTime);

  moveBackward(pinMotorA, 180);
  moveBackward(pinMotorB, 180);
  delay(waitTime);

  fullStop(pinMotorA);
  fullStop(pinMotorB);
  delay(waitTime);
}

void moveForward(const int pinMotor[3], int speed)
{
  digitalWrite(pinMotor[1], HIGH);
  digitalWrite(pinMotor[2], LOW);

  analogWrite(pinMotor[0], speed);
}

void moveBackward(const int pinMotor[3], int speed)
{
  digitalWrite(pinMotor[1], LOW);
  digitalWrite(pinMotor[2], HIGH);

  analogWrite(pinMotor[0], speed);
}

void fullStop(const int pinMotor[3])
{
  digitalWrite(pinMotor[1], LOW);
  digitalWrite(pinMotor[2], LOW);

  analogWrite(pinMotor[0], 0);
}

Códigos similares a estos podréis encontrarlos cientos de veces por Internet. Más limpios, menos limpios, pero similares. Podemos seguir añadiendo funciones para girar, controlar los encoders, activar sensores, etc.

El problema de esta programación es que, aunque sencilla y didáctica, en seguida crece incontroladamente de tamaño, y se convierte en lo que se llama “espagueti” code.

Nosotros no queremos hacer esto, así que vamos a ver como pasar de una programación funcional a una programación orientada a objetos y con una arquitectura definida.

Robot Car en objetos

Vamos a ver cómo implementar el control de motores de una forma más organizada. Todo el código siguiente lo tenéis disponible en GitHub en este enlace, así que no os preocupéis ahora de copiar y pegar el código, sólo nos detendremos en cómo se organiza el robot. Por ello, sólo nos vamos a detener en las declaraciones de cada clase, no su implementación, que podéis consultar en el código de GitHub. github-full En primer lugar, es evidente que tenemos un robot que tiene dos motores. Parece una tontada bien gorda, pero es el primer paso pasa comenzar la abstracción. Necesitamos una clase “Robot” y una clase “motor”. A estas clases les añadiremos clases “encoder”, “sensor”; “comportamiento”, “comunicación”, y podremos juntarlos y combinarlos. Pero vayamos poco a poco.

RobotCar Constants

En primer lugar, tenemos un fichero RobotCar_Constants, que contendrá todas las constantes de nuestro robot (datos de geometría, conexiones, etc). En este primer ejemplo, únicamente tenemos los seis pines de control del L298N.

const int pinIN1 = 5;
const int pinIN2 = 6;
const int pinIN3 = 7;
const int pinIN4 = 8;
const int pinENA = 9;
const int pinENB = 10;

RobotCar L298N

Por otro lado, tenemos una clase llamada RobotCar_L298N, que controla un único motor del robot. Aquí tenemos la declaración de la clase, con los principales métodos para controlar el sentido y velocidad de giro del motor.


#ifndef _ROBOTCAR_L298N_h

#define _ROBOTCAR_L298N_h

#include <Arduino.h>

class RobotCar_L298N
{
public:
  void Init(int pinIN1, int pinIN2, int pinENA);
  void Stop() const;
  void MoveForward() const;
  void MoveForward(uint8_t speed) const;
  void MoveBackward() const;
  void MoveBackward(uint8_t speed) const;

private:
  int8_t _pinIN1;
  int8_t _pinIN2;
  int8_t _pinENA;
};


#endif

RobotCar Lib

Por su parte, la clase RobotCarLib, que representa la clase principal del robot. De momento, nuestro robot consta de dos motores, izquierdo y derecho, y de las funciones para mover el robot, y girar con dos ruedas (Turn) o una rueda(SlowTurn).


#ifndef _ROBOTCARLIB_h

#define _ROBOTCARLIB_h

#include "Arduino.h"
#include "RobotCar_L298N.h"

class RobotCarLib
{
public:
  void Init();

  void Stop() const;

  void MoveForward() const;
  void MoveForward(uint8_t speed) const;
  void MoveBackward() const;
  void MoveBackward(uint8_t speed) const;

  void TurnLeft() const;
  void TurnLeft(uint8_t speed);
  void TurnRight() const;
  void TurnRight(uint8_t speed) const;

  void SlowTurnLeft() const;
  void SlowTurnLeft(uint8_t speed) const;
  void SlowTurnRight() const;
  void SlowTurnRight(uint8_t speed) const;

private:
  RobotCar_L298N _motorLeft;
  RobotCar_L298N _motorRight;
};


#endif

Sketch test motores

Finalmente tenemos el sketch, que simplemente instancia un robot y le ejecuta una serie de movimientos para comprobar su funcionamiento.

#include "RobotCarLib.h"

RobotCarLib robot;

const int waitTime = 2000;

void setup()
{
  robot.Init();
}

void loop()
{
  robot.MoveForward();
  delay(waitTime);

  robot.MoveBackward();
  delay(waitTime);

  robot.TurnLeft();
  delay(waitTime);

  robot.TurnRight();
  delay(waitTime);

  robot.Stop();
  delay(waitTime);
}

Probando el código de nuestro Robot

Subimos el código a nuestro robot y comprobamos que todo funciona correctamente. Si alguno de los motores gira en sentido equivocado (o ambos) podéis, o bien cambiar los cables físicamente en la clema de conexión del L298N, o intercambiar los pines IN1 e IN2 (para el motor A) o IN3 e IN4 para el motor B).

De forma similar, si tenéis los motores intercambiados (el izquierdo es el derecho y viceversa), simplemente cambiar en código los pines del grupo IN1, IN2, ENA, con el grupo IN3, IN4, ENB.

Cuando consigáis que el robot avance en orden, adelante, detrás, girar a la izquierda, girar a la derecha, parar… ¡Enhorabuena! Habéis terminado este primer tutorial, y tenéis un robot funcionando.

Este es el resultado de esta primera sesión de programación. No es demasiado espectacular, pero ya estamos controlando los movimientos básicos.

Lamentablemente, de momento sólo podemos moverlo con funciones programadas por tiempo. No podemos controlar la distancia que recorremos, ni el ángulo que giramos. Es más, es posible que vuestro robot no vaya totalmente recto, y tenga una deriva a izquierda o derecha.

Es el problema de no tener funcionando los encoders. Pero tranquilos, vemos esto en las próximas entradas de programación de nuestro primer robot.

Descarga el código

Todo el código de esta entrada está disponible para su descarga en Github. github-full