Contact us
Leave a message

Mitglied bei

Logo "Bündnis '90/Die Grünen"
B'90/Grüne, OV Lehrte
Logo "Gospelchor 'Swing Low', Markus-Gemeinde, Lehrte"
Gospelchor "Swing Low", Markus-Gemeinde, Lehrte
Logo "Verein zum Erhalt klassischer Computer"
Verein zum Erhalt klassischer Computer

RoboCar

Autonomes Fahren? Alter Hut, machen wir schon lange!


Einführung

Autonom steuerndes Fahrzeugmodell aus ClipMont mit einem ESP8266-Microcontroller
Unser schmuckes, kleines Roboter-Auto

Die grundlegende Idee hinter dem kleinen Roboter-Auto stammt aus Make: Electronics von Charles Platt (O'Reilly, 2009). Dort beschreibt der Autor einen kleinen, motorgetriebenen Wagen, der automatisch kurz in den Rückwärtsgang schaltet, wenn er gegen ein Hindernis fährt. Das -- einzelne -- Hinterrad ist so aufgehängt, dass es beim Rückwärtsfahren zufällig in die eine oder andere Richtung schwenkt, so dass der Wagen, wenn er dann wieder vorwärts fährt, in eine andere Richtung als zuvor braust. Und dann hoffentlich am Hindernis vorbei!

Während Platt seinen Wagen über einen 555-Timer-Chip steuert, verwenden wir hier den NodeMUC, ein Entwicklungs-Board mit ESP8266-Microcontroller. Und statt aus ABS-Plastik bauen wir das Chassis mittels des -- wir wir finden: völlig zu unrecht! -- wenig bekannten Konstruktionsspielzeugs "ClipMont".

Die Steuerelektronik

Hier ein Versuch, das Schaltbild der RoboCar-Steuerung mit KiCad zu zeichnen:

Schaltbild einer ESP8266-basierten Steuerung für ein autonomes Modellfahrzeug
Schaltbild der RoboCar-Steuerung. Die beiden Kondensatoren C1 und C2 haben eine deutlich hörere Kapazität als im Datenblatt des Spannungsstabilisators empfohlen; ich hatte wohl keine anderen zur Hand.

Der Microcontroller kann über die zwei als Ausgang geschaltete GPIO-Pins (D1 und D2) die beiden als einpolige Wechselschalter ausgelegten Relais RE1 und RE2 umschalten; je nachdem, welches Relais umgeschaltet ist, kann der Motor M1 mit wechselnder Polarität mit Strom versorgt werden und somit in verschiedene Drehrichtungen laufen. Normalerweise dreht sich der Motor natürlich so, dass der Wagen vorwärts fährt.

Ein an der Front des RoboCars angebrachte Mikroschalter SW1 zieht den als Eingang geschalteten GPIO-Pin D3 auf Masse, wenn er gedrückt wird; das ist das Zeichen, dass der Wagen gegen ein Hindernis gefahren ist. Daraufhin kehrt der Microcontroller die Drehrichtung des Motors für ein paar Sekunden um, so dass der Wagen rückwärts fährt und schließlich -- wie oben beschrieben -- in eine andere Richtung zeigt.

Da der Strombedarf der Relais beim Schalten die Fähigkeiten des Controllers übersteigen würde, werden die Relais indirekt über je einen zwischengeschalteten Transistor (Q1, Q2) bedient.

Hier noch ein Foto des Steckbrett-Aufbaus sowie eine mit Fritzing erstellte Schemazeichnung davon:

Steckbrett-Schaltung der ESP8266-basierten Steuerung für ein autonomes Modellfahrzeug
Die RoboCar-Steuerung auf dem Steckbrett. Der Motor wird an den beiden oben abgehenden Kabeln angeschlossen, der Mikorschalter an den unteren beiden.
Zeichnung der Steckbrett-Schaltung der ESP8266-basierten Steuerung für ein autonomes Modellfahrzeug
Die Steckbrett-Schaltung noch mal ordentlich gezeichnet
Stromversorgung des autonomen Modellfahrzeugs
Blick in das aus zwei NiPo-Akkus bestehende "Kraftwerk" des RoboCars

Zur Stromversorgung dienen zwei parallel geschaltete Nickel-Polymer-Akkus, die jeweils über eine kleine, billig als Massenware erhältliche Ladeschaltung aufgeladen werden können. Die Verwendung zweier Akkus erwies sich als notwendig, da die Spannung eines einzelnen Akkus beim Startversuch eines Motors unter Last so stark einbrach, dass der Motor erst gar nicht in Gang kam.

Wie auch bein anderen Elektroautos üblich, haben wir die Akkus übrigens im Fahrzeugboden verbaut! ;)

Die Steuersoftware

Die Steuersoftware wurde mit dem Eclipse-Arduino-Plugin Sloeber programmiert. Hier der Quellcode, der auch -- wie weitere Dokumente -- von meinem entsprechenden GitLab-Projekt heruntergeladen werden kann:

//#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;
  }
}