Jonathan Navntoft Lundstrøm & Holger Nordgaard Roland

Introduktion

I følgende blogindlæg vil skabelsen af “Automatic Racing Systems Emulation” (A.R.S.E) blive beskrevet, samt en guide til opbygningen af den. Først beskrives hvilke komponenter der skal bruges i dette projekt, samt hvordan de virker. Ydermere vil der komme en forklaring på hvordan robotten opbygges, samt hvilken bane den skal gennemføre. Herefter vil koden der ligger til grundlag for projektet blive gennemgået. Sidst vil der være en konklusion på hele projektet, hvorefter der vil blive diskuteret hvilke ting man kunne have forbedret. 

Komponenter

I projektet skal følgende komponenter bruges

  • 1 Arduino UNO
  • 2 DC Motorer
  • 1 L293D H-Bro
  • 2 hvide LED’er
  • 2 LDR-modstande
  • 2 10kΩ modstande
  • 2 120Ω modstande
  • 1  HC-SR04 sonarsensor
  • 1 SM-S2309S Micro Servo motor
  • 1 LM7805 5V spændingsregulator
  • Ledninger
  • 1 breadboard
Komponenterne der skal bruges

Robottens bane

Formålet med denne guide, er at opbygge en autonom kørende robot ved hjælp af en Arduino. A.R.S.Es formål er køre igennem en angivet bane så hurtigst så muligt, på nedstående billede er der et billede af den bane robotten skulle køre igennem. Robotten skal først følge den sorte streg, samt undgå objekter på stregen. Derefter skal den navigere udenom væggene og køre i mål. Siden vi ikke har mulighed for at opbygge og afprøve denne robot i fysiske omgivelser, vil vi derfor vise hvordan dette kan gøres i Unity.

Robottens bane

Beskrivelse af robottens funktioner

Inden vi begynder at gå i dybden med opbygningen af robotten, vil der kort blive beskrevet hvordan robottens primære komponenter fungerer sammen. Når robotten starter med at køre, vil 2 hvide LEDer kaste reflekteret lys ned på underlaget, som herefter vil blive opfanget af 2 LDR-modstande. LDR-modstande måler hvor meget lys de kan se, og lader mere eller mindre strøm gå igennem sig alt efter dette. Dermed vil de 2 LDR-modstande måle refleksionen af underlaget som de på et givent tidspunkt er lige over. Da den bane vi vil have robotten til at køre på er defineret ved en sort streg, vil disse LDR-modstande kunne måle når robotten kører over denne ved hjælp af målingsforskellen i det reflekteret lys. Herved justerer robotten dens retning og forbliver på stregen. 
Robotten er også udstyret med en ultralydsafstandsmåler. Denne bliver brugt til: 1) At holde afstand til væggene så robotten ikke hører for tæt på, og rammer disse. 2) At styre robotten forbi den første firkant på banen, samt udenom den sidste horisontale væg på forhindringsbanen, da der ikke er nogen sort streg. Da denne skal kunne kigge rundt om hele robotten, sidder ultralydsafstandsmåleren på en servomotor, som er monteret ovenpå robotten. En demonstrationsvideo kan ses i slutningen af dette blogindlæg.

Hvordan bygges robotten, samt forklaringer

I følgende afsnit vil vi beskrive, hvordan robottens skulle have været sat op. Til dette benyttes Tinkercad, et gratis 3D-modellerings, og kredsløbssimuleringsprogram, som kører i en web browser. I denne guide benyttes kredsløbssimuleringsdelen af dette. Vi anbefaler stærkt at bruge dette program til opsætning af kredsløb, og dette kan findes på følgende link: https://www.tinkercad.com/dashboard.   

Opsætning af de hvide LEDer

De 2 hvide LEDer skal monteres foran på robotten, pege ned mod jorden og skal hele tiden være tændt. Af denne grund sætter vi dem direkte i Arduinoens 5V udgang. Strømmen skal gå igennem en modstand på 120 ohm (forklaring på denne kommer nedenunder) før den når LEDerne, for at undgå at disse sprænger. Derefter skal strømmen føres til Ground/GND. Opsætning af dette med 2 LEDer, 2 modstande og ledninger kan ses på billedet nedenfor.

Opsætning af de hvide LEDer

Modstandens størrelse er beregnet ud fra ohms lov, som bliver beskrevet nedenfor. Vi har taget udgangspunkt i følgende hjemmeside (http://www1.futureelectronics.com/doc/EVERLIGHT%C2%A0/334-15__T1C1-4WYA.pdf) for en hvid LED. Her fandt vi frem til at dens ”Forward Voltage(Vf) = 3.6V” og ”Forward Current(If) = 30 mA”. Dermed kan vi opsætte følgende formel for beregning af modstandens størrelse.

Ohms lov
R = V / I
3.6 V / 0.030 A = 120 ohm

Opsætning af LDR-modstande

Det næste punkt er at opsætte LDR-modstandene. Strømmen igennem de 2 LDR-modstande, skal ligesom de to LEDer komme direkte fra Arduinoens 5V udgang. Efter den 2 LDR-modstande skal der opsættes en spændingsdeler, forklaring hvorfor følger. Herefter løber den mod GND, men vej til GND skal strømmen føres igennem en modstand på 10k ohm, grundlag for hvilket vil blive givet nedenunder. Opsætning af dette med 2 LDR-modstande, 2 modstande og ledninger kan ses på billedet nedenfor.

Opsætning af LDR-modstandende

En Arduino måler forskellen i volt, hvorimod måling af modstand er svært. I dette projekt benyttes en LDR-modstand, som er en variabel modstand. Jo mere lys der er til stede, desto mindre modstand er der. Derfor skal der findes en metode hvorpå volt kan udregnes. Grunden til at Arduinoen har svært ved at måle modstand er begrundet i, at den indeholder et lille system ved navn “Analog to Digital Converter (ADC)”. Siden en ADC er designet til at måle volt og man fx gerne ville se LDR-modstandens målinger via “analogRead”, så bliver man nød til at kunne konvertere den varierende modstand til volt som Arduinoen kan læse. Dette kan gøres ved hjælp af en spændingsdeler. En spændingsdeler kan laves ved at indsætte en modstand, hvorpå man får et referencepunkt, så det er muligt at kunne måle volt forandringerne. For at kunne måle værdien (Vout), målt af LDR-modstandene, kan man gøre brug af ohms lov. Først skal strømstyrken findes igennem vores kredsløb og det bliver gjort på følende måde:

I = Vin / (R1+R2)

I dette tilfælde er Vin = 5V, R1 er i dette tilfælde LDR-modstanden (model PGM 5516) som havde en minimummodstand på 5k ohm og en maksimummodstand på 10K ohm, som kan findes på følgende hjemmeside: http://ronja.twibright.com/datasheets/cds-resistor-pgm.pdf. Ved R2 benyttes en 10K ohm modstand på baggrund af følgende hjemmeside: https://www.instructables.com/id/How-to-Use-a-Light-Dependent-Resistor-LDR/?fbclid=IwAR1y4NyYJvHT6n7_Msk5Gt3PhJ_yr2lD_bbDA_5nxKEBTSzVU6HaXH6H5hI.

Dermed kan man udlede følgende ved Vout:

Vout = I * R2

Hvorefter man kan udlede følgende formel for Vout:

Vout = Vin * R2/(R1+R2)

Ved at bruge tidligere nævnte værdier vil udregningen for maksimum modstand ved LDR-modstanden (Vout):

Vout = 5 * 10000/(10000+10000)
Vout = 2.5V

Udregningen for minimum modstand ved LDR-modstanden (Vout):

Vout = 5 * 10000/(5000+10000)
Vout = 3.33V

Dette kan eftertjekkes ved at opbygge kredsløbet i Tinkercad og dermed måle Vout ved LDR-modstandes minimale og maksimale modstand, henholdsvis 2,5V og 3,33V, som kan ses på nedstående billeder.

Volt ved 10k ohm modstand på LDR
Volt ved 5k ohm modstand på LDR

De 2 LDR-modstande skal bygges ovenpå de allerede eksisterende LEDer, men kredsløbene er stadig forskellige. Grunden til at de skal bygges ved siden af hinanden er at lyset fra LEDerne skal reflekteres direkte ind i LDR-modstandene. Dermed kan det nuværende kredsløb ses på nedstående billede.

Opsætning af LEDer og LDR-modstande

Opsætning af H-Bro og 2 DC motorer

For at få motorerne til at dreje rundt, har vi valgt at bruge en “H-bridge motor driver”(model L293D). Kort fortalt kan man bruge en h-bro til at ændre kørselsretningen på fx en motor. Dette sker ved at man ændre på strømmens retning igennem motoren, derved kan man få en motor til at køre frem og tilbage. Dette kan også illustreres på nedstående billede, som beskriver hvordan en almindelig h-bros elektronisk kredsløb fungerer. En typisk h-bro består af 4 ”kontakter” i form nogle transistorer (BJT og FET). En transistor er kort fortalt lavet af en halvleder og har til opgave at lade samt forstærke et svagt elektrisk signal. Hvis man lukker kontakt 1 og 4, vil strømmen løbe igennem motoren og få den til at dreje den ene vej, som det ses på billede X. Omvendt lukker man kontakt 2 og 3, vil også strømmen løbe igennem motoren, men denne gang vil motoren dreje i den anden retning, som det ses på billederne nedenfor

Strømmen kører her igennem motoren fra positiv til negativ, og motoren vil køre fremad
Strømmen kører her igennem motoren fra negativ til positiv, og motoren vil køre bagud

 Grunden til at det er en god ide at benytte L293D h-bro til dette projekt, er at det er en “dual motor H-bridge motor control”. Dette betyder at man har mulighed for at tilkoble to DC-motorer til en h-bro og derved har mulighed for at kontrollere to motor på en gang. Nedenfor ses et diagram af L293D h-broen med forklaringer, hvilket man skal bruge for at opsætte h-broen i et kredsløb.

L293D dual motor H-bro

Opsætning af dette med 1 L2930 h-bro, 2 DC-motorer og ledninger kan ses på billedet nedenfor. Her er det vigtigt at man forbinder motorerne til PWM (Pulse Width Modulation), fordi det giver muligheden for at styre hastigheden på motorerne. PWM gør brug af analogwrite, som rækker fra 0 til 255, hvorimod brugen af digitalwrite kun kan bruge HIGH og LOW (tænd og sluk). I Tinkercad koden bliver der brugt lave PWM-værdier (det højeste er 2), for at få omdrejninger på motoren ned. Samme resultat kunne have været opnået med en høj gearing. Denne værdi kunne også have blevet justeret, hvis vi kunne have testet projektet i et fysisk miljø.

På h-broen skal Enable 1,2, Enable 3,4, Vcc 1 og Vcc 2 gives strøm for at give begge motorer adgang til strømmen. De 4 GND pins til sættes til GND på arduinoen, og fungerer som udgangen for strømmen. Hver Input pin styrer hvor meget strøm der gives til den motor den er tilsluttet, i den retning den er tilsluttet motoren. Dette signal bliver ført gennem h-broens Output pins til selve motorerne.

Opsætning i Tinkercad med motorer, h-bro og en 5V spændingsregulator

Opsætning af ultralydsafstandsmåler og servomotor

Det næste punkt er forklaring af ultralydsafstandsmåler, og dennes opsætning. Denne sensor bruges til at måle afstand ved hjælp af ultralyd. Den udsender lydbølger og når lydbølgerne rammer et objekt, vil de returnere til sensoren, som beregner tiden det tog. Herefter kan man omregne tiden til afstand og dermed finde ud af hvor langt væk man er fra objektet. Denne beregning er inkluderet i Tinkercad koden, som findes længere nede i bloggen. For at give ultralydsafstandsmåleren mulighed for at kigge rundt om hele robotten, har vi monteret denne på en servomotor der sidder på toppen af robotten.

En servomotor har integreret gear og aksel, hvilket medfører at den kan styres meget præcist. En standard servomotor giver mulighed for at placere akslen i forskellige vinkler, som typisk rækker fra 0 – 180 grader. Dette tillader robotten at benytte denne til at kigge til både venstre (0 grader) og højre (180 grader), da den som udgangspunkt kigger lige frem (90 grader). Ultralydsafstandsmåleren har 4 pins: Vcc, Trigger, Echo og GND. Vcc føres til 5V og GND føres ned til GND og sørger for strøm til den. Trigger beskriver input og Echo beskriver output og de er henholdsvis forbundet til digital pin 13 og 12. Servomotoren har 3 pins: GND, Signal og Power. Power føres til 5V og GND føres ned til GND og sørger for strøm til den. Signal skal forbindes til enten digital pin 9 eller 10 ellers virker den ikke. I vores tilfælde valgte vi pin 9. Derudover fungerer den både som input og output, og fortæller hvordan servomotoren skal dreje, samt måler hvilken position den befinder sig i. Opsætning af dette med 1 HC-SR04 ultralydsafstandsmåler, 1 SM-S2309S Micro Servomotor og ledninger kan ses på billedet nedenfor.

Opsætning af ultralydsafstandsmåler og servomotor

Opsætning af spændingsregulator og slideswitch

Den sidste del af opsætningen, vil der blive forklaret brugen af en spændingsregulator, samt dens opsætning. Da Lego motorerne kører mest effektivt ved 9V, i stedet for de 5V en Arduino kan levere, vil det være mere effektivt at køre disse på en anden strømkilde. Ved at give motorerne strøm fra et 9V batteri, vil det blive sikret at motorerne kører så effektivt som muligt. Resten af kredsløbet har dog brug for 5V, og for at opnå dette bliver der benyttet en spændingsregulator (LM7805). Denne leverer 5V til resten af kredsløbet samt Arduinoen. Derudover er der indsat en ”slideswitch”, der giver muligheden for at slukke og tænde hele kredsløbet. Opsætning af dette med 1 spændingsregulator 5V, 1 9V batteri, 1 slideswtich og ledninger kan ses på billedet nedenfor.

Opsætning af spændingsregulator, batteri og slideswitch

Endeligt kredsløb

Hvis alt bliver sat op ordentligt, burde kredsløbet komme til at se nogenlunde sådan her ud

Det endelige kredsløb, med alle komponenter sat til

Uredigeret kode

Hele koden som den bliver brugt, med kommentarer

Arduino / Tinkercad:

//Registering af servo motor
#include <Servo.h>

//Motorer
  //Motor A (Venstre side)
const int motorPin1  = 5;
const int motorPin2  = 6;
  //Motor B (Højre side)
const int motorPin3  = 10;
const int motorPin4  = 11;

//Ultrasonic Sensor
const int pingPin = 13; // Trigger Pin på Ultrasonic Sensor
const int echoPin = 12; // Echo Pin på Ultrasonic Sensor

//Servomotor
const int servoPin = 9;

//Variabler til distancemåling
long duration, cm;

//LDR-modstande
const int sensorPin1 = A2; 
const int sensorPin2 = A3; 

//Variabler til lysmåling
int sensorValue1 = 0; 
int sensorValue2 = 0;
//Minimumsforskel mellem de 2 LDR-modstande
int minForskel = 50;  


Servo servoMotor;

void setup(){
 
  //Motorpins bliver sat som output
  pinMode(motorPin1, OUTPUT); //Motor 1
  pinMode(motorPin2, OUTPUT); //Motor 1
  pinMode(motorPin3, OUTPUT); //Motor 2
  pinMode(motorPin4, OUTPUT); //Motor 2
     
  //Echo og Ping på Ultrasonic Sensor bliver sat til in-, og output
  pinMode(echoPin, INPUT);
  pinMode(pingPin, OUTPUT);
  
  //Servo motoren er sat til pin 9
  servoMotor.attach(servoPin);
  
  
  Serial.begin(9600); // Starting Serial Terminal
}


void loop(){
  //Aktivere målafstand funktionen
  maalAfstand();
  
  //Hvis robotten måler noget inden for 25 cm, stopper den
  if(cm < 25){
    //Stop bilen
    analogWrite(motorPin1, 0);
    analogWrite(motorPin2, 0);
    analogWrite(motorPin3, 0);
    analogWrite(motorPin4, 0);
    delay(2000);
    
    //Servo motoren drejer først til højre og så til venstre 
    servoMotor.write(0);
    delay(2000);
    servoMotor.write(180); 
    
    
  } else {
    //Hvis der er ikke er noget inden for 25cm, så aktiverer den funktionen fortsæt
    fortsaet();
   
    servoMotor.write(90);
  }
}

void fortsaet(){
  
  //Definerer sensorvalue 1 og 2
  sensorValue1 = analogRead(sensorPin1); 
  sensorValue2 = analogRead(sensorPin2); 
  
  //Forskellen mellen de to værdier fra sensorvalue
  int forskel= abs(sensorValue1 - sensorValue2);
  
  //Robotten ser om den skal køre fremad eller til en af siderne
  if(forskel <= minForskel){
    //Kør fremad
    analogWrite(motorPin1, 0);
    analogWrite(motorPin2, 2);
    analogWrite(motorPin3, 0);
    analogWrite(motorPin4, 2);
    
  } else {
    //Sammenlign sensorValue 1 med sensorValue 2, for at finde ud af hvilken retning den skal dreje
    if(sensorValue1 > sensorValue2){
      while(sensorValue1 > sensorValue2){
        sensorValue1 = analogRead(sensorPin1); 
        sensorValue2 = analogRead(sensorPin2);
        //Drej højre så længe det er nødvendigt
        analogWrite(motorPin1, 0);
        analogWrite(motorPin2, 1);
        analogWrite(motorPin3, 1);
        analogWrite(motorPin4, 0);
      }
    } else {
      //Sammenlign sensorValue 1 med sensorValue 2, for at finde ud af hvilken retning den skal dreje
      while(sensorValue2 > sensorValue1) {
        sensorValue1 = analogRead(sensorPin1); 
        sensorValue2 = analogRead(sensorPin2);
        //Drej venstre så længe det er nødvendigt
        analogWrite(motorPin1, 1);
        analogWrite(motorPin2, 0);
        analogWrite(motorPin3, 0);
        analogWrite(motorPin4, 1);
      }
    }
  }
}

//Konvertere millisekunder til cm
long msTilCentimeter(long microseconds) {
  return microseconds / 29 / 2;
}

//Udsender måling hver 10 millisekund
void maalAfstand(){
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(pingPin, LOW);
  
  //Hvor langt tid det tager for lyden at vende tilbage i millisekunder
  duration = pulseIn(echoPin, HIGH);
  
  //Omregner millisekunder til cm
  cm = msTilCentimeter(duration);
  
  Serial.print(cm);
  Serial.print("cm");
  Serial.println();
  
  delay(200);
}

Unity Editor:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;

public class ArduinoMain : MonoBehaviour
{
    //Referencer til andre objekter i Unity
    public Breadboard breadboard;
    public Servo servo;
    public TimingScript timer;

    //Variabler til lysmåling
    int sensorValue1 = 0;
    int sensorValue2 = 0;
    //Minimumsforskel mellem de 2 LDR-modstande
    int minForskel = 50;
    //Hvor mange gange robotten har drejet om den første obstacle
    int antalRotation;
    //Hvor mange gange robotten har drejet i den sidste halvdel af banen
    public int endTurns;

    //Hvor langt der er til forskellige sider, bruges til at vælge hvilken retning robotten skal vælge at køre
    ulong distanceTilVenste = 0;
    ulong distanceTilHoejre = 0;
    ulong distanceFremad;

    //Hvilken position har servomotoren på et givent tidspunkt
    int servoRotation;

    //Er robotten på den sorte linje / Skal den følge linjen
    bool followLine = true;
    //Skal robotten køre uden om den første udfordring
    bool dodge = false;
    //Har robotten taget det første sving ved den første udfordring
    bool firstDodge = true;
    //Er robotten i den sidste del at banen
    bool navigate = false;

    IEnumerator setup()
    {
        //Har robotten fundet servoen
        if (servo != null)
            //Få servoen til at pege ligeud hvis denne eksisterer
            servo.write(90);

        Debug.Log("pre-delay log");
        yield return delay(2000); //2 second delay

        //following region ensures delay-functionality for setup() & loop(). Do not delete, must always be last thing in setup.
        #region PremadeSetup
        yield return StartCoroutine(loop()); ;
        #endregion PremadeSetup
    }

    IEnumerator loop()
    {
        //Your code goes here:

        //Gør det nemt at ændre farten for at få robotten til at køre så optimalt som muligt
        int forwardSpeed;
        if (firstDodge)
        {
            forwardSpeed = 120;
        }
        else
        {
            forwardSpeed = 88;
        }

        //Hvor meget lys kommer ind i LDR-modstandende
        sensorValue1 = analogRead(5);
        sensorValue2 = analogRead(6);
        //Hvor stor er forskellen mellem LDR-modstandende
        int forskel = Mathf.Abs(sensorValue1 - sensorValue2);

        //Opdater hvilken position servomotoren befinder sig i
        servoRotation = servo.read();

        //Mål hvor langt der er fra afstandsmåleren til det nærmeste objekt
        ulong distance = pulseIn(4);

        //Kør rundt om den første forhindring på den sorte streg ved hjælp af afstandsmåler
        //Hvis robotten kører tæt på forhindringen, stop med at følge den sorte streg og sig at den skal undvige
        //Distacen må aldrig måles til 0, da afstandsmåleren returnerer 0 hvis den ike rammer noget
        if (followLine && distance <= 375 && distance != 0)
        {
            //Stop motoren
            analogWrite(0, 0);
            analogWrite(1, 0);
            analogWrite(2, 0);
            analogWrite(3, 0);
            followLine = false;
            dodge = true;
            //Få servoen og afstandmåleren til at kigge mod højre
            servo.write(180);
            //Havde problemer med at få delay til at virke, så bruger WaitForSeconds i stedet, men funktionen er den samme
            yield return new WaitForSeconds(0.3f);
        }
        //Hvis det er første gang den måler at den er for tæt på en klods (Hvis det er den første objekt)
        if (dodge)
        {
            if (distance <= 300 && distance != 0)
            {
                //Kør robotten ligeud i 1.5 sekunder, og få servoen til at kigge skråt frem mod højre
                servo.write(135);
                analogWrite(0, 50);
                analogWrite(1, 0);
                analogWrite(2, 50);
                analogWrite(3, 0);
                yield return new WaitForSeconds(1.5f);
                analogWrite(0, 0);
                analogWrite(1, 0);
                analogWrite(2, 0);
                analogWrite(3, 0);
                antalRotation = 1;
            }
            //Hvis det er den første gang robotten drejer ved den første forhindring, drej mod venstre
            else if (firstDodge)
            {
                firstDodge = false;
                analogWrite(0, 0);
                analogWrite(1, 44);
                analogWrite(2, 44);
                analogWrite(3, 0);
            }
            //Hvis det er anden eller tredje gang robotten skal dreje rundt om den første forhindring, kør lige ud i et bestemt stykke tid
            else if (distance <= 630 && distance > 350 && distance != 0 && antalRotation == 1 || distance <= 630 && distance > 350 && distance != 0 && antalRotation == 2)
            {
                Debug.Log(distance);
                analogWrite(0, 50);
                analogWrite(1, 0);
                analogWrite(2, 50);
                analogWrite(3, 0);
                //Hvor lang tid den skal køre lige ud kommer an på om det er første eller anden gang robotten når dette stykke kode
                if(antalRotation == 1)
                    yield return new WaitForSeconds(2.4f);
                else
                    yield return new WaitForSeconds(1.3f);
                //Stop robotten
                analogWrite(0, 0);
                analogWrite(1, 0);
                analogWrite(2, 0);
                analogWrite(3, 0);
                //Holder styr på hvor mange gange robotten har drejet
                if (antalRotation == 1)
                    antalRotation = 2;
                else
                {
                    antalRotation = 3;
                    //Hvis det er tredje gang den drejer, peg servootoren ligeud
                    servo.write(90);
                }
            }
            //Hvis robotten har drejet 1 eller 2 gange om første forhindring, og den skal dreje igen, drej mod højre
            else if (antalRotation == 1 || antalRotation == 2)
            {
                analogWrite(0, 44);
                analogWrite(1, 0);
                analogWrite(2, 0);
                analogWrite(3, 44);
            }
            //Hvis det er tredje gang, drej mod venstre
            else if (antalRotation == 3)
            {
                analogWrite(0, 0);
                analogWrite(1, 44);
                analogWrite(2, 44);
                analogWrite(3, 0);
                //Hvis robotten er en meget specifik distance væk fra en væk den kommer til at kigge på efter at dreje, begynd at følg den sorte linje igen
                if(distance < 2450 && distance > 2430)
                {
                    analogWrite(0, 0);
                    analogWrite(1, 0);
                    analogWrite(2, 0);
                    analogWrite(3, 0);
                    dodge = false;
                    followLine = true;
                }
            }
        }

        //Følg linjen i den første halvdel af banen
        if (followLine){
            //Hvis de to LDR-modstande returnerer det samme resultat
            if (forskel <= minForskel)
            {
                //Kør fremad
                analogWrite(0, forwardSpeed);
                analogWrite(1, 0);
                analogWrite(2, forwardSpeed);
                analogWrite(3, 0);
            }
            //Hvis den højre LDR-modstand er mørkere end den venstre
            else if (sensorValue1 > sensorValue2)
            {
                //Drej til højre
                analogWrite(0, 62);
                analogWrite(1, 0);
                analogWrite(2, 5);
                analogWrite(3, 0);
            }
            //Hvis den venstre LDR-modstand er mørkere end den højre
            else if (sensorValue1 < sensorValue2)
            {
                //Drej til venstre
                analogWrite(0, 5);
                analogWrite(1, 0);
                analogWrite(2, 62);
                analogWrite(3, 0);
            }
            //Hvis robotten kører over den sidste streg på den sorte linje
            if (sensorValue1 < 1000 && sensorValue2 < 1000)
            {
                //Retter robotten op så den står lige
                followLine = false;
                analogWrite(0, 0);
                analogWrite(1, 44);
                analogWrite(2, 50);
                analogWrite(3, 0);
                yield return new WaitForSeconds(0.3f);
                analogWrite(0, 0);
                analogWrite(1, 0);
                analogWrite(2, 0);
                analogWrite(3, 0);
                Debug.Log(distance);
                //Få robotten til at navigere ved hjælp af afstandsmåleren
                navigate = true;
            }
        }

        //Kør rundt mellem væggende i den anden halvdel af banen
        if (navigate)
        {
            if (distance > 600 && distance != 0)
            {
                //Kør fremad
                analogWrite(0, 75);
                analogWrite(1, 0);
                analogWrite(2, 75);
                analogWrite(3, 0);
            }
            //Stop og overvej hvad der skal ske nu
            else
            {
                analogWrite(0, 0);
                analogWrite(1, 0);
                analogWrite(2, 0);
                analogWrite(3, 0);
                distance = pulseIn(4);
                distanceFremad = distance;
                //Hvis robotten har drejet 2 gange i det sidste stykke, drej 90 grader mod højre
                if(endTurns == 2)
                {
                    analogWrite(0, 43);
                    analogWrite(1, 0);
                    analogWrite(2, 0);
                    analogWrite(3, 43);
                    servo.write(90);
                    endTurns++;
                    yield return new WaitForSeconds(1.4f);
                    Debug.Log("We're in the endgame now");
                    analogWrite(0, 0);
                    analogWrite(1, 0);
                    analogWrite(2, 0);
                    analogWrite(3, 0);
                }
                //Derefter kør hurtigt mod målstregen
                else if (endTurns > 2)
                { 
                    analogWrite(0, 110);
                    analogWrite(2, 110);
                    analogWrite(1, 0);
                    analogWrite(3, 0);
                    //Hvis robotten rammer målstregen, stop timeren
                    if (sensorValue1 < 1000 && sensorValue2 < 1000)
                    {
                        timer.timeUp = true;
                    }
                }
                //Hvis robotten har drejet mindre end 2 gange
                if (endTurns < 2)
                {
                    //Kig først til højre og mål afstand, så til venstre og gør det samme
                    servo.write(180);
                    yield return new WaitForSeconds(1.2f);
                    distance = pulseIn(4);
                    distanceTilHoejre = distance;
                    servo.write(0);
                    yield return new WaitForSeconds(2.3f);
                    distance = pulseIn(4);
                    distanceTilVenste = distance;
                    //Hvis højre er længere væk end venstre, vend mod højre og kør den vej
                    if (distanceTilHoejre > distanceTilVenste && distanceFremad != 0)
                    {
                        analogWrite(0, 44);
                        analogWrite(1, 0);
                        analogWrite(2, 0);
                        analogWrite(3, 44);
                        servo.write(90);
                        endTurns++;
                        yield return new WaitForSeconds(1.3f);
                    }
                    //Hvis venstre er længere væk end højre, vend mod venstre og køre den vej
                    else if (distanceTilHoejre < distanceTilVenste&& distanceFremad != 0)
                    {
                        analogWrite(0, 0);
                        analogWrite(1, 43);
                        analogWrite(2, 43);
                        analogWrite(3, 0);
                        servo.write(90);
                        endTurns++;
                        yield return new WaitForSeconds(1.3f);
                    }
                }
            }
        }

        //Following region is implemented as to allow "yield return delay()" to function the same way as one would expect it to on Arduino.
        //It should always be at the end of the loop()-function, and shouldn't be edited.
        #region DoNotDelete
        //Wait for one frame
        yield return new WaitForSeconds(0);
        //New loop():
        yield return loop();
        #endregion DoNotDelete 
    }



    #region PremadeDefinitions
    void Start()
    {
        servo = FindObjectOfType<Servo>();
        if (servo == null)
        {
            Debug.Log("No servo found in the scene. Consider assigning it to 'ArduinoMain.cs' manually.");
        }
        Time.fixedDeltaTime = 0.005f; //4x physics steps of what unity normally does - to improve sensor-performance.
        StartCoroutine(setup());


    }


    IEnumerator delay(int _durationInMilliseconds)
    {
        float durationInSeconds = ((float)_durationInMilliseconds * 0.001f);
        yield return new WaitForSeconds(durationInSeconds);
    }

    public long map(long s, long a1, long a2, long b1, long b2)
    {
        return b1 + (s - a1) * (b2 - b1) / (a2 - a1);
    }

    public ulong millis()
    {
        return (ulong)(Time.timeSinceLevelLoad * 1000f);
    }

    public ulong abs(long x)
    {
        return (ulong)Mathf.Abs(x);
    }

    public long constrain(long x, long a, long b)
    {
        return (x < a ? a : (x > b ? b : x));
    }


    #endregion PremadeDefinitions

    #region InterfacingWithBreadboard
    public int analogRead(int pin)
    {
        return breadboard.analogRead(pin);
    }
    public void analogWrite(int pin, int value)
    {
        breadboard.analogWrite(pin, value);
    }
    public bool digitalRead(int pin)
    {
        return breadboard.digitalRead(pin);
    }
    public void digitalWrite(int pin, bool isHigh)
    {
        breadboard.digitalWrite(pin, isHigh);
    }

    public ulong pulseIn(int pin)
    {
        return breadboard.pulseIn(pin);
    }
    #endregion InterfacingWithBreadboard
}

Konklusion

A.R.S.E kører igennem den angivne bane i Unity på 67,08224 sekunder, hvilket er den absolut hurtigste tid vi opnåede. Den bruger en kombination, af LDR-modstandene og LEDerne til at navigere på den sorte streg. Undervejs bruger den ultralydsafstandsmåler til at navigere udenom det først objekt på banen, samt den sidste halvdel af banen, hvor der ikke er nogen sort streg. Denne kombination af alle vores komponenter medfører at A.R.S.E formår at gennemføre banen uden at ramme nogle objekter. Grundet COVID 19 situationen skulle robotten køre igennem en virtuel bane i Unity, hvilket til tider kunne være lidt inkonsekvent og fejlbehæftet.

Perspektivering

Når A.R.S.E gennemfører banen, er det ikke fordi den altid er klog. Robotten bliver flere gange guidet af såkaldt ”hard coding”, altså vi fortæller robotten præcis hvad den skal gøre på forskellige tidspunkter af banen. For at undgå dette ville det blive nødvendigt at få den til at kunne reagere på banen af sig selv, eventuelt ved at give den en slags Artificial Intelligence, eller A.I. Robotten ville i sådan en situation selv kunne finde ud af hvordan denne skulle benytte sine forskellige input muligheder, men desværre var dette ikke muligt for os at opnå.

A.R.S.E formår som nævnt tidligere at gennemføre banen uden problemer, men for at forbedre den yderligere kunne dennes sensorplaceringer ændres. Lige nu kører den med de to LDR-modstande meget tæt på hinanden, hvilket forsager at A.R.S.E drejer ret tidligt når den når et sving. Hvis den kunne dreje senere ville den kunne holde topfarten hvor den kører ligeud længere, og dermed opnå en hurtigere tid. Dette ville potentielt medføre andre problemer, som for eksempel at den ikke ville reagere på slutstregen af den sorte linje, men med gennemgående testing kunne det potentielt forbedres.
Det samme gælder placeringen af servomotor/ultralydsafstandsmåler comboen. Som de sidder lige nu virker de, men der er plads til forbedring ved f.eks. at placere disse mere centreret på robotten for at få bedre målinger i sving. Dette ville dog ødelægge den ”hard coding” vi på nuværende tid har skrevet, og vil ikke nødvendigvis forbedre robotten.

I vores kredsløb valgte vi at have en ledning fra Arduinoens 5V udgang og over til positiv på breadboardet. Dette medfører at det kun er motorerne der slukker, når man slukker slideswitchen i Tinkercad. I vores tilfælde var det meningen, at slideswitchen skulle slukke hele kredsløbet skulle slukke. Vælger man at slette denne ledning, vil hele kredsløbet slukke, dog vil man ikke kunne bygge det i virkeligheden, da Arduinoen ikke vil få noget strøm fra USBen eller 9V batteriet. Derfor valgte vi at lade ledningen sidde.  

Video af robotten på banen

A.R.S.Es bedste tid på banen

Leave a Reply