Nikolaj Martinus. Kristensen, Mira Berg Schmidt, Simone Dalager

Robottens navn: Sneglen

Introduktion

I denne PF2 opgave blev vi stillet en opgave om at skulle gennemfører en virtuel bane i unity, ved at bygge en “robot” som skal kunne følge en sort linje og undgå objekter. Opgaven går mere ud på at lave et program og en opbygning af robotten, der bringer den sikkert i mål uden at den kører af stregen eller kører ind i objekter.

Til at kunne gennemfører dette benyttes en arduino,  2 DC Motorer, 2 lyssensorer, 2 lys LDR, 1 Sonar Sensor, 1 servomotor som bliver opsat unity.

Dette blogindlæg vil indeholde afsnit omkring b.la hardwaren, opbygning af programmet, en konklusion og til sidst en perspektivering. 

Tutorial

Hvad skal bruges til opbygningen af Sneglen

  • Arduino Uno
  • 1 eller 2 breadboard
  • 2 hvide LED’er
  • 2 LDR
  • 2 stk.10 Kohm modstande
  • 2 stk.450 ohm modstande
  • 2 DC gearing motor
  • L293D H-Bro
  • 9V batteri
  • 5V spændingsregulator
  • Servo motor
  • HC-SR04 sonar

Princippet bag Sneglens linje følger

Sneglen kan følge en sort linje på en hvid baggrund ved hjælp af lys. Hvidt lys på en hvid overflade vil næsten reflektere helt tilbage, hvor på en sort overflade vil lyset blive absorberet. Det er dette princip Sneglen bruger i sin opførsel. Det er de 2 LED’er der skaber lyset mod jorden/overfladen og de 2 LDR’er som aflæser den reflekterende lys der kommer tilbage.

Reference for billede findes i kilder

Når begge LDR’er får reflekteret det meste af lyset tilbage, fordi de befinder sig over en hvid overflade vil Sneglen bevæge sig lige ud. Det betyder nemlig at der er en LDR på hver side af stregen

//if both sensors see white then forward
            if (leftLDRValue > 500 & rightLDRValue > 500)
            {
                Debug.Log("Move forward");
                analogWrite(motorRight1, motorspeedGo);
                analogWrite(motorRight2, motorspeedStop);
                analogWrite(motorLeft1, motorspeedGo);
                analogWrite(motorLeft2, motorspeedStop);

                //yield return delay(10);
            }

Reference for billede findes i kilder

Når den venstre LDR får en lav værdi skyldes dette at den bevæger sig over den sorte streg, hvor det hvide lys absorberes. Når dette sker vil motoren på Sneglens højre hjul køre hurtigere og dermed drejer Sneglen til venstre.

//left
if (leftLDRValue < 500 & rightLDRValue > 500)
            {
                Debug.Log("Turn left");
                analogWrite(motorRight1, turnSpeed); //forsøg
                analogWrite(motorRight2, motorspeedStop);
                analogWrite(motorLeft1, notTurnwheel);
                analogWrite(motorLeft2, 20);

                //yield return delay(20);//forsøg
            }

Reference findes i kilder

Det er det samme princip der sker når den højre LDR rammer den sorte streg, som når den venstre LDR gør. I stedet vil motoren på Sneglens venstre hjul nu køre hurtigere og det vil få sneglen til at dreje mod højre.

//right
            if (leftLDRValue > 500 & rightLDRValue < 500)
            {
                Debug.Log("Turn right");
                analogWrite(motorRight1, motorspeedStop);
                analogWrite(motorRight2, motorspeedStop);
                analogWrite(motorLeft1, turnSpeed);
                analogWrite(motorLeft2, motorspeedStop);

                //yield return delay(10);
            }

Reference for billede findes i kilder

Når begge LDR’er ikke får nogen værdi tilbage betyder det at de begge befinder sig over den sorte streg hvor alt lys absorberes. Denne sorte streg er en stoplinje for Sneglen og dermed stopper Sneglen med at køre når begge LDR rammer den.

if (leftLDRValue < 500 & rightLDRValue < 500)
            {
                Debug.Log("Stop");
                analogWrite(motorRight1, motorspeedStop);
                analogWrite(motorRight2, motorspeedStop);
                analogWrite(motorLeft1, motorspeedStop);
                analogWrite(motorLeft2, motorspeedStop);

                yield return delay(500);
                followLine = false;
                followSonar = true;
            }

Opbygning

Vi har ikke haft mulighed for at kunne bygge sneglen fysisk. VI har bygget den i Unity. I Unity består sneglen af en turkis plade som forestiller en Ardunio. Der er koblet to styrende hjul på hver sin side af Arduinoen. Foran er et fri rullende hjul som skal sikre balance og sørge for at den hverken tipper/vælter. Under pladen er to firkanter, som forestiller de to LED’er og LDR’er. I Unity har de fået lyskilder tilføjet, så vi styre ikke LED’er i koden i Unity. Afstanden mellem de to firkanter må ikke være for stor eller for lille ellers vil den ikke være præcis nok. Afstanden mellem dem skal være lidt større end bredden på den sorte streg for at de bedst kan læse. Oven på pladen er der en orange firkant med tre små forskellig farvede firkanter. Dette skal forestille en servomotor. De små farvede firkanter indikere vinklerne 0, 90 og 180 grader. Kapslen oven på firkanten peger i den retning som servo vinkel er indsat til. Ovenpå servomotoren er der en sonar. Sonaren sidder oven på servoen for at den drejer med servoen rundt, på denne måde kan man vinkle sonaren i den retning man ønsker den skal dreje.

I den fysiske verden ville der være noget til forskel. Vi vil her komme ind på nogle af de forskelle der er. Placeringen LDR’erne er ikke ligegyldig som det også er tilfældet i Unity. Dog er der en forskel på placeringen af LDR’erne i forhold til LED’erne. LDR’erne er bedste placeret lidt over LED’erne da de derved nemmere kan opfange den reflekterede lys fra overfladen. Ved en fysisk opbygning af Sneglen ville der være langt flere aspekter at tage højde for i opbygningen, men det omhandler meget hvordan den opbygges i lego og dette er svært at beskrive når man ikke selv har gjort det.

For at lave opsætningen i Unity har vi fået udleveret et Unity projekt med de forskellige “komponenter”, som vi skal bruge. Her er det vigtigt at huske opsætningen i Unity. H-broerne skal refereres til den tilhørende motor og hjul. Linjesensor skal være “child” af “robot chassis” da den ellers ikke vil følge med robotten. Hvis man har problemer med at hjulene ikke følger roboten skal man se om deres “connected body” under “Configurable Joint” er “CarChassis”. Sonaren skal være “child” af “CarChassis”

Opsætning af Hierakiet i Unity:

Video af testkørsel

Vedhæftet nedenfor ses en video af  “Sneglens” hurtigste tid på 77 sekunder, når man trækker delayed på 2 sekunder fra når den er sat i gang. Robotten er ikke hurtig, men formår at kører banen igennem fra start til slut, uden at berører de røde vægge og køre på afveje. Vi prioriterede at robotten gennemførte banen med de stillede regler, frem for hvor hurtigt robotten kunne komme fra start til slut punkter i banens opsætning. Hvilket resulterede i en relativ langsom tid, men en gennemført tur rundt i banen.

Testkørsel af Sneglen

Opbygning af hardware i TinkerCAD

For at finde hvilke modstand der skal på LED’erne kan man bruge ohms lov og udregne modstanden, da forspændingen(forward current) på en hvid LED er 20 mA fundet i datablad side 4

Ohms lov

http://www1.futureelectronics.com/doc/EVERLIGHT%C2%A0/334-15__T1C1-4WYA.pdf

LED’erne er hvide, da hvidt lys reflekteres bedre end f.eks. rødt lys.

LDR’erne er placeret lidt højere oppe end LED’erne derved bliver lyset reflekteret bedst i den virkelige verden. Dette er dog ikke vigtigt i vores virtuelle verden.

Modstanden for LDR’erne er ikke beregnet da deres modstand i LDR’erne jo ændre sig afhængigt af lysforholdet. Der er valgt modstand på 10 kohm ud fra en tommelfingerregel og da systemet er virtuelt vil det ikke give mening at måle på en fysisk LDR.

Vores valg faldt på HC-SR04 sonar, da det var den vi fandt bedst og mest information om da vi undersøgte hvordan vi bedst kunne bruge en sonar til at sikre sig at robotten ikke kører ind i vægge og andre objekter. I Den virtuelle verden i Unity var det også opførelsen på HC-SR04 sonaren der var blevet efterlignet og det naturlige valg faldt derfor på denne.

Spændingsregulatoren bruges til at give Arduino strøm, da den ikke kan klare 9V. Derudover kan servomotoren og sonaren heller ikke klare 9V, de bliver derfor også tilkoblet 5V spændingsregulatoren

Vi har valgt at benytte os har de analog pin til hjulenes motore, så vi kan regulere hvor hurtig de kører frem af, ved at tilskrive forskellige værdier som en hastighed mellem 0-255. Hvis vi have brugt de digitale pini stedet for, så kan vi kun tilskrive den 2 værdier som er 1 og 0, hvilket vil sige de kan være high eller low så inten kører hjulenes motorer med fuld hastighed eller stilstand. Men da vi gerne ville regulere hastigheden, på hurtig robotten skulle køre bruger vi de analog pin. 

Vi har valgt at sætte sonars output i en digital pin med pwm (pulse width modulation) da vi begyndte at løbe tør for analoge porte.

Opbygning af program 

For at lave vores system har vi brugt Arduinos servo  bibliotek og deres “new ping” bibliotek. Vi ønskede at teste vores arduino kode i TinkerCAD, men “new ping” biblioteket findes ikke i TinkerCAD og da vi prøvede at indsætte biblioteket manuelt var for meget kode for at TinkerCAD kunne simulere dette.

Hvordan er samspillet mellem mekanik, elektronik og software?
Der er ikke noget samspil da det hele er software da det foregår virtuelt. Vi kan ikke spekulere i hvad der kunne eller ikke kunne fungere i den virkelige verden når vi ikke har arbejdet med det fysisk.

Braitenberg vehicles kan autonomt bevæge sig på basis af dets sensor inputs. Den har sensorer, der måler en stimulus og hjul der skriver hver deres motor. Disse motorer fungere som aktuatorer. Når sensorer og aktuatorer bliver forbundet, og kan registrer et signal, vil et hjul blive sat i bevægelse. Dette kan overføres til hvordan vores robot “Sneglen” bevæger sig rundt på den opstillede bane. Som tidligere gennemgået i princippet bag hvordan sneglen følger en linje, har vi en sensor i højre og venstre side af robotten som hver især er forbundet til de respekterende hjul på hver side.  Når robottens ene sensor kører over den sorte streg og ser sort og den anden ser hvidt, så drejer robotten til den side hvor sensoren ser sort. Hermed har vi en opbygning der benytter samme princip som med Braitenberg vehicles om at nogle sensorer giver et signal til nogle aktuatorer om at de skal sættes i bevægelse.

Unity kode

int motorLeft1 = 0; //digitalport, pin 2 i Ardunio kode
    int motorLeft2 = 1; //digitalport, pin 3 i Ardunio kode
    int motorRight1 = 2; //digitalport, pin 4 i Ardunio kode
    int motorRight2 = 3; //digitalport, pin 5 i Ardunio kode

    int sonar = 6;

    int motorspeedStop = 0;
    int motorspeedGo = 50;
    int turnSpeed = 50;
    int notTurnwheel = 0;

    
    bool followLine = true;
    bool followSonar = false;
    bool firstTurn = false;
    bool secondTurn = false;
    bool thirdTurn = false;
    bool firstBox = false;

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

        //Example of delay:
        Debug.Log("pre-delay log");
        yield return delay(2000); //2 second delay
        Debug.Log("after delay log");
        
        //Your code ends here -----

        //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:
        // put your main code here, to run repeatedly:
        int leftLDRValue = analogRead(4); //venstre LDR værdi A1 i Arduino kode
        int rightLDRValue = analogRead(5); //højre LDR værdi, A2 i Arduino kode

        ulong distance = pulseIn(6);

        Debug.Log("loop started");

        //sætter servo til at vende lige ud

        if (followLine == true) //DET SKAL VÆRE ET IF STATEMENT (KAN IKKE LAVE WHILE LOOP I UPDATE FUNCTION I UNITY)
        {
            Debug.Log("FollowLine");
            servo.write(70);
            //if both sensor reads black then stop
            Debug.Log(distance);
            if (leftLDRValue < 500 & rightLDRValue < 500)
            {
                Debug.Log("Stop");
                analogWrite(motorRight1, motorspeedStop);
                analogWrite(motorRight2, motorspeedStop);
                analogWrite(motorLeft1, motorspeedStop);
                analogWrite(motorLeft2, motorspeedStop);

                yield return delay(500);
                followLine = false;
                followSonar = true;
            }

            //right
            if (leftLDRValue > 500 & rightLDRValue < 500)
            {
                Debug.Log("Turn right");
                analogWrite(motorRight1, motorspeedStop);
                analogWrite(motorRight2, motorspeedStop);
                analogWrite(motorLeft1, turnSpeed);
                analogWrite(motorLeft2, motorspeedStop);

                //yield return delay(10);
            }

            //left
            if (leftLDRValue < 500 & rightLDRValue > 500)
            {
                Debug.Log("Turn left");
                analogWrite(motorRight1, turnSpeed); //forsøg
                analogWrite(motorRight2, motorspeedStop);
                analogWrite(motorLeft1, notTurnwheel);
                analogWrite(motorLeft2, 20);

                //yield return delay(20);//forsøg
            }

            //if both sensors see white then forward
            if (leftLDRValue > 500 & rightLDRValue > 500)
            {
                Debug.Log("Move forward");
                analogWrite(motorRight1, motorspeedGo);
                analogWrite(motorRight2, motorspeedStop);
                analogWrite(motorLeft1, motorspeedGo);
                analogWrite(motorLeft2, motorspeedStop);

                //yield return delay(10);
            }

            if(firstBox == false & distance < 700 & distance > 10)
            {
                Debug.Log("Turn right");
                analogWrite(motorRight1, motorspeedStop);
                analogWrite(motorRight2, 50);
                analogWrite(motorLeft1, 80);
                analogWrite(motorLeft2, motorspeedStop);
                yield return delay(450);

                Debug.Log("Move forward");
                analogWrite(motorRight1, motorspeedGo);
                analogWrite(motorRight2, motorspeedStop);
                analogWrite(motorLeft1, motorspeedGo);
                analogWrite(motorLeft2, motorspeedStop);
                yield return delay(500);

                Debug.Log("Turn left");
                analogWrite(motorRight1, turnSpeed); //forsøg
                analogWrite(motorRight2, motorspeedStop);
                analogWrite(motorLeft1, 15);
                analogWrite(motorLeft2, motorspeedStop);
                yield return delay(5100);
                firstBox = true;
            }
        }


        //servo and sonar
        if (followSonar == true) 
        {
            servo.write(75);
            Debug.Log("followSonar");
            //go forward for a small period of time
            Debug.Log("Move forward Sonar ");
            analogWrite(motorRight1, motorspeedGo);
            analogWrite(motorRight2, motorspeedStop);
            analogWrite(motorLeft1, motorspeedGo);
            analogWrite(motorLeft2, motorspeedStop);
            yield return delay(10);
            //Debug.Log(distance);

            if (thirdTurn == true & secondTurn == false & firstTurn == false)
            {
                Debug.Log("Move forward");
                analogWrite(motorRight1, motorspeedGo);
                analogWrite(motorRight2, motorspeedStop);
                analogWrite(motorLeft1, motorspeedGo);
                analogWrite(motorLeft2, motorspeedStop);
            }

            //kør frem indtil man er x cm fra væg, drej til venstre, drej servo 90 grader
            if (distance < 700 & thirdTurn == false & secondTurn == false & firstTurn == false)
            {
                Debug.Log("Turn left Sonar");
                analogWrite(motorRight1, 80); //forsøg
                analogWrite(motorRight2, motorspeedStop);
                analogWrite(motorLeft1, notTurnwheel);
                analogWrite(motorLeft2, 50);
                yield return delay(1070);
                firstTurn = true;
                secondTurn = false;
                thirdTurn = false;
                Debug.Log(distance);
                //pulseIn(6);
                //yield return delay(10);
            }
           
            if (distance > 600 & firstTurn == true)
            {
                Debug.Log("Move forward Sonar ");
                analogWrite(motorRight1, motorspeedGo);
                analogWrite(motorRight2, motorspeedStop);
                analogWrite(motorLeft1, motorspeedGo);
                analogWrite(motorLeft2, motorspeedStop);
               // yield return delay(10);
                Debug.Log(distance);
            }
            

            if (distance < 680 & firstTurn == true)
            {
                Debug.Log("Turn right Sonar");
                analogWrite(motorRight1, motorspeedStop);
                analogWrite(motorRight2, 50);
                analogWrite(motorLeft1, 80);
                analogWrite(motorLeft2, motorspeedStop);
                secondTurn = true;
                firstTurn = false;
                thirdTurn = false;
                yield return delay(900);
                Debug.Log(secondTurn);
                Debug.Log(distance);
            }

            if (distance > 600 & secondTurn == true)
            {
                Debug.Log("Move forward Sonar ");
                analogWrite(motorRight1, motorspeedGo);
                analogWrite(motorRight2, motorspeedStop);
                analogWrite(motorLeft1, motorspeedGo);
                analogWrite(motorLeft2, motorspeedStop);
                // yield return delay(10);
                Debug.Log(distance);
            }


            if (distance < 650 & secondTurn == true)
            {
                Debug.Log("Turn right sonar 2");
                analogWrite(motorRight1, motorspeedStop);
                analogWrite(motorRight2, 50);
                analogWrite(motorLeft1, 80);
                analogWrite(motorLeft2, motorspeedStop);
                yield return delay(950);
                thirdTurn = true;
                firstTurn = false;
                secondTurn = false;
                Debug.Log(distance);
            }

            if (distance > 600 & thirdTurn == true)
            {
                Debug.Log("Move forward Sonar ");
                analogWrite(motorRight1, motorspeedGo);
                analogWrite(motorRight2, motorspeedStop);
                analogWrite(motorLeft1, motorspeedGo);
                analogWrite(motorLeft2, motorspeedStop);
                // yield return delay(10);
                Debug.Log(distance);
            }

            if (leftLDRValue < 500 & rightLDRValue < 500)
            {
                Debug.Log("Stop");
                analogWrite(motorRight1, motorspeedStop);
                analogWrite(motorRight2, motorspeedStop);
                analogWrite(motorLeft1, motorspeedStop);
                analogWrite(motorLeft2, motorspeedStop);

                yield return delay(1000);
            }
        }



        //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 
    }

Konklusion

Robotten løser den stillede opgave, uden at berøre nogle af de røde vægge og kører kun af den sorte streg for at undvige den lille firkantede røde væg i starten af banen. Så hvis vi kigger på hvor godt den kører banen igennem uden at tage tid i betragtning, løser robotten opgaven godt.

Perspektivering

I den fysiske verden hvor der ville være brug af konkurrencetid, ville det give god mening at gøre robotten hurtigere og derved få en hurtigere banekørsel

Det kunne give mening at optimere koden så den er mere dynamisk. Således at Sneglen også ville kunne køre andre baner uden at man havde ændret i koden

Kilder

Lignende projekter

https://create.arduino.cc/projecthub/robocircuits/line-follower-robot-arduino-299bae

https://circuitdigest.com/microcontroller-projects/line-follower-robot-using-arduino

Opsætning af DC motor og L293D modul

https://www.instructables.com/id/How-to-use-the-L293D-Motor-Driver-Arduino-Tutorial/

Datasheet for hvid LED

http://www1.futureelectronics.com/doc/EVERLIGHT%C2%A0/334-15__T1C1-4WYA.pdf

Sonar HC-SR04

Servo SM-S2309S

https://www.bananarobotics.com/shop/SpringRC-SM-S2309S-Micro-Analog-Servo-9.9g

http://descargas.cetronic.es/microservo.pdf

Leave a Reply