Benny the Robot

Af Laura, Pernille & Sandra

Formål

I faget Hardware & Robotteknologi er der opstillet opgaven, at lave en autonom linje-følge robot. Denne robot skal kunne flytte kasser fra givne startpunkter til givne slutpunkter, igennem forskellige baner. Banerne er forskellige, og i de fleste af banerne er det blot en sort linje der skal følges med en bredde på 5 cm. De to første baner, A & B, er obligatoriske, mens resten af banerne er valgfrie.

Banerne

Komponenter

Til robottens opbygning er der brugt følgende:

  • 1x Arduino UNO
  • 1x Motorshield
  • 3x Linjesensorer
  • 2x DC Motorer
  • 4x LED’er
  • 1x 150 Ohm modstand
  • 1x PCB
  • Lego Technic

Arduino & Motorshield

Arduino er en open source, fysisk computer platform, hvilket ofte bruges i multidisciplinære projekter. En Arduino bliver tit brugt i interaktive projekter som indeholder input fra for eksempel afbrydere eller sensorer. Robotten bruger Arduinoen for at kunne tilføje kode således at den kan køre, samt styringen motorer og LED’er.

Motorshieldet benyttes, da der ønskes at kunne skifte retning på DC-motorerne, og tilføje mere strøm. Et motorshield er et ekstra board (En add-on) man kan tilføje ovenpå en Arduino Et motor shield har en indbygget H-bro. En DC-motor fungerer ved at to ledere, der er sat sammen med kontaktflader som kontrollerer det magnetiske felt i spiralen, drives af en metalkerne. Retningen af rotationen kan blive vendt ved at ændre polariteten af strømmen på kontaktfladen.

En H-bro består af 4 switching elementer og transistors. Ved at aktivere to switches på samme tid kan man ændre retningen af current flow og derved ændre rotationen af motoren.

H-Bro

En transistor er halvledere og kan ses som en digital switch. Det kan bruges til at åbne op for en større strøm end Arduinoen selv kan trække. BJT og MOSFET er de transistorer der normalt bruges. Der findes to udgaver NPN, som er low side og PNP, som er high side. PNP bruges oftest ved h-broer. Her aktiveres den gemmen negativ spænding på basen og strømmer løber fra emitter(VCC) til collector(GND). For motorshieldet bruges begge.

Transistorer kan have forskellige modes alt efter hvordan strømmen løber igennem dem.
Active: Transistoren er åben baseret på forholdet mellem Vb og Vc og strømmen løber igennem den
Saturation: Transistoren er fuldt åben (kortsluttet) og strømmen løber igennem den
Cutoff: Transistoren er lukket og ingen strøm løber igennem den
Reverse: Strømmen løber den modsatte vej gennem transistoren

Linje sensorerne

Til at løse opgaven er der gjort brug af linje sensorer, der kan aflæse om baggrunden er hvid eller sort. I denne opgave er dette smart, da banerne der køres efter, er tegnet op af sorte baner på hvid baggrund.

For robotten aflæste linje sensorerne på hvid generelt en værdi på under 400, hvor de på sort generelt aflæste en værdi på over 600. Dette hjalp gruppen til at bestemme om robotten fulgte banen, og om den skulle dreje eller stoppe.

Forrige robotter

Igennem processen er bilen blevet bygget om flere gange. Den første bil der blev konstrueret, havde sensorerne siddende langt væk fra motorerne, samtidig med at selve konstruktionen var bygget meget ustabilt. Bilen var samtidig let i fronten, hvilket gjorde at den stejlede hver gang den skulle igangsættes, og den ustabile konstruktion gjorde dens bevægelser ukontrollerbare. Efterfølgende blev bilen gjort tungere foran, men den stadigt ustabile konstruktion og placeringen af sensorerne, gjorde at bilen stadig ikke kørte optimalt. Gruppen blev dernæst rådet til at placere sensorerne tættere på motorerne, og fik samtidig vist hvordan man kunne placere motorerne, således at hele konstruktionen blev stabiliseret.

Opbygning

Opgaven lyder, at der skal skabes en robot der kan flytte en kasse fra ét startpunkt til ét slutpunkt. Robotten er opbygget af 2 motorer der skaber fundamentet for robotten, samt udvidelser af længden på hver side af motorerne. På fronten af robotten er der sat 2 længere udstikkere, der bruges til at transportere kassen fra start til slut. Tæt på motorerne sidder to linje sensorer, og på den ene udstikker er den tredje placeret. Disse er placeret med en afstand til jorden på cirka 3mm – linje sensorerne havde behov for en kort distance at aflæse værdierne, for at få den mest korrekte aflæsning.
Der er blevet brugt gearing til robottens bevægelse, for at få en bedre kontrol over robottens hastighed.

Robottens opbygning

Hardware opbygning

Robottens hardwares opbygning er tegnet ind i Fritzing. På tegningen ses forbindelserne mellem Motorshieldet og motorerne, samt linje sensorerne, batteriet og de fire LED’er.

Fritz tegning af hardware opsætning

PCB

Til bilen er der blevet lavet et PCB. Et PCB, også kaldet et printed circuit board, er et board bestående af linjer og ruter, der sammenfletter forskellige punkter. Det lader et signal og strøm blive ført gennem disse ruter. Man lodder på et PCB med loddetin, og dette laver en elektronisk sammenfletning af PCB’ets overflade og det elektroniske komponent. På PCB’et, til bilen, blev der gjort plads til de 4 LED’er, der skulle lyse afhængigt af bilens retning, deres fælles modstand, samt plus og minus til linje sensorerne, og plus og minus til Arduinoen.

PCB’et til robotten

LED’erne

LED’erne der lyser afhængigt af robottens retning, sidder i PCB’et i en parallelforbindelse. Dette er gjort for at simplificere kredsløbet, da maksimalt to LED’er skal lyse samtidig. Eftersom at disse LED’er alle er forskellige, er en parallelforbindelse dog ikke optimal, da hver LED skal bruge en forskellig mængde af spænding, og da de nu deler én modstand, får de ikke forskellige mængder spænding.

Der er efter udførslen af PCB’et blevet udregnet, hvilke modstande der skulle have været i PCB’et, fremfor modstanden der er tilstede på nuværende tidspunkt.

LED’erne brugt er henholdsvis 1x rød LED, 1x gul LED, 1x grøn LED og 1x hvid LED. Ud fra de forskellige LED’ers datablad, kan der beregnes deres optimale modstand. For at gøre dette, skal Ohms lov benyttes – denne lyder:

U = R * I, hvor U er spændingen målt i Volt, R er modstanden målt i Ohm og I er strømmen målt i Ampere. For at kunne beregne modstandene til LED’erne skal denne formel omskrives:

R = (Vs – Vled) / Iled, hvor Vs er spændingen fra strømkilden (her 5V fra Arduinoen), Vled er spændingen der ønskes passeret gennem LED’en og Iled er strømstyrken der ønskes passeret gennem LED’en.

I databladet for den røde LED ses der at strømstyrken der ønskes passeret er 20mA, svarende til 0,02A, hvor den ønskede spænding er min 1,8V og max 2,2V. I databladet for den gule og den grønne LED ses at de ønskede værdier er ens med den røde LED. I databladet for den hvide LED ses det dog, at den ønskede strømstyrke er 20mA, svarende til 0,02A, og den ønskede spænding er min 3,2V og max 3,4V.

For både den røde, gule og grønne LED skal der bruges samme modstand. Denne skal have størrelsen:

R = (5V – 2,2V) / 0,02A = 140 Ohm
Altså skal disse LED’er bruge en modstand på 140 Ohm, for at ramme den ønskede spænding.

Den hvide LED’s modstand skal have størrelsen:
R = (5V – 3,4V) / 0,02A = 80 Ohm

Flow chart

Flow Chart for robottens opførsel

Robottens kode

Bilens kørsel er baseret på et array af tal, hvoraf der bliver kaldt et tal mellem 0 og 5, der fortæller hvordan bilen skal køre – dette array kaldes commandList. I kodens loop er der to if-statements, der fortæller hvordan koden skal læses. Hvis tælleren stateCounter er under eller lig arrayets længde, skal variablen currentState sættes til commandList[stateCounter]. Dette betyder at hvis der i arrayet er et et-tal, vil currentState sættes til commandList[1]. Hvis tælleren stateCounter derimod er mere end 18, sættes currentState til 5.

if (stateCounter <= 18) {
    currentState = commandList[stateCounter];
  }
  else if (stateCounter > 18) {
    currentState = 5;
  }

Tidligere i samme funktion er brugt en switch case, der bruger tælleren currentState, til at afgøre hvad bilen skal gøre. Hvis currentState er lig 0, går den ind i første case, hvis currentState er lig 1, går den ind i næste case og så videre. Koden er bygget op således at ved currentState = 0, skal bilen bevæge sig fremad, ved 1 skal den dreje til højre, ved 2 skal den lave en u-vending, ved 3 skal den dreje til venstre, ved 4 skal den bakke og ved 5 skal den stoppe.

switch (currentState) {
    case 0:
      forward();
      digitalWrite(forwardLED, HIGH);
      digitalWrite(rightLED, LOW);
      digitalWrite(leftLED, LOW);
      digitalWrite(backLED, LOW);
      break;

    case 1:
      turnRight();
      digitalWrite(forwardLED, LOW);
      digitalWrite(rightLED, HIGH);
      digitalWrite(leftLED, LOW);
      digitalWrite(backLED, LOW);
      break;

    case 2:
      uTurn();
      digitalWrite(forwardLED, HIGH);
      digitalWrite(rightLED, LOW);
      digitalWrite(leftLED, LOW);
      digitalWrite(backLED, HIGH);
      break;

    case 3:
      turnLeft();
      digitalWrite(forwardLED, LOW);
      digitalWrite(rightLED, LOW);
      digitalWrite(leftLED, HIGH);
      digitalWrite(backLED, LOW);
      break;

    case 4:
      backUp();
      digitalWrite(forwardLED, LOW);
      digitalWrite(rightLED, LOW);
      digitalWrite(leftLED, LOW);
      digitalWrite(backLED, HIGH);
      break;

    case 5:
      analogWrite(speedA, 0);
      analogWrite(speedB, 0);
      digitalWrite(forwardLED, LOW);
      digitalWrite(rightLED, LOW);
      digitalWrite(leftLED, LOW);
      digitalWrite(backLED, LOW);
      break;

    default:
      break;
  }

I hver case kaldes en ny funktion, der indeholder de forskellige koder. I første case, bliver forward() funktionen kaldt, hvilket er den der får bilen til at køre ligeud. I denne funktion aflæser sensorerne overfladen bilen kører på, og registrerer om det er hvidt eller sort. Hvis den ene sensor registrerer sort og den anden hvid, retter bilen selv op, således at begge sensorer igen aflæser hvid. Hvis begge sensorer derimod begge aflæser sort, stopper bilen med at køre, og tælleren stateCounter adderes med 1.

void forward() {
  
  while (followNow == false) {
    leftSensorValue = analogRead(leftSensor);
    rightSensorValue = analogRead(rightSensor);

    digitalWrite(directionA, HIGH);
    digitalWrite(directionB, LOW);

    analogWrite(speedA, goSpeed);
    analogWrite(speedB, goSpeed);

    if (rightSensorValue < whiteMax || leftSensorValue < whiteMax) {
      followNow = true;
    }
  }
  while (followNow == true) {
    leftSensorValue = analogRead(leftSensor);
    rightSensorValue = analogRead(rightSensor);
    Serial.println(leftSensorValue);
    Serial.println(rightSensorValue);

    digitalWrite(directionA, HIGH);
    digitalWrite(directionB, LOW);

    if (rightSensorValue < whiteMax) {
      analogWrite(speedA, goSpeed);
    }
    else {
      analogWrite(speedA, stopSpeed);
    }

    if (leftSensorValue < whiteMax) {
      analogWrite(speedB, goSpeed);
    }
    else {
      analogWrite(speedB, stopSpeed);
    }

    //if both sensors = black, go to next state
    if (rightSensorValue > blackMin && leftSensorValue > blackMin) {
      stateCounter++;
      followNow = false;
    }
  }
}

Hvis currentState er lige 2, vil funktionen uTurn igangsættes. Her aflæses sensorværdierne igen, og bilen drejer mod venstre. Når bilen har passeret sort én gang, er bilen halvvejs og booleanen beenBlack sættes til true. Når bilen har passeret sort endnu en gang, sættes booleanen beenBlack igen til false, men tælleren blackCounter adderes med 1. Dette gentages indtil blackCounter er større eller lig 2, hvorefter stateCounteren adderes med 1, og det næste i arrayet påbegyndes.

void uTurn() {
  leftSensorValue = analogRead(leftSensor);
  rightSensorValue = analogRead(rightSensor);

  digitalWrite(directionA, HIGH);
  digitalWrite(directionB, HIGH);

  analogWrite(speedB, uTurnSpeed);
  analogWrite(speedA, uTurnSpeed);


//turn till leftSensor has seen black twice
  if ( blackCounter < 2 ) {
    if (leftSensorValue > blackMin) {
      beenBlack = true;
    }
    else if (leftSensorValue < whiteMax && beenBlack) {
      beenBlack = false;
      blackCounter++;
    }

    //stateCounter upped
  } else if (blackCounter >= 2 ) {
    blackCounter = 0;
    stateCounter++;
  }
}

På nedenstående eksempel ses et eksempel på arrayet commandList. Dette er arrayet for udførslen af ruterne A og B i én. Når tælleren stateCounter er under 29, vil currentState sættes lig med commandList[stateCounter]. Den starter med at tælle fra første tal i arrayet, og for hver stadie den går igennem, bliver denne plusset og et nyt stadie igangsættes. Hvis stateCounter derimod er større eller lig 29, vil currentState sættes til 5, og bilen vil stoppe.

int commandList[]= {0, 0, 0, 1, 0, 0, 3, 0, 2, 0, 3, 0, 0, 4, 0, 1, 0, 1, 0, 1, 0, 3, 0, 0, 1, 0, 2, 0, 3, 0, 0, 1, 0, 3, 0};

Bane C og D’s kode ses nedenfor. Der er igen arrays af tal, der hver især henviser til de forskellige funktioner.

//Bane C
int commandList[] = {0,0, 0, 0, 2, 0, 3, 0, 0, 0, 1, 0, 0, 3, 0};

//Bane D
int commandList[] = {0, 0, 0, 3, 0, 1, 0, 2, 0, 3, 0, 1, 0, 3, 0, 0, 0};

Koden er bygget op som en finite state machine, hvilket kan ses som værende en maskine, der kan kalde et uendeligt nummer af stadier på ethvert tidspunkt. Dette kommer til udtryk i de 6 cases i loop funktionen, der hver især kalder deres egne funktioner. En finite state machine defineres ofte som havende en liste er stadier, og betingelserne for at skifte til næste stadie (Wiki, 2019).

Problemer & udfordringer

En af de store udfordringer med denne bil var blandt andet, at opførslen ændrede sig hver gang et batteri mistede en vis mængde strøm. Så snart batterierne kom under en given spænding, kørte bilen en del langsommere og dens reaktionsevne var ikke nær så god som hvis batteriet var fyldt. Gruppen prøvede at tilkoble en større strømforsyning, og sendte konstant 9V til bilen, men dette reagerede bilen også anderledes på, end hvis et batteri blev brugt.

Grundet bilens opbygning kørte den ikke lige længere tid af gangen, hvilket i forward() funktionen blev rettet op på. I bilens bak-funktion kunne dette dog ikke rettes, og bilen kørte for skævt til at kunne udføre den ønskede handling. En beslutning blev derfor taget om at tilføje en tredje sensor på spidsen af bilen, der skulle aflæse om bilens yderste del var på det sorte igen. Dette blev gjort således at bilen kun bakkede et kort stykke ud, for igen at køre frem og tage banen rundt.

I transitionen mellem bane A og B, blev der taget et valg om at bilen, i stedet for at lave en uvending og kører videre, derfor blot skulle bakke lidt ud, kassen skulle fjernes fra dens plads, og bilen skulle igen køre ligeud, og rundt i en firkant for at nå samme sted.

Konklusion

Robotten Benny kan udføre banerne A, B, C og D uden større problemer – den benytter 3 linjesensorer til dette. I videoerne nedenfor ses robottens udførsel af disse.

Bane A & B
Bane C
Bane D

Leave a Reply