Contact us
Leave a message

Please use:

My public cryptographic key


Member of

RoboCar

Autonomous driving? Pretty old hat for us -- we did it already!


Introduction

The underlying idea for this little robotic cart is described in Make: Electronics by Charles Platt (O'Reilly, 2009). In this book, the author describes a little engine-powered cart which automatically switches to driving backwards for a few seconds when hitting an obstacle. The -- single -- back wheel is mounted through a hinge, allowing it to randomly flip to the one side or the other while driving backwards. This has the effect that the cart will point into a different direction than before when it eventually starts moving forward again, thus allowing it to (hopefully) overcome the obstacle by passing it laterally.

While Platt used a 555 timer chip to control his cart, we utilize a NodeMCU development board, featuring an ESP8266 microcontroller. And instead from ABS plasctic, we built our cart's frame using an -- in our opinion: totally unjustifiedly -- little known construction toy called "ClipMont".

Cruise Control Electronics

This is an attempt to sketch RoboCar's control circuit using KiCad:

The microcontroller uses GPIOs D1 and D2 to -- mutually exclusively -- switch over the SPCO (single pole change over) relays RE1 and RE2. Depending on which relay is switched, the motor M is powered with opposite polarities, causing it to run into different directions. Of course, normally the motor drives the cart forwards.

By pulling GPIO input D3 to ground, micro-switch SW1 (which is mounted at the cart's front) signals the presence of an obstacle. When this happens, the microcontoller reverses the motor's direction of rotation for a few seconds, causing the cart to drive backwards and to point into a different direction, as explained above.

Since a switching relay's power consumption exceeds the microcontoller's capacity to yield current, the relays don't get switched directly, but through intermediary transistors Q1 and Q2.

Here's a photo of the control circuit assembled on a breadboard and a schematic drawing thereof created with Fritzing:

The cart is powered by two lithium polymer batteries, which can be charged by means of small charging boards which are cheaply available as mass-products.

Using two batteries turned out to be necessary since when trying to start the motor under load, a single battery's voltage would drop so much that the motor wouldn't even start moving.

According to a common design principle of electric vehicles, we, by the way, built the batteries into the cart's base plate.

Cruise Control Software

The cart's control software was develuped using the Eclipse Arduino plugin Sloeber. Here's the source code, which is -- among other documents -- also available from my corresponding GitLab projekt:

//#include "Arduino.h"
#include <ESP8266WiFi.h>

const unsigned short STATUS_READY = 0;
const unsigned short STATUS_FORWARD = 1;
const unsigned short STATUS_SWITCHING1 = 2;
const unsigned short STATUS_BACKWARD = 3;
const unsigned short STATUS_SWITCHING2 = 4;

const unsigned long BACKWARD_DURATION_MS = 2000;
const unsigned long SWITCHING_DURATION_MS = 1000;

const short MOTOR_STOPPED = 0;
const short MOTOR_RUNNING_FORWARD = 1;
const short MOTOR_RUNNING_BACKWARDS = -1;

const unsigned int LED_PIN = D0;
const unsigned int FORWARD_PIN = D1;
const unsigned int BACKWARD_PIN = D2;
const unsigned int BUTTON_PIN = D3;

unsigned short status = STATUS_READY;
short motor = MOTOR_STOPPED;
unsigned long lastSwitchedAt = 0;
volatile bool switchToBackward = false;

void setup() {
  Serial.begin(9600);
  Serial.setTimeout(2000);
  Serial.write("Setting up... \n");

  pinMode(FORWARD_PIN, OUTPUT);
  pinMode(BACKWARD_PIN, OUTPUT);
  setMotorStop();

  pinMode(BUTTON_PIN, INPUT_PULLUP);

  pinMode(LED_PIN, OUTPUT);
  for (int i = 0; i < 3; i++) {
    digitalWrite(LED_PIN, LOW);
    delay(150);
    digitalWrite(LED_PIN, HIGH);
    delay(150);
  }

  attachInterrupt(BUTTON_PIN, onButton, RISING);

  Serial.write("Setup finished.\n");
}



// The loop function is called in an endless loop
void loop() {
  //Serial.write("Loop started...\n");

  switch(status) {
  case STATUS_FORWARD:
    if (motor != MOTOR_RUNNING_FORWARD) {
      setMotorForward();
    }
    if (switchToBackward) {
      switchToBackward = false;
      status = STATUS_SWITCHING1;
    }
    break;
  case STATUS_SWITCHING1:
    if (motor != MOTOR_STOPPED) {
      setMotorStop();
    }
    if (millis() - lastSwitchedAt > SWITCHING_DURATION_MS) {
      status = STATUS_BACKWARD;
    }
    break;
  case STATUS_BACKWARD:
    if (motor != MOTOR_RUNNING_BACKWARDS) {
      setMotorBackward();
    }
    if (millis() - lastSwitchedAt > BACKWARD_DURATION_MS) {
      status = STATUS_SWITCHING2;
    }
    break;
  case STATUS_SWITCHING2:
    if (motor != MOTOR_STOPPED) {
      setMotorStop();
    }
    if (millis() - lastSwitchedAt > SWITCHING_DURATION_MS) {
      status = STATUS_FORWARD;
    }
    break;
  default:
    status = STATUS_FORWARD;
    break;
  }

  //Serial.write("Loop finished.\n");
}

void setMotorStop() {
  digitalWrite(FORWARD_PIN, LOW);
  digitalWrite(BACKWARD_PIN, LOW);
  digitalWrite(LED_PIN, HIGH);
  motor = MOTOR_STOPPED;
  lastSwitchedAt = millis();
  Serial.write("Motor stopped.\n");
}

void setMotorForward() {
  digitalWrite(FORWARD_PIN, HIGH);
  digitalWrite(BACKWARD_PIN, LOW);
  digitalWrite(LED_PIN, LOW);
  motor = MOTOR_RUNNING_FORWARD;
  lastSwitchedAt = millis();
  Serial.write("Motor running forward.\n");
}

void setMotorBackward() {
  digitalWrite(FORWARD_PIN, LOW);
  digitalWrite(BACKWARD_PIN, HIGH);
  digitalWrite(LED_PIN, LOW);
  motor = MOTOR_RUNNING_BACKWARDS;
  lastSwitchedAt = millis();
  Serial.write("Motor running backwards.\n");
}

void onButton() {
  Serial.write("* Button pressed!\n");
  if (status == STATUS_FORWARD && !switchToBackward) {
    switchToBackward = true;
  }
}