“The Brick” – den autonome racerbil

Forfatter: Anders Bossel Holst Christensen

 

Introduktion

Formålet med denne opgave er at skabe et samspil mellem hardware, elektronik og software. Ved brug af LEGO, simple elektroniske interfaces, sensorer samt et Arduino-software program, bygges en autonomt kørende racerbil, som skal følge en sort streg gennem en bane.

Denne rapport vil indeholde et afsnit om hardwaren, robottens opbygning i LEGO, samt softwaren i robotten. Desuden vil der evalueres på robottens evner, og diskuteres, hvordan disse ville kunne forbedres.

Robotten, som kan ses på billedet herunder, navngives ”The Brick”.

Figur 1: “The Brick” set forfra

Konstruktion

Robotten kan ses på figur 1. For yderligere billeder af robotten fra andre vinkler, henvises til afsnittet efter konklusionen.

Hardware

Til denne opgave, benytter robotten sig af 2 slags sensorer; 2 stk. infrarøde refleksionssensorer samt en enkelt ultralydssensor.

Refleksionssensorerne virker ved at de er udstyret med en kompakt LED, som afgiver lys i det infrarøde spektrum. Ved siden af denne LED sidder en fototransistor, som er følsom overfor infrarødt lys. Når det infrarøde lys bliver sendt ned på en lys flade, reflekteres en stor del af lyset tilbage og bliver opfanget af fototransistoren, hvilket måles som en lav spænding på output-benet. Når det infrarøde lys bliver sendt ned på en mørk flade, bliver der ikke reflekteret særlig meget lys tilbage til fototransistoren, hvilket giver anledning til en høj spænding på output-benet. På denne måde kan der skelnes mellem lyse og mørke flader.
Robotten er udstyret med to af disse refleksionssensorer. De sidder ved siden af hinanden med ca. 4 cm mellem sig ved robottens front. På denne måde kan robotten føle, om den rører ved den sorte linje, som den skal følge gennem banen, og korrigere for dette.
Refleksionssensorerne har tre ben; VCC, GND og OUT. Sensorerne forsynes med 5 volt over VCC og GND, og på OUT-benet måles output spændingen gennem en analog kanal.

Ultralydssensoren virker ved at afgive en højfrekvens lydbølge gennem en transmitter. Når denne lydbølge eksempelvis rammer en mur, vil den blive reflekteret tilbage til receiveren, som afgiver et signal til, at lydbølgen er modtaget. Ved at måle tiden mellem afgivelse og modtagelse, kan afstanden til objektet forude beregnes.
Ultralydssensoren har fire ben; VCC, GND, TRIG og ECHO. Spændingen over TRIG sættes høj i 10 ms, hvorved en kort puls afgives, og lydbølgen sendes ud. Herefter måles der på ECHO benet (via en digital kanal), indtil der modtages en puls. Herved måles tiden, det tog for lydbølgen at rejse fra transmitter til mur, tilbage til receiver. Da tiden (i mikrosekunder) både inkluderer den tid, det tager for lydbølgen at rejse ud til muren og tilbage til receiveren, divideres tiden med 2. Dernæst divideres tiden (i mikrosekunder) med 29, da lydens hastighed er ca. 29 mikrosekunder per centimeter. Herved har man regnet afstanden til objektet i centimeter.

Robotten er udstyret med to DC-motorer, som styrer hver sit hjul. Motorerne forsynes med 9 volt gennem et Arduino-motorshield. Deres omdrejningshastighed styres med PWM.

Design

Robotten er bygget med LEGO. Dens chassis er bygget op som en rektangulær, hul kasse, hvor der er plads til batterier og ledninger inden i. På toppen af chassiset er der bygget en holder til Arduinoen med motorshield’et på. I fronten er der bygget to ”arme” på, som har til formål at være extensions, hvorpå refleksionssensorerne kan sidde. Refleksionssensorerne sidder med ca. 3 mm. over jorden, så de kan opfange det infrarøde lys, som bliver reflekteret tilbage fra LED’erne.
På toppen sidder ultralydssensoren. Den vender fremad, så den kan se, når robotten har et objekt foran sig. Da robotten er højere i fronten end bagenden, er det vigtigt, at ultralydssensoren peger en smule nedad.
I robottens bagende, sidder to motorer bygget ind i chassiset. På motorernes aksler sidder et 1:5 ratio gearsystem i forbindelse til baghjulene. Dette gør, at motorerne kan køres med højere PWM uden at hjulene kører for hurtigt rundt. Herved kan der skabes et større moment, men samtidig beholde en kontrollerbar hastighed.
I robottens front, har den et passivt støttehjul, som er begrænset til at kunne rotere ca. 45 grader til hver side. Dette gør, at robotten støttes når den skal lave skarpe sving. Det nedsætter samtidig friktionen. Det er essentielt, at det passive hjul er begrænset, så det ikke kan dreje hele vejen rundt, da det ville gøre robotten ukontrollerbar.
Det kraftige chassis gør, at robotten er forholdsvis tung, og motorerne skal derfor yde meget, for at robotten kan køre. Til gengæld er chassiset så stærkt, at det ikke går i stykker, hvis robotten udsættes for stød eller vrid i forbindelse med skarpe sving.

Software

Koden kan ses efter konklusionen.

Softwaren er meget simpelt opbygget. I loopet måles først værdierne fra hhv. refleksionssensorerne og ultralydssensoren. Hvis ultralydssensoren måler, at der er et objekt inden for 28 cm, køres en hard-coded sekvens, som skal guide robotten rundt om muren på banen. Denne løsning er ikke smart, og giver i nogle tilfælde robotten problemer med at komme rundt om muren. Dette uddybes i perspektiveringsafsnittet.

Hvis der ikke er et objekt tæt på robottens front, sammenlignes værdierne fra de to refleksionssensorer. Hvis de begge måler en værdi under 400 betyder det, at de to sensorer begge er over en lys overflade, som reflekterer meget lys tilbage til fototransistoren. Dette giver anledning til, at robotten skal køre ligeud. Derfor gives samme PWM signal til begge motorer.
Hvis dette ikke er tilfældet, tjekker programmet, hvilken af de to sensorer, som giver det højeste output. Den sensor, som giver det højeste output, vil være den, som er over den sorte streg. Dette giver anledning til, at den ene motor skal dreje hurtigere end den anden, og derfor gives der et højt PWM signal til den motor, som sidder i den modsatte side af den sensor, som gav et højere output. Den motor, som sidder i den samme side som den sensor, der gav et højere output, skifter omdrejningsretning, og gives et lavt PWM signal. Når dette gøres samtidig, og holdes et stykke tid (i denne implementering holdes denne konfiguration i 250 ms), kan robotten hurtigt og effektivt rette ind, så den kører i linjens retning igen.
Når robotten kommer til 180 graders svingene, er det nødvendigt for den at kunne dreje endnu kraftigere, end når den bare skal lave små korrektioner for at holde sig på den rette linje. I softwaren holdes der styr på, hvor lang tid, robotten har været i gang med et givent sving. Hvis den samme sensor giver højere output i mere end et enkelt loop gennem programmet, prøver den at dreje endnu skarpere i den retning, og holder denne konfiguration i længere tid end normalt. På denne måde kan robotten skelne mellem, om den blot skal lave små korrektioner, eller om den er nødt til at dreje meget ifm. et 180 graders sving.

Konklusion

Robotten formår at skabe et samspil mellem hardwaren, softwaren og mekanikken som tillader den at bruge sine sensorer til at finde vej gennem det meste af banen. Den klarer på fin vis at manøvrere gennem banens første halvdel, som består af to 180 graders sving og tre lige strækninger. Softwaren gør det muligt for robotten at skelne mellem små nødvendige kursændringer på de lige strækninger og skarpe sving. Softwaren gør det i teorien også muligt for robotten at klare den sidste halvdel af banen, hvor robotten bliver mødt af en mur, som den skal manøvrere udenom. Robotten kan selv opdage, når muren er tæt på ved hjælp af ultralydssensoren. Når den møder denne, er den programmeret til at køre udenom muren. Dette klarer robotten ikke altid, idet robotten ikke møder muren med samme orientering ved hvert forsøg.

På konkurrencedagen klarede robotten sig gennem banen med en tid på 2 min og 58 sek.

Perspektivering

Robotten møder to store udfordringer på ruten; den skal kunne følge en sort streg gennem tre lige strækninger og to 180 graders sving, og derudover skal den kunne overkomme overgangen fra at følge stregen, til at kunne manøvrere udenom en mur og i mål, hvor der ikke er nogen streg at følge.

Første del af udfordringen klarer robotten uden de store problemer. Den anden del af udfordringen er derimod nogle gange et problem for robotten. Der er flere faktorer, som spiller ind:

  • Friktionen i de to baghjul og specielt forhjulet har indflydelse på hvordan robotten kører. Forhjulet sidder på en sådan måde, at friktionen ændrer sig efter hver gang, den har kørt en omgang på banen. Hjulet kan presses tilbage på plads efter hver omgang, men det er ikke en optimal løsning. Dertil kommer, at hvis der ikke er ens friktion i baghjulene, risikeres at de to baghjul ikke kører lige hurtigt ud af lige strækninger. Dette kan være et reelt problem. Specielt hvis den sidste del af banen bliver hard-coded, som den er blevet gjort i dette løsningsforslag.
  • Der er mange parametre at optimere i koden; hvor meget PWM man skal give til motorerne, hvor lang tid de skal have denne konfiguration, hvor skarpt man skal dreje osv. Alle disse parametre skal ændres, hvis forsyningsspændingen ændrer sig. Dette var et reelt problem, da det var næsten umuligt at optimere robottens ydeevne, når batterierne aflades efterhånden som man kører tests.
  • I overgangen mellem at følge den sorte streg, og til at skulle køre udenom muren er der et problem med, at robotten ikke møder muren med samme orientering hver gang. Dette er et problem som gør det næsten umuligt for robotten konsekvent at kunne klare sidste del af banen, da koden som får robotten til at køre udenom muren, er hard-coded. Det vil sige, at robotten essentielt set kører i blinde. Den bruger ikke ultralydssensoren til at guide sig rundt om muren. Det er fuldstændig op til programmøren at vide hvordan robotten skal køre. Dette er umiddelbart en dårlig løsning, og kunne ændres til, at robotten havde ultralydssensoren på højre side, og derved ville kunne bruge den til at holde muren i en fast afstand idet den kører rundt om muren. Jeg er overbevist om, at dette ville kunne lade sig gøre. Årsagen til, at jeg ikke har gjort det er, at jeg var i mangel på tid.

Denne opgave viser, at det med forholdsvis primitive midler kan lade sig gøre at skabe en autonomt kørende robot, som kan navigere rundt på en bane, som er standardiseret. Dette ville bl.a. kunne bruges til at skabe autonome robotter til at navigere rundt på eksempelvis et lager. Det ville kræve at robotterne færdedes i et standardiseret miljø, hvor der eksempelvis var linjer at følge, således at de ville kunne bruge refleksionssensorer og ultralydssensorer, eller laser-range finders til at navigere rundt.

 

Bilag

Kode

const int dirPinA = 12; //Motor
const int pwmPinA = 3;
const int brakePinA = 9;
const int dirPinB = 13;
const int pwmPinB = 11;
const int brakePinB = 8;

const int trigPin = 7; //Ultrasound
const int echoPin = 6;

const int prLeftRead = 5; //Reflection sensors
const int prLeftVcc = 2;

const int prRightRead = 4;
const int prRightVcc = 4;

int prLeftVal = 0;
int prRightVal = 0;
int difVal = 0;
int ultraSoundDist = 50;
const int turnDelay = 250;
int turnRightCounter = 0;
int turnLeftCounter = 0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(dirPinA, OUTPUT);
  pinMode(brakePinA, OUTPUT);
  pinMode(dirPinB, OUTPUT);
  pinMode(brakePinB, OUTPUT);
  pinMode(prLeftVcc, OUTPUT);
  pinMode(prRightVcc, OUTPUT);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  long duration, distance;
  digitalWrite(prLeftVcc, HIGH);
  digitalWrite(prRightVcc, HIGH);
  
  digitalWrite(dirPinA,LOW);
  digitalWrite(brakePinA,LOW);
  digitalWrite(dirPinB,HIGH);
  digitalWrite(brakePinB,LOW);

  digitalWrite(trigPin, LOW);
  delay(2);
  digitalWrite(trigPin, HIGH);
  delay(10);
  digitalWrite(trigPin,LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = (duration/2)/29;

  if (distance <= 28) {
    Serial.println("Wall detected on route - going around...");

    //Hardcoded sequence to go around the wall
    analogWrite(pwmPinA, 45);//Go straight for a bit
    analogWrite(pwmPinB,35);
    delay(800);
    
    analogWrite(pwmPinB, 200);//Turn left
    digitalWrite(dirPinA, HIGH);
    analogWrite(pwmPinA, 35);
    delay(turnDelay*5);
    analogWrite(pwmPinB, 0);
    analogWrite(pwmPinA, 0);
    
    digitalWrite(dirPinA, LOW);//Go straight
    analogWrite(pwmPinA, 45);
    analogWrite(pwmPinB, 35);
    delay(6000);
    analogWrite(pwmPinA, 0);
    analogWrite(pwmPinB, 0);
    
    analogWrite(pwmPinA, 200);//Turn 90 degrees right
    digitalWrite(dirPinB,LOW);
    analogWrite(pwmPinB, 35);
    delay(turnDelay*8);
    analogWrite(pwmPinA, 0);
    analogWrite(pwmPinB, 0);
    digitalWrite(dirPinB, HIGH);
    analogWrite(pwmPinA, 35);
    analogWrite(pwmPinB, 35);
    delay(500);
    analogWrite(pwmPinA,0);
    analogWrite(pwmPinB, 0);
    analogWrite(pwmPinA, 200);//Turn 90 degrees right
    digitalWrite(dirPinB,LOW);
    analogWrite(pwmPinB, 35);
    delay(turnDelay*8);
    analogWrite(pwmPinA, 0);
    analogWrite(pwmPinB, 0);
    digitalWrite(dirPinB, HIGH);
    
    
    digitalWrite(dirPinA, LOW);//Go straight
    analogWrite(pwmPinA, 48);
    analogWrite(pwmPinB, 35);
    delay(9000);
    
    analogWrite(pwmPinA,0); //Stop for good
    analogWrite(pwmPinB,0);
    delay(100000);
  }
  else {
    prLeftVal = analogRead(prLeftRead);
    prRightVal = analogRead(prRightRead);
    difVal = abs(prLeftVal-prRightVal);
    Serial.print("Left sensor read: ");Serial.println(prLeftVal);
    Serial.print("Right sensor read: ");Serial.println(prRightVal);
  
    if(prLeftVal < 400 && prRightVal < 400) { //They are both on their separate sides of the line
      Serial.println("Driving straight.");
      analogWrite(pwmPinA, 35);
      analogWrite(pwmPinB, 35);
      turnRightCounter = 0;
      turnLeftCounter = 0;
    }
    else if (prLeftVal < prRightVal) {
      Serial.println("Turning right");
      turnRightCounter = turnRightCounter+1;
      turnLeftCounter = 0;

      if (turnRightCounter == 1) {
        analogWrite(pwmPinA, 100);
        digitalWrite(dirPinB,LOW);
        analogWrite(pwmPinB, 30);
        delay(turnDelay);
        analogWrite(pwmPinA, 0);
        analogWrite(pwmPinB, 0);
      }
      else {
        analogWrite(pwmPinA, 180);
        digitalWrite(dirPinB,LOW);
        analogWrite(pwmPinB, 60);
        delay(turnDelay+70);
        analogWrite(pwmPinA, 0);
        analogWrite(pwmPinB, 0);
      }
    }
    
    else if (prLeftVal > prRightVal) {
      Serial.println("Turning left.");
      turnLeftCounter = turnLeftCounter+1;
      turnRightCounter = 0;

      if(turnLeftCounter == 1) {
        analogWrite(pwmPinB, 120);
        digitalWrite(dirPinA, HIGH);
        analogWrite(pwmPinA, 30);
        delay(turnDelay);
        analogWrite(pwmPinB, 0);
        analogWrite(pwmPinA, 0);
      }
      else {
        analogWrite(pwmPinB, 200);
        digitalWrite(dirPinA, HIGH);
        analogWrite(pwmPinA, 60);
        delay(turnDelay+80);
        analogWrite(pwmPinB, 0);
        analogWrite(pwmPinA, 0);
      }
    }
  }

  
}

Test-kørsel

I den første video klarer robotten den første del af banen, men da den når overgangen til muren, har den en forkert orientering, og den den hard-codede sekvens kan derfor ikke få den i mål.

 

I denne video startes robotten når den skal til at møde muren. Her har robotten den optimale orientering, når den møder muren, og kan derfor klare den sidste del af banen.

Billeder

Figur 2: “The Brick” fra siden, med åbent chassis

 

Figur 3: “The Brick” fra siden, med lukket chassis

 

Figur 4: Close-up på gearingen (1:5)

 

Figur 5: “The Brick” bagfra. Robotten startes med en ON/OFF switch.
Figur 6: “The Brick” ovenfra.

Leave a Reply