Portefølje 2: Neatracer

Af Mathias Winnard, Nikolai Damm og Stephan Sørensen

Neatracer er en robot bygget af LEGO og styret af en Arduino. Dens funktion er at navigere gennem en færdiglavet bane (se figur 1) ved benyttelse af en enkelt farvesensor og ultralyd. Ved test har den vist sig at være i stand til at gøre dette på omtrent 32 sekunder, som set i videoen herunder.

I denne vejledning vil det blive uddybet hvordan Neatracer er opbygget, både teknisk og mekanisk, hvad man skal bruge for at bygge én, samt hvordan koden bag den virker.

Figur 1. Banen som Neatracer skal navigere.

Materialer

For at bygge din egen Neatracer, skal du bruge følgende:

  • 1 stk. Arduino Uno
  • 1 stk. Arduino Motor Shield
  • 1 stk. HC-SR04 ultralydssensor
  • 1 stk. QTR-1A Reflectance Sensor
  • 1 stk. toggle switch
  • 2 stk. LEGO Technic DC-motorer
  • 4 stk. AA-batterier (à 1,5V stykket, til en total spænding af 6V)
  • 1 stk. batteriholder med plads til 4 AA-batterier
  • Ledninger
  • Tape, elefantsnot, eller en anden lignende type klister
  • LEGO-klodserne anvist i den vedhæftede byggevejledning (–> –> link <– <–)

Kredsløbets opbygning

Kredsløbet er forholdsvis simpelt opbygget, og kan ses illustreret i figur 2.

Figur 2. Kredsløbets opbygning, skitseret i Fritzing. Programmet havde ikke en Reflectance-sensor, så en lignende substitut er benyttet.

For at bygge kredsløbet skal man først montere Motor Shieldet på sin Arduino Uno. Motoren til robottens venstre hjul skal herefter forbindes til Motor Shieldets port A, mens det højre hjul forbindes til port B.

Batteripakkens negative pol forbindes til ground, mens den positive pol forbindes det midterste ben på systemets toggle switch, hvis ene ben derefter forbindes til Vin-indgangen. Det er her vigtigt at pointere, at det er muligt at anvende andre og flere batterier end de fire angivne AA-batterier, men tilføjelsen af yderligere batterier og en derved en højere spænding vil ændre robottens hastighed. Det vil også være muligt at tilknytte en kondensator til systemet, hvilket kan begrænse uregelmæssige ændringer i hastighed, hvis batterierne begynder at blive afladt. Det ville sørge for at robotten først begyndte at køre, når kondensatoren nåede den ønskede spænding.

Ultralydssensoren har fire ben, der skal forbindes: GND, der skal forbindes til en ground-port på Motor Shieldet; Echo, der skal forbindes til port 5; Trig, der skal forbindes til port 4, og Vcc, der skal forbindes til 5V-indgangen på Motor Shieldet.

Reflectance-sensoren har tre ben, der skal forbindes: GND, der skal forbindes til ground på Motor Shieldet; Out, der skal forbindes til port 0 på Motor Shieldet, og Vin, der skal forbindes til 5V-indgangen på Motor Shieldet. Sensoren fungerer ved at udsende infrarødt lys fra en LED, hvilket reflekteres fra en overflade og opfanges af en dertilhørende fototransistor. Fototransistoren sender herefter en værdi videre til Arduinoen; værdiens størrelse afhænger af hvor meget infrarødt lys, der reflekteres tilbage på fototransistoren. Jo højere værdien er, jo mørkere er overfladen.

Der er her flere muligheder for hvordan man ønsker at forbinde sensorerne, eftersom der kun er en enkelt 5V-port på Motor Shieldet. Hvis man foretrækker at lodde, er det muligt at lodde ledningerne fra de to sensorer sammen, og derefter forbinde dem til 5V-porten. Alternativt kan man benytte et lille breadboard, som vist i oversigten. I den fungerende version af Neatracer blev der benyttet lodning, da dette betød at robottens vægt blev sænket.

Robottens opbygning

Neatracers design fokuserer på simplicitet i forhold til at benytte færrest mulige sensorer, og tillader udvikleren nemt at udskifte komponenter, kode eller batteripakke. For at bygge robotten behøver man ikke meget forberedelse. Man kan i figur 3 se formen af robotten , og ved at følge byggevejledningen opnå et identisk design. Dog skal det nævnes, at de klodser, der er markeret med lyserød i figur 3, repræsenterer de to DC-motorer, og derfor ikke skal bygges.

Figur 3. Opbygningen af Neatracer i LEGO Digital Designer, med angivet placering af sensorer.

Robottens ryg er bygget, så den består af to beholdere; én til Arduino og Motor Shield, og én til batteripakken. Boksen til Arduino og Motor Shield er bygget asymmetrisk med en åbning i den ene side, så USB-porten på Arduinoen er tilgængelig uden meget besvær (se figur 4).

Figur 4. Robottens ryg. Fra venstre til højre: tænd-/sluk-knap, Arduino med Motor Shield, batteripakke.

Tænd-sluk-knappen er en toggle switch, der er fæstnet til robotten med gaffatape. Det er ikke specielt vigtigt hvor på robotten denne er placeret, og den kan derfor placeres efter forgodtbefindende, og sættes fast efter hvordan man finder det nemmest. Dog er det en god idé at placere den et sted, hvor den er nem at komme til, når robotten skal køres – hvilket er hvorfor den er placeret på toppen af robotten i dette eksempel.

Arduinoen og dens dertilhørende Motor Shield er placeret i det midterste rum på toppen af robotten. Som det kan ses på figur 4, stikker dens USB-port ud gennem hullet på robottens højre side, hvilket gør det muligt at sætte kablet i uden at skulle skille robotten ad. Samme tanke lægger også til grund for hvorfor batteripakken lægger som den gør, da det er nemt at skifte batterierne ud, samt at montere alternative batteripakker. Begge disse rum er desuden bygget med hullede LEGO-klodser, så det er muligt at trække ledningerne igennem her, og derved mindske risikoen for at de vikler sig ind i hjulene. Rummet til Arduinoen er desuden stort nok til også at kunne have plads til et lille breadboard, hvis dette er foretrukket over at lodde ledninger sammen.

Figur 5. Ultralydssensorens placering på robottens udhæng.

Udhænget foran på Neatracer fungerer til at forbedre robottens styring. Udhænget består specifikt af rektangulær beholder, hvilken ultralydssensoren er placeret på fronten af (se figur 5), og reflectance-sensoren er placeret under (se figur 6).

Beholderen på udhænget er bygget så reflectance-sensoren sidder ca. 4-5 mm over jorden, og er mørklagt fra alle sider end direkte under sig. På denne måde er der ret stor forskel på værdierne som reflectance-sensoren måler, når den bevæger over en lys overflade til sammenligning med en mørk, da der ikke trænger meget andet lys ind, hvilket ellers kunne forstyrre sensorens data.

Reflectance-sensoren er placeret foran hjulene, da robotten herved bedre og hurtigere kan reagere på den sorte linje og tilrette kørslen derefter. Dette skyldes at jo tættere sensoren er ved hjulene, jo kortere tid har robotten til at skifte retning undervejs, og eftersom Neatracer kun kører med ét hjul ad gangen, ville den lave reaktionstid betyde at der var en større sandsynlighed for at robotten ville køre over stregen og begynde at køre i ring. 

Beholderen på udhænget er bygget så reflectance-sensoren sidder ca. 4-5 mm over jorden, og er mørklagt fra alle sider end direkte under sig. På denne måde er der ret stor forskel på værdierne som reflectance-sensoren måler, når den bevæger over en lys overflade til sammenligning med en mørk, da der ikke trænger meget andet lys ind, hvilket ellers kunne forstyrre sensorens data.

Figur 6. Reflectance-sensorens placering under robottens udhæng.

Reflectance-sensoren er placeret foran hjulene, da robotten herved bedre og hurtigere kan reagere på den sorte linje og tilrette kørslen derefter. Dette skyldes at jo tættere sensoren er ved hjulene, jo kortere tid har robotten til at skifte retning undervejs, og eftersom Neatracer kun kører med ét hjul ad gangen, ville den lave reaktionstid betyde at der var en større sandsynlighed for at robotten ville køre over stregen og begynde at køre i ring. 

Ultralydssensoren er ligeledes placeret på fronten, da denne skal kunne måle hvornår robotten er kommet tæt på væggen i den anden halvdel af banen, og derfor skal skifte til den anden halvdel af styringsprogrammet. Det er meget vigtigt at sensorens sidder på robottens front, da den skal registrere en væg foran robotten, hvilken den derefter skal dreje udenom. Ligesom on-/off-knappen kan sensoren sættes fast ved tilføjelse af yderligere LEGO-klodser, men er i dette eksempel sat fast med elefantsnot.

Robottens kode

I dette afsnit præsenteres de vigtigste dele af koden. For at downloade den fulde kode benyttet ved testen, klik her. Robottens bevægelse kommer overordnet til at følge flow-diagrammet i figur 7.

Figur 7. Flow-diagram over robottens bevægelse.

Som nævnt under kredsløbets opbygning skal de forskellige komponenter kobles til porte på Motor Shieldet. For at disse skal kunne kunne tilgås i koden, kræver det en instantiering af de benyttede porte som vist herunder:

//Left Motor
const int leftMotorPWM = 3;
const int leftMotorDir = 12;

//Right Motor
const int rightMotorPWM = 11;
const int rightMotorDir = 13;

//Line Sensor
const int lineSensor = A0;

//Ultrasonic Sensor
const int trigPin = 4;
const int echoPin = 5;<br>

Hertil kommer en instantiering af yderligere variable, der er centrale for styring af robotten. De første to, duration og distance er nødvendige for benyttelse af ultralydssensoren, mens wallTime, programIsOver og threshold er nødvendige for den generelle styring af robotten. wallTime markerer overgangen fra styring efter den sorte streg på gulvet til styring rundt om muren, mens programIsOver markerer – som navnet antyder – hvornår programmet skal stoppe, og robotten ikke længere skal bevæge sig. threshold er en int-værdi, hvilken benyttes af reflectance-sensoren til at styre hvor sensitiv robotten er, da denne benyttes til at bevæge sig langs den sorte linje.

//Ultrasonic variables
long duration;
int distance;

//Program variables
bool wallTime = false;
bool programIsOver = false;
int threshold = 800;

Sensoren returnerer en numerisk værdi, baseret på hvor meget lys der reflekteres fra den overflade, den befinder sig over. Jo højere sensorværdien er, desto mørkere er overfladen. Sensorens værdi for genkendelse af den sorte streg kan variere afhængig af mængden af lys der falder ind udefra, men gennem gentagne forsøg blev threshold-værdien for genkendelse af stregen sat til 800, hvilket gav den mest nøjagtige bevægelse for robotten. Eftersom dette kan variere fra forsøg til forsøg, samt efter hvor stor farvekontrast der er på underlaget, kan threshold ændres efter behov.

Al bevægelse af robotten er styret af en loop-funktion, der bestemmer robottens handlemønster. Loopet kan ses herunder:

void loop()
{
  int sensorVal = analogRead(lineSensor);
  //Prints Sensor Values on the Serial Monitor
  //Serial.print("Sensor: ");
  //Serial.println(sensorVal);
  if (programIsOver) {
    fullStop();
  }
  else if (sensorVal > threshold && !wallTime) {
    goRight();
  } else if (sensorVal < threshold) {
    if (wallTime) {
      goAroundWall();
    } else {
      goLeft();
    }
  }
  checkDistance();
}

Robottens første handling at få hentet værdierne som reflectance-sensoren opfanger, og omdanner derefter den indhentede værdi til en int kaldt sensorVal. Derefter bliver der tjekket for om programmet er ovre; hvis det er, skal den sætte robotten til standsning ved at sætte PWM ned på begge hjul. Der bliver derefter tjekket for hvilken retning robotten skal køre i. Hvis den indhentede data fra reflectance-sensoren er højere end den angivne threshold og wallTime ikke er sand endnu, skal robotten køre mod højre (ved at sætte PWM til LOW for højre motor og PWM til HIGH på venstre). Hvis dette dog ikke er gældende og den angivne værdi er større end threshold-værdien, skal robotten derimod tjekke om wallTime er sand. Hvis wallTime ikke er sand, skal robotten bevæge sig mod venstre. Robotten kører til sidst en funktion kaldet checkDistance, inden loopet begynder forfra.

void checkDistance() {
  // Clears the trigPin
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);

  // Sets the trigPin on HIGH state for 10 micro seconds
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  // Reads the echoPin, returns the sound wave travel time in microseconds
  duration = pulseIn(echoPin, HIGH);

  // Calculating the distance
  distance = duration * 0.034 / 2;
  if (distance < 25) {
    wallTime = true;

  }
  // Prints the distance on the Serial Monitor
  Serial.print("Distance: ");
  Serial.println(distance);
}

checkDistance-funktionen måler distancen mellem robotten og muren ved hjælp af ultralydssensoren monteret forrest på robotten. Hvis den kalkulerede distance fra ultralydssensoren returnerer en værdi under 25, sætter den wallTime til sand; hvis den derimod er mindre, forbliver wallTime falsk. I begge tilfælde vil loopet derefter begynde forfra igen. Værdien på 25 er her fundet gennem tests af robotten, og kan variere baseret på størrelsen af banen, eftersom den er tilpasset det specifikke mellemrum i testbanen, samt efter om ultralydssensoren sidder skævt. Hvis distanceværdien i disse tests var højere end 25, ville robotten begynde at dreje udenom væggen før tid,  mens den stadigvæk ville befinde sig på den sorte linje, og hvis værdien var lavere end 25, ville robotten komme for tæt på muren til at kunne nå at dreje, uden at ramme muren først.

Når wallTime er sand og sensorVal er større end vores angivne threshold, vil robotten i næste loop aktivere funktionen goAroundWall. Denne funktion er egentlig hardcoded data, som sørger for at robotten automatisk kører i samme bane hver gang den kommer til muren.  Her er antallet og længden af bevægelsesfunktionerne timet, så robotten drejer og kører ligeud i et bestemt stykke tid, så den ønskede bevægelse opnås. Herved bevæger robotten sig rundt om muren og ender i målet – hvor den herefter vil sætte booleanen programIsOver til sand og derved stoppe robotten. Herunder ses goAroundWall-funktionen fra den seneste test af robotten. De udkommenterede værdier er standardværdierne for bevægelsen, hvilke dog kan ændre sig efter hvor meget strøm der er på batteriet.

void goAroundWall() {
  fullStop();
  delay(300);
  goLeft();
  delay(350); //500

  fullStop();
  delay(300);
  goForward();
  delay(2800); //3000

  fullStop();
  delay(300);
  goRight();
  delay(1800); //2000

  fullStop();
  delay(300);
  goForward();
  delay(800); //1000

  fullStop();
  delay(300);
  goRight();
  delay(1400); //1700

  fullStop();
  delay(300);
  goForward();
  delay(3300); //1700

  //fullStop();
  //delay(300);
  //goLeft();
  //delay(30); //50

  //fullStop();
  //delay(300);
  //goForward();
  //delay(1700); //1700
  fullStop();
  programIsOver = true;
}

Konklusion

Neatracer er fuldt i stand til at køre gennem banen, selv ved brug af kun en enkelt reflectance-sensor. Dens præstation varierer fra test til test, eftersom ultralydens indfaldsvinkel, når den når frem til muren, samt robottens retning, når den skifter til goAroundWall-funktionen har enorm indflydelse på hvilken bane den kører, eftersom denne er hardcoded. Dette betyder også, at hvis vinklen er forkert, når den ikke altid i mål. Robottens hastighed afhænger yderligere af hvor friske batterier der sidder i, samt hvor stor spænding der er på dem, hvilket betyder at dens evne til at køre rundt om muren kan falde fra, hvis dens energikilde udskiftes. Dette kan dog også kommes til livs gennem ændring af værdierne i goAroundWall. Neatracer har dog vist sig i stand til at gennemføre banen og derved løse opgaven på 32 sekunder.

Perspektivering

Der er visse forbedringer, som man nemt kunne tilføje for at gøre robotten hurtigere og sikkert også mere pålidelig. Det første ville være at implementere flere reflectance-sensorer – eksempelvis to ved siden af hinanden. Dette vil gøre arealet hvor robotten sanser efter den sorte linje større, og formentlig også forbedre robottens evne til at reagere på den sorte linje. Dette blev testet under udviklingen, men gav da ikke stort nok udsving til at blive taget med i det endelige produkt. Dette skyldtes blandt andet, at en konsekvens af tilføjelsen af en ekstra sensor havde en negativ indflydelse på robottens hastighed ved testen. Dette ville dog i retrospekt let kunne have været kommet til livs ved at give robotten et større batteri. Dette vil øge robottens maksimumhastighed, hvilket i sidste ende er det vigtigste for at forbedre robottens tid – så længe den stadig fulgte linjen.

Derudover vil man også kunne få robotten til at udnytte sin ultralydssensor mere, når den bevæger sig rundt om muren. Dette kunne eksempelvis gøres ved at få dreje sensoren 90 grader til højre for at tjekke om muren er der. Så længe muren var på højre side, ville robotten følge den, ved at køre ligeud. Robotten ville herved yderligere være i stand til at korrigere, hvis den kørte skævt, ved at justere sin retning baseret på hvor langt væk muren var. Når robotten herefter nåede enden af muren, så den ikke længere var på højre side, ville robotten herefter kunne dreje rundt om muren, og følge den på samme vis indtil enden af banen, hvor den slutteligt ville kunne holde stille. Herved ville det også være muligt at undgå at hardcode bevægelsen.

Leave a Reply