Smart Robot Car Kit mit allen wichtigen Komponenten

Erinnern Sie noch die Blog-Serie zum Thema Robot Car oder Smart Car? Im Frühjahr 2021 hatten wir dieses Thema von verschiedenen Seiten beleuchtet und insbesondere auch verschiedene Möglichkeiten der Steuerung aufgezeigt. Alles, was seinerzeit gefehlt hatte, war die passende Hardware im Sortiment. Nun kommt ein Robot Car Kit, das keine Wünsche offenlässt. Alles dabei, um ein ferngesteuertes Smart Car zu realisieren.

Car Kit

Im Folgenden möchte ich zunächst die weniger bekannten Komponenten vorstellen: das Motor Controller Board mit L298N, das Sensor Shield V5 und eine einfache Fernsteuerung mit IR-Fernbedienung. Am meisten begeistert mich die rechteckige Aussparung im Chassis für den Einbau eines kleinen Servos, an dem der Abstandssensor HC-SR04 angeschraubt wird. Damit müsste es möglich sein, das Smart Car noch besser auf Hindernisse reagieren zu lassen und so dem autonomen Fahren ein Stück näher zu kommen. Das hatte ich bisher nirgendwo gesehen und kenne auch keine Lösung für den bestmöglichen Einsatz dieses nun schwenkbaren Sensors. 

Motor Controller Board

Das Motor Controller Board mit dem IC L298N hatte Jörn Weise bereits in seinem Blog- Beitrag über die H-Brücke vorgestellt. An dem großen Kühlkörper erkennt man, dass dieser Baustein auch deutlich größere Gleichstrom-Motoren, als die weit verbreiteten kleinen gelben Motoren mit Spannung versorgen kann. Zur Erinnerung: Kein Mikrocontroller liefert so viel Strom an den GPIOs, dass die Motoren drehen können. Von hier kommen nur die Steuersignale. Der Motor Controller hat drei Aufgaben: erstens genügend elektrische Energie (Spannung und Stromstärke!) bereitstellen, zweitens das Umpolen für die Fahrtrichtungsänderung zu ermöglichen (H-Brücke) und drittens die Geschwindigkeit (Drehzahl des Motors) zu steuern. Die Signale zum Umpolen und zur Geschwindigkeit kommen vom Mikrocontroller.

Die blauen Schraubanschlüsse (Terminal Blocks) sind die Anschlüsse zur „Außenwelt“. Jeweils links und rechts zwei Anschlüsse für die beiden Motoren. Wenn am Ende die Drehrichtung der Motoren nicht stimmt, hat man zwei Möglichkeiten: entweder hier umpolen, oder die Anschlussbelegung im Sketch ändern. Der Dreierblock dient der externen Spannungsversorgung: ganz links wird der Pluspol von Batterie/Akku angeschlossen, in der Mitte der Minuspol (immer auch mit GND am Mikrocontroller verbinden) und rechts befindet sich ein 5V-Ausgang, um optional den Mikrocontroller damit zu versorgen. Sofern die externe Spannung kleiner oder gleich 12 Volt ist, bleibt der Jumper oberhalb des Terminal Blocks gesteckt, oberhalb 12 Volt wird er entfernt.

Die Pins unten rechts im Bild sind die sechs Anschlüsse zum Mikrocontroller (drei je Motor). Von links nach rechts: ENA - IN1 - IN2 - IN3 - IN4- ENB. 

Im Auslieferungszustand befinden sich Jumper auf den beiden Enable Pins ENA und ENB. Diese entfernen wir, damit wir hier die jeweiligen PWM-Signale einspeisen können. IN1 bis IN4 können an normale GPIO-Ausgänge angeschlossen werden, ENA und ENB gehen an PWM-fähige Pins (das sind die mit der Tilde ~).

Im Beispiel-Sketch (siehe unten) sind die Motoranschlüsse wie folgt festgelegt:
// Motor pins

int EnA = 6;

int IN1 = 9;

int IN2 = 8;  

int EnB = 5;

int IN3 = 7;

int IN4 = 10;   


Wie gesagt, wenn Seite oder Drehrichtung nicht passen, kann man hier die Belegung ändern, oder die Anschlüsse am Terminal Block umpolen.

Sensor Shield V5.0

Das Arduino Sensor Shield V5.0 wird wie üblich auf den Mikrocontroller mit ATmega328 aufgesteckt und bietet verschiedenartige Anschlussmöglichkeiten, dabei auch Mehrfachbelegungen der Pins.

Download Datenblatt

In der Mitte befinden sich sämtliche digitalen und analogen Ein- und Ausgänge. Das Schöne ist, dass neben jedem Pin auch die Anschlüsse für Ground und die Versorgungsspannung liegen, sodass man hier endlich genügend Möglichkeiten hat. 

Rechts im Bild sind die COM-Anschlüsse, auch für Bluetooth. Schön sind auch hier die Anschlüsse für die Spannungsversorgung, jedoch gehen die Signalleitungen an D1 und D0, also TX und RX. Das bedeutet, Verzicht auf den Serial Monitor im Betrieb und Trennen dieser Anschlüsse beim Hochladen des Sketches. Ich persönlich bevorzuge SoftwareSerial an beliebigen anderen Pins.

Rechts unten ist ein Ultra Sonic-Interface, das die analogen Eingänge A0 und A1 verwendet. Kann man machen, denn das sind ja auch digitale Ein- oder Ausgänge, man muss es nur wissen, falls man die analogen Eingänge verwenden möchte.

Optionaler Aufbau mit Display

Am Spannendsten (weil für mich neu) waren die Anschlüsse für das SD Card-Interface und das LCD12864. Ein SD-Kartenleser macht für ein Sensor Shield absolut Sinn, wenn man Messdaten aufnehmen und speichern möchte. Bei dem Beispiel-Sketch Datalogger musste ich nur die Zeile
const int chipSelect = 4; ändern in const int chipSelect = 10;
Ich war überrascht, wie schnell und einfach das ging.

Beim LCD12864 habe ich mich für den seriellen Anschluss entschieden (im Bild oben rechts) und bin den Ausführungen des eBooks gefolgt. Anders als die Displays LCD1602 und 2004, die vordefinierte Stellen für Zeichen haben, ist dieses ein Grafikdisplay mit einer Auflösung von 128x64 = 8192 Pixeln. Das erfordert eine andere Programmbibliothek, die jedoch vielfach, u.a. auch für TFT Displays, verwendet wird und deshalb bekannt ist. Wie so häufig gibt es auch für diese Bibliothek Beispielprogramme. Im eBook ist der Beispiel-Sketch GraphicsTest auf das wesentliche gekürzt worden.

Nach dem Inkludieren  mit
#include <U8g2lib.h>
werden bei der folgenden Zeile die beiden Schrägstriche entfernt und anstelle der üblichen SPI-Pins die Pins 4, 3, 2 verwendet.
U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0, 4, 3, 2);    // anstelle von 13, 11, 10

Dabei verbinden wir SCL=4 an LCD-Pin E, MOSI=3 an LCD-R/W und CE=2 an LCD-RS.

Fazit Sensor Shield: Sehr funktionales Add-on, das für den Anschluss von mehreren Sensoren, einem SD Card Reader und dem LCD12864 absolut sinnvoll ist, für die spartanische Ausführung eines Robot Cars mit sechs GPIOs für die Motoren und einem GPIO für den IR-Sensor verzichtbar und bei anderen Projekten besser eingesetzt.

IR-Fernbedienung

Nach diesen Versuchen habe ich die IR-Fernbedienung ausprobiert. Dabei habe ich eine sehr gute Programmbibliothek entdeckt. Ich habe sie mit meinen Robot Car Sketch mit der selbstgebauten Fernsteuerung mit LCD-Keypad und dem 433 MHz Transceiver kombiniert.

Dabei hatte ich Code entwickelt, der in Fahrtstufen umgewandelt wird. Wegen der Verwendung eines Joysticks in anderen Varianten liegt die Fahrtstufe Stillstand bei Code 505. Die Hunderter-Stelle erhöht die Geschwindigkeit bei Vorwärtsfahrt von 6 bis 10, bei Rückwärtsfahrt von 4 bis 0. Kurvenfahrten werden über Erhöhung oder Verminderung der Einer-Stelle initiiert.

Diese Veränderungen kann man wunderbar mit den Cursortasten einer IR-Fernbedienung realisieren. Das Programm fragt ab, welche Taste gedrückt wurde und passt den Code entsprechend an. Wenn das Maximum bzw. Minimum bereits erreicht wurde, wird nichts mehr verändert.  Die rote OK-Taste in der Mitte führt zum Schnell-Stopp mit Code 505, alle anderen Signale werden ignoriert. 

Um den Lesern viele Möglichkeiten bei der Nutzung der IR-Bibliothek von Armin Joachimsmeyer zu erhalten, habe ich diese weitgehend unverändert in meinem Sketch eingebaut. Das bedeutet, dass seine Hilfsdatei PinDefinitionsAndMore.h im gleichen Sketch-Verzeichnis vorhanden sein muss. Wer Speicherplatz optimieren möchte, kann sicherlich den IR-Pin=2 auch im Hauptprogramm definieren.

Der Distanzsensor HC-SR04 wird an Pins 3 und 4 angeschlossen, das Servo an Pin 12.

Quellcode

Im der Funktion setup() wird das Servo mit myservo.attach(12); initialisiert, getestet und mittig (nach vorn) ausgerichtet. Um den IR-Sensor nicht zu stören, wird das Servo danach wieder mit myservo.detach(); deaktiviert. 

Um Kollisionen zu verhindern, wird in jeder loop() die selbst-definierte Funktion distancemeasuring() aufgerufen. Bei Abständen unter 10 cm werden die Motoren automatisch gestoppt und der Code auf 505 (Stillstand) gesetzt. An dieser Stelle ist der Code ausbaufähig, um eine gewisse Autonomie beim Fahren zu erreichen, z.B. Servo 45° und 90° nach links und rechts schwenken, besseren Ausweg finden, einige cm rückwärtsfahren und Kurve einleiten. Ich habe es zunächst beim Schnellstopp belassen.

Hier der Sketch (Download)

/* Demo Program for Robot Car with IR Remote Controller
 * based on SimpleReceiver.cpp, which is part of Arduino-IRremote library 
 * https://github.com/Arduino-IRremote/Arduino-IRremote.
 * MIT License  Copyright (c) 2020-2022 Armin Joachimsmeyer
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 ************************************************************************************
 */

/*
 * Specify which protocol(s) should be used for decoding.
 * If no protocol is defined, all protocols are active.
 * For first use uncomment line //#define INFO 
 */
//#define DECODE_DENON        // Includes Sharp
//#define DECODE_JVC
//#define DECODE_KASEIKYO
//#define DECODE_PANASONIC    // the same as DECODE_KASEIKYO
//#define DECODE_LG

#define DECODE_NEC          // Includes Apple and Onkyo - kleine Fernbedienung

//#define DECODE_SAMSUNG
//#define DECODE_SONY
//#define DECODE_RC5

//#define DECODE_RC6           // works fine with my Philips DVD Remote Controller

//#define DECODE_BOSEWAVE
//#define DECODE_LEGO_PF
//#define DECODE_MAGIQUEST
//#define DECODE_WHYNTER
//#define DECODE_DISTANCE     // universal decoder for pulse width or pulse distance protocols
//#define DECODE_HASH         // special decoder for all protocols
//#define DEBUG               // Activate this for lots of lovely debug output from the decoders.
//#define INFO                // To see valuable informations from universal decoder for pulse width or pulse distance protocols

#include <Arduino.h>
/*
 * Define macros for input and output pin etc.
 */
#include "PinDefinitionsAndMore.h"
#include <IRremote.hpp>

#include <Servo.h>
Servo myservo;  // create servo object to control a servo
int pos = 90;    // variable to store the servo position

// Motor pins
int EnA = 6; int IN1 = 9; int IN2 = 8;  
int EnB = 5; int IN3 = 7;  int IN4 = 10;   

int x = 0;
int y = 0;
int left = 0;
int right = 0;
int code = 505;
//int codeRead = 505;
int speedL = 0;
float factor = 0.65; // Korrektur für Fahrtstufe

// Distance Sensor HC-SR04, trigger at pin 4, echo at pin 3
int trigger=4;     // Trigger-Pin 
int echo=3;        // Echo-Pin
long travelTime=0; // Variable travelTime, initially 0
long distance=0;   // Variable distance, initially 0

void setup() {
  myservo.attach(12);    // attaches the servo on pin 12 to the servo object  
  myservo.write(0);    // tell servo to go to position in variable 'pos'
  delay(500);
  myservo.write(180);    // tell servo to go to position in variable 'pos'  
  delay(500);
  myservo.write(90);    // tell servo to go to position in variable 'pos'
  delay(500);  
  myservo.detach();     // absolutely necessary, because servo disturbs IR receiver !
  // initialize digital pins as an output.
  pinMode(IN1, OUTPUT);  pinMode(IN2, OUTPUT); //Motor 1 
  pinMode(IN3, OUTPUT);  pinMode(IN4, OUTPUT); //Motor 2  
  // Distance Sensor
  pinMode(trigger, OUTPUT); // Trigger-Pin is output
  pinMode(echo, INPUT); // Echo-Pin is input
   
  Serial.begin(115200);
  // Just to know which program is running on my Arduino
  Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE));

  // Start the receiver and if not 3. parameter specified, take LED_BUILTIN pin from the internal boards definition as default feedback LED
  IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);

  Serial.print(F("Ready to receive IR signals of protocols: "));
  printActiveIRProtocols(&Serial);
  Serial.print(F("at pin "));
  Serial.println(IR_RECEIVE_PIN);

  Serial.println("Motor test!");
  Serial.println(" ");
  }   // end setup

void loop() {
  distancemeasuring();   
  if (IrReceiver.decode()) {
      // Print a short summary of received data
      IrReceiver.printIRResultShort(&Serial);
      if (IrReceiver.decodedIRData.protocol == UNKNOWN) {
          // We have an unknown protocol here, print more info
          IrReceiver.printIRResultRawFormatted(&Serial, true);
      }
      Serial.println();
      delay(100);   // Entprellen, keine schnelle Wiederholung  
      /*
       * !!!Important!!! Enable receiving of the next value,
       * since receiving has stopped after the end of the current received data packet.
       */
      IrReceiver.resume(); // Enable receiving of the next value
      /*
       * Finally, check the received data and perform actions according to the received command
       */
      int IR_Signal = IrReceiver.decodedIRData.command;
      Serial.print("IR_Signal = ");
      Serial.println(IR_Signal,HEX);  
      if (IR_Signal == 0x46)   {
        if (code<911)   code = code + 100;         
        Serial.print("Code = ");
        Serial.println(code);
        }
      else if (IR_Signal == 0x15)   {
        if (code>99)   code = code - 100;      
        Serial.print("Code = ");
        Serial.println(code);
        }  
      else if (IR_Signal == 0x43)   {
        if ((code-100*int(code/100))<10)  code = code + 1;      
        Serial.print("Code = ");
        Serial.println(code);
        }
      else if (IR_Signal == 0x44)   {
        if (code-100*int(code/100) > 0)   code = code - 1;
        Serial.print("Code = ");
        Serial.println(code);
        }
      else if (IR_Signal == 0x40)   {
        code = 505;
        Serial.print("Code = ");
        Serial.println(code);
        }
      else   {
        Serial.print("invalid Code");
      }
      motor();
      }
  }   // end loop


void distancemeasuring()   {
  digitalWrite(trigger, LOW); //Hier nimmt man die Spannung für kurze Zeit vom Trigger-Pin, damit man später beim senden des Trigger-Signals ein rauschfreies Signal hat.
  delay(5);     //travelTime: 5 Millisekunden
  digitalWrite(trigger, HIGH); //Jetzt sendet man eine Ultraschallwelle los.
  delay(10);    //Dieser „Ton“ erklingt für 10 Millisekunden.
  digitalWrite(trigger, LOW);//Dann wird der „Ton“ abgeschaltet.
  travelTime = pulseIn(echo, HIGH); //Mit dem Befehl „pulseIn“ zählt der Mikrokontroller die Zeit in Mikrosekunden, bis der Schall zum Ultraschallsensor zurückkehrt.
  distance = (travelTime/2) * 0.03432; //Nun berechnet man die distance in Zentimetern. Man teilt zunächst die Zeit durch zwei (Weil man ja nur eine Strecke berechnen möchte und nicht die Strecke hin- und zurück). Den Wert multipliziert man mit der Schallgeschwindigkeit in der Einheit Zentimeter/Mikrosekunde und erhält dann den Wert in Zentimetern.
  if (distance >= 500 || distance <= 0)   {
    Serial.println("Kein Messwert"); //dann soll der serial monitor ausgeben „Kein Messwert“, weil Messwerte in diesen Bereichen falsch oder ungenau sind.
    return;
  }
  else if (distance <10)  {
    code = 505;
    analogWrite(EnA,0);
    analogWrite(EnB,0);
    Serial.println();
    Serial.println("Distanz unter 10 cm");
    Serial.println();
    return;
  }
  else   {
    return;
  }
}

void motor()  {
  int speedLevel[11]={-255,-210,-165,-130,-100,0,100,130,165,210,255};
  y = int(code /100);
  x = code - 100*y;
  speedL = speedLevel[y];
  Serial.print("code = ");
  Serial.print(code);
  Serial.print(" y = ");
  Serial.print(y);
  Serial.print(" x = ");
  Serial.print(x);
  Serial.print(" speedL = ");
  Serial.println(speedL);

  //Korrektur der Fahrtstufen für Kurvenfahrt
  if (x==0){
    right = speedL+160;
    left = speedL-160;
  }
  else if (x==1){
    right = speedL+120;
    left = speedL-120;
  }
  else if (x==2){
    right = speedL+90;
    left = speedL-90;
  }
  else if (x==3) {
    right = speedL+60;
    left = speedL-60;
  }
  else if (x==4) {
    right = speedL+30;
    left = speedL-30;
  }
  else if (x==6) {
    right = speedL -30;
    left = speedL+30;
  }
  else if (x==7) {
    right = speedL-60;
    left = speedL+60;
  }
  else if (x==8) {
    right = speedL-90;
    left = speedL+90;
  }
  else if (x==9) {
    right = speedL-120;
    left = speedL+120;
  }
  else if (x==10) {
    right = speedL-150;
    left = speedL+150;
  }
  else {
    right = speedL;
    left = speedL;
  }

  //Eingabe der Fahrtstufen für "left" und "right"
  Serial.print("left = ");
  Serial.print(left);
  Serial.print(" right = ");
  Serial.println(right);

  if (left < 80 && left > -80)   {
    analogWrite(EnA,0);
  }
  if (right < 80 && right > -80)   {
    analogWrite(EnB,0);
  }

  if (left>=80) {
    if (left>255) left=255;
    digitalWrite(IN1,1);
    digitalWrite(IN2,0);
    left = factor*left;
    analogWrite(EnA,left);
    Serial.print("left*factor = ");
    Serial.println(left); 
  }
  if (right>=80) {
    if (right>255) right=255;
    digitalWrite(IN3,1);
    digitalWrite(IN4,0);
    right = factor*right;
    analogWrite(EnB,right);
    Serial.print("right*factor = ");
    Serial.println(right);     
  }
  if (left<= -80) {
    if (left<-255) left=-255;
    digitalWrite(IN1,0);
    digitalWrite(IN2,1);
    left = -factor*left;
    analogWrite(EnA,left);
    Serial.print("left*factor = ");
    Serial.println(left);     
  }
  if (right<= -80) {
    if (right<-255) right=-255;
    digitalWrite(IN3,0);
    digitalWrite(IN4,1);
    right = -factor*right;
    analogWrite(EnB,right);
    Serial.print("right*factor = ");
    Serial.println(right);     
  }
}

Fazit

Preiswürdiges und gut ausgestattetes Robot Car Kit, mit dem Servo für den Distanzsensor ein großer Schritt in Richtung autonomes Fahren. Allerdings gibt es Störungen des IR-Empfängers beim Betrieb des Servos. Das ist jedoch leicht zu umgehen, indem man entweder die Distanzen im Stillstand misst, oder anstelle der IR-Fernbedienung Bluetooth, 433MHz oder 2,4 GHz Transceiver benutzt.

Für arduinoProjekte für anfängerSensoren

8 commentaires

Michael

Michael

BlogEine Bauanleitung wäre fein, der Blog Beitrag ist das sicher nicht! Bin enttäuscht da ich mehr Qualität von A’Z Delivery gewohnt bin!

Andreas Wolter

Andreas Wolter

Liebe Leser: ein e-book mit den fehlenden Informationen ist in Arbeit und wird zeitnah fertiggestellt sein.

Grüße,
Andreas Wolter
AZ-Delivery Blog

Sihler Uli

Sihler Uli

Die fehlende Aufbauanleitung gereicht der Firma AZ-Delivery nicht zur Ehre.
Puzzle wollte ich nicht spielen.
Hoffentlich tut sich da noch was.

mike

mike

Danke für den Beitrag.

Im Set ist keine Bauanleitung vorhanden.
Bei az-delivery.de finde ich auch keine online.
Dafür können Sie natürlich nichts aber vllt. liest die Fa. hier mit und veröffentlich eine Anleitung.

jacques Lormiez

jacques Lormiez

Comment trouver le manuel de montage
Merci

HeinzHe Walser

HeinzHe Walser

Hallo
vielleicht finde ich es ja nicht, aber gibt es irgendwo eine Aufbauanleitung (wenigstens grob, mechanischer Teil) für das Kit ????
so groß ist meine Fantasie denn doch wieder nicht
Danke
Mit freundlichen Grüßen
Heinz Walser

Andreas Wolter

Andreas Wolter

@Dr. Dieter Roller: dieser Blogbeitrag ist für die elektronischen Komponenten konzipiert worden. Verwendung der Motorsteuerung oder Sensoren. Gang allgemein dienen sie als Inspiration, um eigene Projekte und Ideen damit umzusetzen. Die Verlinkung auf der Shopseite zu diesem Beitrag lässt etwas anderes vermuten. Mit Aufbau ist eher die elektrische Schaltung gemeint. Ich werde das weitergeben.
Ich würde Sie fürs Erste an den technischen Support verweisen, falls im Set keine Bauanleitung enthalten ist.

Grüße,
Andreas Wolter
AZ-Delivery Blog

Dr. Dieter Roller

Dr. Dieter Roller

Mir ist, wie ich auch sonst schon kommuniziert habe, schleierhaft, wie man mit diesem Blog-Eintrag hier das Auto zusammenbaut.

Laisser un commentaire

Tous les commentaires sont modérés avant d'être publiés