Mathias Ø Hansen, Dune, Firat

I projektet har vi til opgave at udvikle en autonom robot bil der skal kunne navigere rundt på en forhindringsbane og samle objekter og køre dem i mål, i dette tilfælde nogle små trækasser. Til projektet bruger vi en Arduino Uno, Arduino motor shield, analog line sensors(LS), LEGO, LEGO stepmotor.

Metode og fremgang

Med opgavebeskrivelsen formuleret, er der problemstillinger vi bliver nød til at løse:

Sensorstyring – hvordan er det smartest at behandle den data vi får fra sensorerne og hvordan skal de sidde for at vi får det bedste resultat

Kabelstyring – hjulene og motorerne skal kunne køre frit og sensorernes kabler skal ikke forstyrres af kørsel

Styresystem – hvordan skal hjulene opbygges og hvordan er det smartest de sidder ift. kun at have to motorer

Bilen skal kunne følge mønstret på banen der fører fra start, til kasserne og til slut. Sensorerne placeres så de er med til at styrer bilen i den korrekte retning, og deres placering kommer til at sidde tæt op ad bilens karosseri da den skal kunne dreje skarpt. Der er valgt at bilen skal være forhjulstrukket da det i opstillingen vil gøre det simplere at få den til at dreje. Hjulene er uafhængige af hinanden og styres af hver sin motor, dette gør at styringen af bilen er relativ simpel.

Motor

Der er til projektet gjort brug af en Arduino Uno. En Arduino er en open-source elektronikplatform med en mikrocontroller, der kan læse indgange og gøre det til output.

Der er brugt to LEGO Power Functions L-motor. De to motorer driver de to forhjul og er monteret på samme akse. Motorerne er tilsluttet et Arduino motor shield som er en udvidelse til den originale Arduino produceret til nemmere at styre små jævnstrøm(DC) eller step motorer. Den har indbygget broer hvilket gør det muligt for den både at køre baglæns og forlæns uden at skulle have shift registers eller lignende involveret. Da hvilken vej motoren kører afhænger af hvilken vej strømmen bliver sendt gennem den.

Line Sensors

Line sensors fungerer på den måde at der er en LED der udsender infrarøde signaler som en anden sensor opfanger når de bliver reflekteret tilbage på sensoren. Hvilket betyder at det analoge output vi læser fra sensorerne afhænger af om det er et sort eller hvidt underlag bilen kører over da hvid reflekterer mere end sort gør. LS’erne er placeret nederst på vores bil med retning mod gulvet, da vi fandt ud af jo tættere på underlægget de kan sidde jo bedre. Forhindringsbanen er opbygget således at der er med en sort tyk linje er markeret bilens rute. Så vi har placeret en sensor foran hvert hjul der læser ved siden af den sorte stribe på begge sider, derved vil den opfange når den enten når et kryds eller sving.

Software

Programmeringssproget der er brugt til projektet, er skrevet i Arduinoversionen C++. Arduinoen, der er den mikrochip controller der styrer systemet, og afgøre systemets opførsel.

Opstillingen

Da vi byggede første version af bilen havde sat ét centreret baghjul på der skulle fungere som ”finne”, men det vidste sig at den var for modtagelige for bevægelser. Rotationen på baghjulet gav derfor problem for bilen med at kører en i fremadrettet retning da gulvmiljøet havde stor sandsynlighed for at påvirke. Anden version af bilen udskiftede vi hjulet med et kugleleje der ingen begrænsninger har på sin rotation.

Sensorerne er placeret foran dækkene på bilen og så tæt på gulvet som muligt, for at få præcise målinger. De er tapet på, så de ikke falder ud. Da sensorerne er placeret foran dækkene, er det lettere at dreje og fuldføre andre funktioner.
I første version af bilen brugte vi store dæk, dette ændrede vi dog til små dæk i den endelige version. Dette er gjort, da det derved vil være lettere at dreje og der derfor ikke skulle bruges lige så meget spænding for at køre.
Der er intet gearing i bilen, begge dæk sidder direkte på motoren, da vores opbygning har gjort det muligt for os, at placere dem overfor hinanden og derved har det ikke været nødvendigt med gearing.

Motorshield

Motorshieldet er en dual full-bridge driver, som tillader os at bruge to DC motorer. Hvor vi bade kan kontrollere hastigheden og retningen af hver motor selvstændigt. Shieldet har to seperate kanaler, A og B, der begge bruger 4 pins til at vælge rotationsretningen, variere hastigheden, bremse eller måle den strøm, der strømmer gennem motoren. I alt er der 8 pins i brug på dette skjold. Du kan bruge hver kanal separat til at køre to DC-motorer eller kombinere dem til at køre en bipolær stepper motor. Skjoldet kan levere 2 ampere per kanal, i alt maksimalt 4 ampere. Det giver dig også mulighed for at kunne power en motor med en separat strømforsyning på op til 12V.

Motorshieldet har en H-bro indbygget, hvilket gør det muligt, at skifte retningen af strømmen, så den både kan køre frem og tilbage.
En H-bro er et elektronisk kredsløb, der skifter retningen af ​​en spænding
Grundet, at der kan styres to motorer selvstændigt og at skjoldet kan vælge rotationsretning, har vi derfor valgt at bruge motorshieldet, så der både kan bakkes og drejes på et dæk sepereat.
På billedet nedenfor ses de to basale tilstande af en H-bro.


Grundet, at der kan styres to motorer selvstændigt og at skjoldet kan vælge rotationsretning, har vi derfor valgt at bruge motorshieldet, så der både kan bakkes og drejes på et dæk sepereat.

PCB

Gruppen har lavet et printed circuit board, også kaldet PCB. PCB bruges til at skabe elektriske kredsløb, de er lavet således at der på den ene side af brættet er kobber, der med sin metalliske egenskaber er ledende og resten ikke er ledende. På den måde, kan man sørge for, at strømmen kører i nogle bestemte retninger. I projektet er der blevet udviklet PCB til de 5 LED’er, som skulle lyse op, alt efter hvilken funktion der blev kørt. Dette blev gjort i en parallel forbindelse, så hver LED kunne lyse op uafhængigt af hinanden. Hver LED havde en resistor, da hver LED skal bruge en forskellig mængde af spænding.

Modstande

For at regne ud, hvor meget spænding hver LED skal have, bruger vi formlen:
R=(Vs-Vled)/Iled

  • Vs = Supply voltage (spændingen fra strømkilden)
  • Vled = Spændingsfaldet (afhænger af farven der bliver udsendt.
  • Iled = LED current (strømmen gennem LED’en).

Vi kigger på datasheetet (Eks. Rød LED:
(https://www.sparkfun.com/datasheets/Components/LED/COM-09590-YSL-R531R3D-D2.pdf)

Vi ved nu, at Arduinoen udsender 5V, Iled = 20mA (0,02A) og spændingsfaldet er maks. 2,2V.

Vi kan nu bruge tallene vi har fået fra datasheetet.

RØD: R = (5V – 2,2V) / 0,02A = 140 Ohm

GUL: R = (5V – 2,2V) / 0,02A = 140 Ohm

HVID: R = (5V – 3,6V) / 0,02A= 70 Ohm

Kode

Vi starter med at initialisere vores line sensors til pins, hhv. A2 og A4. Derefter laver vi nogle variabler, som vi efterfølgende vil bruge, til at gemme de læste sensorværdier. Vi laver to variabler til motorhastigheden, så hastigheden kan justeres fra et sted. Til sidst initialiserer vi vores fem LED’er til frem pins.

int pinSens1 = A2;
int pinSens2 = A4;
int sensReading1 = 0;
int sensReading2 = 0;
int motorSpeed = 80;
int turnSpeed = 80;
int lys1 = 2;
int lys2 = 4;
int lys3 = 5;
int lys4 = 6;
int lys5 = 7;

Derefter initialiserer vi motorerne i setup, hhv. motor A og motor B, og indstiller datahastigheden til 9600 bits per sekund (baud), til seriel dataoverførsel og initialiserer de fem LED’er til output.

void setup() {
  Serial.begin(9600);

pinMode(lys1, OUTPUT);
pinMode(lys2, OUTPUT);
pinMode(lys3, OUTPUT);
pinMode(lys4, OUTPUT);
pinMode(lys5, OUTPUT);


  //Setup Channel A (Left motor)
  pinMode(12, OUTPUT); //Initiates Motor Channel A pin
  pinMode(9, OUTPUT); //Initiates Brake Channel A pin
  //Setup Channel b (Left motor)
  pinMode (13, OUTPUT); //Initiates Motor Channel A pin
  pinMode(8, OUTPUT); //Initiates Brake Channel A pin

}

 Under loop kører vi vores finite state machine. Vi kalder en funktion, kører funktionen igennem og starter næste funktion, indtil alle funktioner er kørt igennem.

void loop()
{
  
  
  //Assignment A
  
  forward();
  pause2();
  forward2();
  pause2();
  forward();
  pause2();
  forward2();
  pause2();
  forward();
  pause2();
  right2();
  pause2();
  forward2();
  pause2();
  forward();
  pause2();
  forward2();
  pause2();
  forward();
  pause2();
  left2(); 
  pause2();
  forward2();
  pause2();
  forward();
  pause2();
  forward2();

  //Bilen er ved kassen
  
  pause();
  Uturn();
  pause2();
  forward();
  pause2();
  left2();
  pause2(); 
  forward2();
  pause2();  
  forward();
  pause2();
  forward2();
  pause2();
  forward2();
  pause2();
  forward();
  pause2();
  forward2();
  backward();
  pause2();
  Uturn2();
  pause();


  //Assignment B

  forward();
  pause2();
  forward2();
  pause2();
  forward();
  pause2();
  forward2();
  pause2();
  forward();
  pause2();
  forward2();
  right2();
  pause2();
  forward2();
  pause2();
  forward();
  pause2();
  forward2();
  //Bilen er ved Kasse B
  pause2();
  Uturn();
  pause2();
  forward();
  pause2();
  left2();
  pause2();
  forward2();
  pause2();
  forward();
  pause2();
  forward2();
  pause2();
  forward();
  pause2();
  right2();
  pause2();
  forward2();
  pause2();
  forward();
  pause2();
  left2();
  pause2();
  forward2();
  forward();
  pause2();
  backward();
 
}

I forward kører bilen ligeud og retter samtidigt vha. Line sensorerne, så den sorte linje følges. Forward er en recursive funktion. Der tændes for lys1 i starten af denne funktion, derefter læser vi værdierne fra line sensors. Derefter har vi nogle while statements.

Hvis værdien fra en sensor overstiger 900, betyder det at det er en sort linje på banen.

Det første while statement; hvis højre sensor rammer en sort linje og venstre sensor ikke gør, kører vi funktionen right.

Det andet while statement; hvis venstre sensor rammer en sort linje og højre sensor ikke gør, kører vi funktionen left.

Det tredje while statement; hvis begge sensorer ikke overstiger en værdi af 900, hvilket vil sige at vi ikke rammer nogle sorte linjer, kører vi ligeud.

Derefter har vi et if statement; hvis begge sensorer er over 900, betyder det, at begge sensore har ramt en sort linje, hvilket betyder, at vi har ramt et kryds. Hvis dette er tilfældet, slukkes der for lys1 og vi returnerer til loopet. Hvis dette ikke er tilfældet, kalder vi funktionen igen.

void forward()
{
  digitalWrite(lys1, HIGH);
  //Kører ligeud og retter med sensorene samtidigt.
  sensReading1 = analogRead(pinSens1);
  Serial.print("sensor 1: ");
  Serial.println(sensReading1);
  sensReading2 = analogRead(pinSens2);
  Serial.print("sensor 2: ");
  Serial.println(sensReading2);
  
  while(sensReading1>900 && sensReading2<900 ){right();
  sensReading1 = analogRead(pinSens1);
  sensReading2 = analogRead(pinSens2);}
    
  while (sensReading2>900 && sensReading1<900){left();
  sensReading1 = analogRead(pinSens1);
  sensReading2 = analogRead(pinSens2);}

  while (sensReading1<900 && sensReading2<900){
  digitalWrite(12, LOW); //Establishes forward direction of Channel A
  digitalWrite(9, HIGH);   //Disengage the Brake for Channel A
  analogWrite(3, motorSpeed);   //Spins the motor on Channel A
  digitalWrite(13, LOW); //Establishes forward direction of Channel B
  digitalWrite(8, HIGH);   //Disengage the Brake for Channel B
  analogWrite(11, motorSpeed);   //Spins the motor on Channel B*/
  sensReading1 = analogRead(pinSens1);
  sensReading2 = analogRead(pinSens2);}

if (sensReading1>900 && sensReading2>900){
  digitalWrite(lys1, LOW);
  return;
}
else {
  forward();
}
  delay(20);
}

I funktionen forward2, tænder vi for lys2 og får bilen til at køre, i 350 ms, hvorefter vi returnerer til loopet. Dette gøres, når bilen har ramt et kryds, for derved at køre over krydset.


void forward2()
{
  digitalWrite(lys2, HIGH);
  //kører ligeud kort uden at rette.
  digitalWrite(12, LOW); //Establishes forward direction of Channel A
  digitalWrite(9, HIGH);   //Disengage the Brake for Channel A
  analogWrite(3, motorSpeed);   //Spins the motor on Channel A

  digitalWrite(13, LOW); //Establishes forward direction of Channel B
  digitalWrite(8, HIGH);   //Disengage the Brake for Channel B
  analogWrite(11, motorSpeed);   //Spins the motor on Channel B*/
  delay(350);
  digitalWrite(lys2, LOW);
  return;
}

Funktionen right og left, bliver kaldet under while statementsne i forward. Left kører venstre motor, så bilen drejer til venstre og right kører højre motor, så bilen drejer til højre og derved ikke kører ind over den sorte linje, men altid retter, så den sorte linje følges i stedet.

void left()
{
  //Venstre rette
  Serial.println("correct left");
  //RIGHT @ half speed
  digitalWrite(12, HIGH); 
  digitalWrite(9, LOW);   
  analogWrite(3, turnSpeed);   

  digitalWrite(13, LOW); 
  digitalWrite(8, LOW);   
  analogWrite(11, turnSpeed);   
  delay(60);
  
}

void right()
{
  //Højre rette
  Serial.println("correct right");
  //LEFT @ half speed
  digitalWrite(12, LOW); 
  digitalWrite(9, LOW);   
  analogWrite(3, turnSpeed);    
  
  digitalWrite(13, HIGH); 
  digitalWrite(8, LOW);  
  analogWrite(11, turnSpeed);  
  delay(60);
}

I funktionen right2 tændes der for lys3, hvorefter bilen drejer til højre i 520 ms, hvilket svarer til en kvart omdrejning, med 8 volt. Når dette er sket, slukkes der for lys3, hvorefter vi returnerer til loopet.

I funktionen left2 tændes der for lys4, hvorefter bilen drejer til venstre i 650 ms, hvilket svarer til en kvart omdrejning, med 8 volt. Når dette er sket, slukkes der for lys4, hvorefter vi returnerer til loopet.

void right2()
{
  digitalWrite(lys3, HIGH);
  //Dreje ved et kryds
  Serial.println("right");
  //LEFT @ half speed
  digitalWrite(12, LOW); 
  digitalWrite(9, LOW);   
  analogWrite(3, turnSpeed);    
  
  digitalWrite(13, HIGH); 
  digitalWrite(8, LOW);  
  analogWrite(11, turnSpeed);  
  delay(520);
  digitalWrite(lys3, LOW);
  return;
}

void left2()
{
  digitalWrite(lys4, HIGH);
  //Dreje ved et kryds
  delay(500);
  Serial.println("correct left");
  //RIGHT @ half speed
  digitalWrite(12, HIGH); 
  digitalWrite(9, LOW);   
  analogWrite(3, turnSpeed);   

  digitalWrite(13, LOW); 
  digitalWrite(8, LOW);   
  analogWrite(11, turnSpeed);   
  delay(650);
  digitalWrite(lys4, LOW);
  
}

I funktionen Uturn tændes der for lys5, hvorefter bilen drejer til venstre i 2300 ms, med 8 volt, hvilket svarer til en halv omdrejning, med vægten af kassen tilføjet. Lys5 slukkes efterfølgende.

I funktionen Uturn2 tændes der for lys5, hvorefter bilen drejer til venstre i 160 ms, med 8 volt, hvilket svarer til en halv omdrejning. Lys5 slukkes efterfølgende.

void Uturn() {
//kassen er med, derfor skal den have mere kraft. 
    //RIGHT @ half speed
    digitalWrite(lys5, HIGH);
  digitalWrite(12, HIGH); 
  digitalWrite(9, LOW);   
  analogWrite(3,  turnSpeed + 40);   

  digitalWrite(13, LOW); 
  analogWrite(11, turnSpeed + 40);   
  delay(2300);
  digitalWrite(lys5, LOW);
}


void Uturn2() {
//Kassen er ikke med
 digitalWrite(lys5, HIGH);
    //RIGHT @ half speed
  digitalWrite(12, HIGH); 
  digitalWrite(9, LOW);   
  analogWrite(3,  turnSpeed);   

  digitalWrite(13, LOW); 
  analogWrite(11, turnSpeed);   
  delay(1600);
   digitalWrite(lys5, LOW);
}

I funktionen backward tændes der for lys2, lys3 og lys4 og motorerne roterer modsat retning, for at bakke. Derefter slukkes der for lys2, lys3 og lys 4.


void backward()
{
    digitalWrite(lys2, HIGH);
  digitalWrite(lys3, HIGH);
  digitalWrite(lys4, HIGH);
  
  digitalWrite(12, HIGH); //Establishes forward direction of Channel A
  digitalWrite(9, LOW);   //Disengage the Brake for Channel A
  analogWrite(3, motorSpeed);   //Spins the motor on Channel A

  digitalWrite(13, HIGH); //Establishes forward direction of Channel B
  digitalWrite(8, LOW);   //Disengage the Brake for Channel B
  analogWrite(11, motorSpeed);   //Spins the motor on Channel B*/
  delay(1000);

  digitalWrite(lys2, LOW);
  digitalWrite(lys3, LOW);
  digitalWrite(lys4, LOW);

  return;
}

I funktionen pause2, tændes der for alle led’er, hvorefter motorerne stopper i 400 ms og alle led’er slukkes igen. Dette er gjort, så der er en kort pause imellem hver sekvens, for at tydeliggøre funktionerne i vores finite state machine og derved også gøre det lettere at debugge.

void pause2()
{
  digitalWrite(lys1, HIGH);
  digitalWrite(lys2, HIGH);
  digitalWrite(lys3, HIGH);
  digitalWrite(lys4, HIGH);
  digitalWrite(lys5, HIGH);
  //kort pause
  //STOP
  digitalWrite(9, HIGH); //Eengage the Brake for Channel A
  digitalWrite(8, HIGH); //Eengage the Brake for Channel B
  analogWrite(11, 0);
  analogWrite(3, 0);
  
  delay(400);
  digitalWrite(lys1, LOW);
  digitalWrite(lys2, LOW);
  digitalWrite(lys3, LOW);
  digitalWrite(lys4, LOW);
  digitalWrite(lys5, LOW);
}

Problemer ift. software & hardware

Koden har ikke forsaget problemer, udover, motorhastigheden ændrede sig, da vi skiftede 9 volts batterier. De fleste problemer gruppen har haft, har været forsaget af hardwaren, primært sensorerne. I første version af bilen, skulle et af sensorerne udskiftes, da den ikke fungerede optimalt. I en lidt senere version af bilen, sad sensorerne ikke fast og målte derfor ukorrekte værdier.

Flow chart

Under loop kører vi vores finite state machine. Vi kalder en funktion, kører funktionen igennem og starter næste funktion, indtil alle funktioner er kørt igennem. Derfor peger alle pile tilbage til loop, hvorefter næste funktion vil køre.

Imellem hver funktion er der sat en kort pause funktion ind. Dette er gjort, så der er en kort pause imellem hver sekvens, for at tydeliggøre funktionerne i vores finite state machine og derved også gøre det lettere at debugge.

Perspektivering

I projektet er der gjort brug af delays i flere funktioner. Dette er ikke en optimal løsning ift. at løse opgaven, hhv. at hente og aflevere kasse a og b. Grundet tidspres og andre faktorer, såsom manglende loddekolbe, har gruppen valgt ikke at skille bilen ad. Da sensorerne og bilens opbygning skulle ændres, ville det betyde, at gruppen skulle starte forfra.

Et problem gruppen havde var, at et af fordækkene kunne ramme den sorte linje lige inden et kryds. Hvilket ville sætte enten left eller right funktionen i gang for at rette bilen. Dette gjorde så, at når bilen ramte krydset, at den havde en skæv indgangsvinkel og derfor ville køre over krydset ud til det hvide.

En løsning ville være at sætte de to sensorer tættere på hinanden. Derudover at ændre forward2 funktionen til at køre så længe sensoren så sort og stoppe, når den så hvidt. Dette ville gøre, at bilen ville køre over et kryds og stoppe, så snart den havde kørt over krydset. Sensorernes afstand forsagede også et problem ift. Dreje bilen. Hvis vi tager udgangspunkt i figuren herunder, ville det optimale være, at få bilen til at dreje (i dette eksempel), til højre, indtil venstre sensor (3), ramte hvidt og var ved punkt 5.

Uturn funktionen blev også udført med delays. Hvis vi tager udgangspunkt i figuren herunder. Her ville det igen være optimalt at gøre brug af sensorerne. Hvis venstre fordæk (1), drejede til venstre indtil den så hvidt igen (dvs. hvidt, sort, hvidt). Og højre fordæk (2) gjorde det samme, ville bilen dreje 180 grader og derved lave en mere præcis U-vending.

Konklusion

Afsluttende vil gruppen dog mene at selvom der kan arbejdes videre på opsætningen og kodeløsningen så er opgaven løst tilfredsstillende således at robotten kan transportere kasserne rundt på banen. Den følger ude mærket linjerne på banen og delfunktionerne kører i størstedelen af tilfældene upåklageligt.

Leave a Reply