Navn på robotten: UNDER_SCORE (Udtales: “Under underscore score”)

 

Udarbejdet af: Luca Casa, Tobias Jensen, Jamie Møller Christensen.

 

Formålet med robottens konstruktion

Robotten er en autonom bil, med sensorer der tillader den både at følge en sort streg under den, såvel som at foretage et højresving rundt om en væg. Den skal kunne køre igennem en bane bestående af en sådan sort streg og væg. Robotten har to lego-motorer, såvel som gearing og hjul dertil. Alle førnævnte komponenter benytter og styrer robottens arduino UNO. Robotten skal kunne køre igen

nem banen på minimum 3 minutter. Den klarer typisk banen på mellem 13 til 14 sekunder. Robotten er ydermere konstrueret i lego, hvis konstruktion vil blive vist i denne guide med illustrationer lavet i LEGO digital designer.

 

 

 

 

Hardware

 

 

Konstruktion af robotten

Opbygningen af lego-bilen: 

Download (ZIP, 15KB)

Sensorer

 

SparkFun Line Sensor Breakout – QRE1113

Denne sensor udsender infrarødt lys og giver en spænding i forhold til hvor meget infrarødt lys der reflektere tilbage. Refleksionen af lys afhænger af overfladen, som lyset kastes ind på. Dette gør den i stand til at detektere en sort linje, da stregen ikke vil reflektere lys tilbage. Sensoren skal sidde ret tæt på den overflade, hvor den skal afmåle (omkring 3-5 mm, men der henvises til databladet).

Datasheet: https://www.sparkfun.com/datasheets/Robotics/QR_QRE1113.GR.pdf

 

HC-SR04 Ultrasonic Range Finder

Komponenten kaldes også en Ultralydssensor, som kan bruges til at måle afstand. Sensoren udsender lydbølger frem i luften. Hvis lydbølgerne rammer et objekt, reflekteres lyden tilbage til sensoren, og den kan dermed udregne, hvor lang tid der er gået. Der skal dog lidt behandling til på software-delen:

  1. Tiden skal omregnes til afstand vha. konstanten om lydens hastighed.
  2. Man skal huske at man får afstanden for både frem og tilbage, og derfor yderligere skal behandle denne data ved at dividere resultatet med to jf. nedenstående eksempel:

Datasheet til ultrasonisk senor: https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf

 

Sensorernes placering og mekanikken

 

Til at følge linjen bruges to Line Sensor Breakout fra Sparkfun. De er placeret på en lego klods vha. elastikker, som spænder dem fast til klodsen. Denne klods er placeret på undersiden af robotten, og sidder fem millimeter fra underlaget, så den kommer så tæt på som muligt, for at få præcise målinger. Klodsen er placeret et par centimeter foran den akse, hvor hjulene med motorer er placeret, hvorved målingerne bliver foretaget før hjulene når frem til det punkt, hvor sensorerne læser. Derved kan sensor målingerne foretages og behandles før hjulene når at køre for langt. Oprindeligt var klodsen med sensorerne placeret forrest på robotten, men efter at have vendt robottens kørselsretning, så blev de placeret tættere mod centrum af robotten. Dette blev til dels gjort, så målingerne ikke blev foretaget på et område, som robotten havde passeret, men også for at give robotten et bedre sving. Hvis klodsen sad forrest stadig, så ville robottens udsving være hurtige og ekstreme i forhold til sensorerne, hvorved der ville måles oftere. For at få den hurtigste rute langs stregen, skal robotten køre lige så ofte som muligt, hvilket er nemmere at opnå, med sensorerne tættere på hjulene, hvor der er mindre og langsommere sving end foran. Hvis sensorerne var placeret lige under hjulene, så kunne robotten foretage målingerne for sent, hvilket ville gøre det sværere at rette den ind igen.

Robotten har to store hjul, hver sat til en motor bagerst på robotten. For bedre at kunne styre robotten er baghjulene blevet gearet. Gearingen er på 1:1,67 (eller 0,599), hvilket er opnået ved at bruge et tandhjul med 24 tænder, som sidder på motoren, som trækker et tandhjul med 40 tænder, som sidder fast til et hjul. Forrest er et lille hjul placeret på en roterbar lego-plade, således at dette hjul kan dreje med robotten.

 

 

Kredsløb

“U1” og “U2” er de to Line Sensor Breakout sensorer. “Hc-sr1” er den ultrasoniske sensor hc-sr04. “M1” og “M2” er motorerne. VCC1 er et 9V batteri, en batteripakke på 9V eller en strømforsyning sat til 9V.

 

 

Software

 

Robottens adfærd på den sorte linje

Der bruges to “Sparkfun Line Sensor”- sensorer til at orientere robotten på banens sorte linje. Disse sensorer læser med analogRead() i deres respektive pins. Når der læses et analog-signal giver dette en værdi, i formen af en int (heltal), der ligger i intervallet [0,1023]. Sensorerne på robotten læser typisk værdier omkring 30-60. Robotten fungerer så længe værdierne sensorerne læser er under 200 når robottens sensorer er over den hvide del af banen. De to sensorers værdier bliver læst og lagt i variablerne rightValue og leftValue, hvor sensoren på den højre side af robotten aflæses og lagres i rightValue variablen. Værdierne af leftValue og rightValue bliver sammenlignet med variablet “sort”’s værdi, som er sat til 600. Denne værdi benyttes til at vurdere om sensorerne er over den sorte streg eller ej. Når sensorerne er over den sorte streg læser de typisk over 800. “sort” er derfor sat til 600, sådan at små vibrationer eller variationer i afstanden fra jorden til sensorerne ikke påvirker robottens opførsel når den følger linjen.

Når begge sensorer har en værdi mindre end “sort”, kører robotten ligeud. Dette sker ved at funktionen driveForward() kaldes, i denne sættes retningen på motorerne til at køre frem, og pwm-signalet (fra [0,255])  motorerne modtager bliver givet. Størrelsen af pwm-signalerne givet til motorerne er proportionale med robottens hastighed. Når robotten starter med at køre er størrelsen på disse signaler givet med “fremR” og “fremL”,  disse har værdien 125 til at starte med. Når robotten så ender med den ene sensor over den sorte streg, kaldes den respektive retnings funktion, enten turnLeft() eller turnRight(). Funktionerne turnRight() og turnLeft() er inverse, de gør altså det samme, bare med alle retninger sat modsat. Den ene motor i disse dreje-funktioner sættes i “bakgear” ved at ændre på strømretningen, dette sker ved at sende et digitalt signal (LOW eller HIGH), hvor HIGH er frem og LOW er baglæns (dette kan dog variere alt efter hvordan kredsløbet er sammensat). Således drejer robotten på stedet, indtil begge sensorer igen er over den hvide del af banen, med den sorte streg imellem sig. Robotten har haft en tendens til at dreje over den sorte streg, typisk når den kører fremad med en høj hastighed, denne kan nedjusteres efter behov, hvis strømkilden ikke kan give de 8.7V koden er egnet til. Når en af disse drejefunktioner bliver kaldt, justeres “fremR” og “fremL” sådan at driveForward() kører en smule skævt mod den retning der sidst blev drejet i. Den justering har det formål at undgå alt for stærke indfaldsvinkler på den sorte linje, samt at det giver blødere sving hvor linjen drejer.

 

Robottens adfærd efter linjen

Når begge Line Sensorer måler sort (værdi større end 600) ændres en bool (ved navn followWall) fra false til true, og robotten skifter til at følge væggen. Først starter den med at køre frem og dreje, dette ved kald til driveForward() og turnLeft() med delay()-kald imellem hver funktion. Disse forsinkelser givet i millisekunder er tilpasset en spænding på ca. 8.73 V, da større og mindre spænding giver motorerne mere eller mindre kraft. Derfor bør de justeres efter behov. Formålet med dette er at robottens højre side, hvor ultralydssensoren HC-SR04 sidder, får en vinkel til væggen som giver brugbare værdier. Alt efter hvor langt ultralydssensoren  er fra væggen kører robotten efter samme princip som ved den sorte linje, enten til højre eller venstre, og ellers ligeud. Dette gøres ved funktionerne turnWLeft(), turnWRight() og driveWForward(). driveWForward svinger reelt set meget til højre, sådan at robotten har en tendens til oftest at køre tættere på væggen. Robotten holder sig altså inden for en vis afstand fra væggen, når den kommer inden for 12 cm for væggen drejer den væk fra væggen. Når robotten er længere end 18 cm fra væggen, drejer den til højre. Dette resulterer i at robotten, så snart væggen stopper, begynder at dreje til højre, og derefter følger væggen med en afstand ca. mellem 12-18 cm.

 

Ved mållinjen

Når robotten når målstregen, lyser LED´erne på robotten op således at de repræsenterer tiden det tog robotten at nå i mål binært. Sådan kan der tages tid på hvor lang tid det tager fra robotten starter med at køre, til den når målstregen. Det skal dog bemærkes at dette måles i integers (heltal) og disse altid rundes ned til nærmeste hele tal ved division, derfor er en tid på 13.9 vist som 13 med LED´erne.

 

Kode

//NEEDS 8.7 VOLTS THROUGH WIRES FROM A POWERSUPPLY TO WORK + DEJAVU.
//On-Off button:
const int onOffButtonPin = 4;


//--------------


//For ultrasound sensor:
const int trigPin = 10;           //pwm pin #10 for trig
const int echoPin = A5;           //Analog5 pin for echo
const float speedOfSound = 0.034;
int sensorInput;
int distance;

//-----------------

//Millis for timing:
uint32_t timingMillis = 0;
uint32_t previousMillis = 0;
uint32_t currentMillis;

//Millis for avoiding wallbanging:
uint64_t previousWMillis  = 0;
uint64_t currentWMillis   = 0;

bool stopDrive = false;
bool followWall = false;
bool firstWall = false;
bool cycleWallMillis = false;

//For stopping the robot when it finishes
bool kill = false;
bool leftFirst = false;

int plusUltraVals[10];
uint64_t myCounter = 0;
int ultraAvg = 0;

//Left = B on the motorshield
const int motorLeft = 13;

const int brakeLeft = 8;

const int pwmLeft = 11;


//Right = A on the motorshield
const int motorRight = 12;
const int brakeRight = 9;
const int pwmRight = 3;

const int analogPinRight = 0; //
const int analogPinLeft = 1;

const int led1 = 2;
const int led2 = 4;
const int led4 = 5;
const int led8 = 6;
const int led16 = 7;

float rightValue = 0; //We write the analogPin value to this value
float leftValue = 0;  //We write the analogPin value to this value

//Med 1/16 gearing:
//Rekord med 93/155/155 = 10 sek. (heldigt, gennemsnit = 12)
int fremR = 125;  //255 //45 //Last working: 95 (100 er for meget) - 94+turnAdjust på 5 => 10 sekunder. (turnAdjust = 4 => 9 sekunder, meget heldigt)
int fremL = 125;
int turnAdjust = 30;  //+- values for fremR/fremL when turning. 30 => 11 seconds. 5 => 10|11|crash|11|10 sekunder-
int drej = 155 + 100; //255 //45+0      //Last working: 155 :
int pwmBrake = 155 + 100; //255 //85+0  //Last working: 150 (145 har givet: 11 / 11 / )

//115 virker, 120 gør også, todo: prøv 125, se om drej og PWM skal være højere eller er passende.
//125frem og 155+80drej giver 9 sekunder, men failer sving 2 hvis den er uheldig. turnAdjust 30 virker fint.

int sort = 600; //

void setup() {
  // put your setup code here, to run once:
  pinMode(motorLeft, OUTPUT);
  pinMode(motorRight, OUTPUT);

  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);

  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(led4, OUTPUT);
  pinMode(led8, OUTPUT);
  pinMode(led16, OUTPUT);

  Serial.begin(9600);
  Serial.println("Starting: ");

  digitalWrite(brakeRight, LOW);
  digitalWrite(brakeLeft, LOW);

  previousMillis = millis();
}

void loop() {

  if (!followWall) {
    //Line-sensor reading:
    leftValue = analogRead(analogPinLeft);    //Read the line-sensor value.
    rightValue = analogRead(analogPinRight);  //Same.

    /*
      Serial.print("leftPin: ");
      Serial.println(leftValue);
      Serial.print("rightPin: ");
      Serial.println(rightValue);
    */

    //!stopDrive used normally in this if-statement.
    if (!stopDrive) {
      //Driving:
      if (leftValue < sort && rightValue < sort) {
        driveForward();
      } else if (leftValue > sort && rightValue > sort && !stopDrive) {
        stopDrive = true;
        followWall = true;
        //currentMillis = millis(); - used this to time the black line sprint.
        //timingMillis = currentMillis - previousMillis;
      } else if (leftValue > sort) {
        turnLeft();
        leftFirst = true;
      } else if (rightValue > sort) {
        turnRight();
        leftFirst = false;
      }

      if (stopDrive) {
        /*stopDriving();

          timingMillis = timingMillis / 1000;
          timingMillis = constrain(timingMillis, 0, 32);

          if (timingMillis >= 16) {
          digitalWrite(led16, HIGH);
          timingMillis -= 16;
          }
          if (timingMillis >= 8) {
          digitalWrite(led8, HIGH);
          timingMillis -= 8;
          }
          if (timingMillis >= 4) {
          digitalWrite(led4, HIGH);
          timingMillis -= 4;
          }
          if (timingMillis >= 2) {
          digitalWrite(led2, HIGH);
          timingMillis -= 2;
          }
          if (timingMillis >= 1) {
          digitalWrite(led1, HIGH);
          timingMillis -= 1;
          }
        */
      }
    }
  }
  if (!kill) {
    if (followWall) {     //Trigpin is the yellow wire. //Use "followWall" for bool here.
      if (!firstWall) {
        stopDriving();
        delay(50);
        fremR = 125;
        fremL = 125;
        driveForward();
        delay(300 + 45 + 10);
        turnLeft();
        delay(200 + 25);       //This makes it turn left in preparation for the wall.
        if(leftFirst){
          delay(60);
        }
        
        driveWForward();
        firstWall = true;
      }
      //kode der læser på ultralyds - virker men det kan volde problemer at vi bruger delay() ikke millis()
      digitalWrite(trigPin, LOW);
      delayMicroseconds(2);
      digitalWrite(trigPin, HIGH);
      delayMicroseconds(10);
      digitalWrite(trigPin, LOW);
      sensorInput = pulseIn(echoPin, HIGH);
      distance = (sensorInput / 2) / 29.1;
      //distance = (sensorInput * 1/29.1) /2
      plusUltraVals[myCounter % 10] = constrain(distance, 0, 25);
      myCounter++;

      ultraAvg = 0;
      for (int i = 0; i < 10; i++) {
        ultraAvg += plusUltraVals[i];
        if (i == 9) {
          ultraAvg /= 10;
        }
      }
      //Serial.print(distance);
      //Serial.println(" cm");
      delay(1);

      //Use ultraAvg instead of puny distance.

      if (firstWall && ultraAvg > 18) {
        turnWRight();
      } else if (firstWall && ultraAvg < 12 && ultraAvg > 0)  {
        turnWLeft();
      } else {
        driveWForward();
      }

      //Trying to time the way around the track:
      leftValue = analogRead(analogPinLeft);    //Read the line-sensor value.
      rightValue = analogRead(analogPinRight);
      if (leftValue > sort && rightValue > sort) {
        currentMillis = millis();
        timingMillis = currentMillis - previousMillis;


        timingMillis = timingMillis / 1000;
        timingMillis = constrain(timingMillis, 0, 32);

        if (timingMillis >= 16) {
          digitalWrite(led16, HIGH);
          timingMillis -= 16;
        }
        if (timingMillis >= 8) {
          digitalWrite(led8, HIGH);
          timingMillis -= 8;
        }
        if (timingMillis >= 4) {
          digitalWrite(led4, HIGH);
          timingMillis -= 4;
        }
        if (timingMillis >= 2) {
          digitalWrite(led2, HIGH);
          timingMillis -= 2;
        }
        if (timingMillis >= 1) {
          digitalWrite(led1, HIGH);
          timingMillis -= 1;
        }

        stopDriving();
        kill = true;
      }

      // divide by two to get the "correct" distance
      // that is; from sensor to object
      //distance = (sensorInput * speedOfSound)/2;
      //Serial.print("sensorInput value: ");
      //Serial.println(sensorInput);
      // put your main code here, to run repeatedly:
    }



  } else {
    stopDriving();
    //DEJAVU(); //Sejrsdans!
  }
}


void DEJAVU(){
    //I've just been in this place before!
    turnRight();
    delay(500);
    //(Higher on the street!) 
    turnLeft();
    delay(500);
    //And I know it's my time to come home!
    turnLeft();
    delay(280);
    //Calling you! 
    //And the subject's a mystery! 
    //(Standing on my feet!) 
    fremR = 255;
    fremL= 255;
    driveForward();
    delay(500);
    //It's so hard when I try to believe! 
    //Whooooaaaaaaaaaaaaaaa!!
    //DEJAVU
    DEJAVU();
    
}

void turnLeft() { //This was originally turnRight, might need to be changed back.
  digitalWrite(motorLeft, HIGH);

  analogWrite(pwmLeft, drej);

  digitalWrite(motorRight, LOW);    //Change direction of motor
  analogWrite(pwmRight, pwmBrake);  //Add a slight force in the opposite direction, such that it turns faster.

  fremR = 125 + turnAdjust;
  fremL = 125 - turnAdjust;
}
void turnRight() {  //This was originally turnLeft, might need to be changed back.
  digitalWrite(motorRight, HIGH);

  analogWrite(pwmRight, drej);      //Speed of the motor

  digitalWrite(motorLeft, LOW);     //Change direction of motor
  analogWrite(pwmLeft, pwmBrake);   //Add a slight force in the opposite direction, such that it turns faster.

  fremL = 125 + turnAdjust;
  fremR = 125 - turnAdjust;
}
void driveForward() {
  digitalWrite(motorRight, HIGH);
  digitalWrite(motorLeft, HIGH);

  analogWrite(pwmLeft, fremL);
  analogWrite(pwmRight, fremR);
}
void stopDriving() {
  //digitalWrite(brakeRight, HIGH);
  //digitalWrite(brakeLeft, HIGH);

  analogWrite(pwmRight, 0);
  analogWrite(pwmLeft, 0);
}


void turnWLeft() {
  digitalWrite(motorLeft, HIGH);

  analogWrite(pwmLeft, 180 + 18*4);

  digitalWrite(motorRight, HIGH);    //Change direction of motor
  analogWrite(pwmRight, 0);  //Add a slight force in the opposite direction, such that it turns faster.


}

void turnWRight() {
  digitalWrite(motorRight, HIGH);

  analogWrite(pwmRight, 180 + 18*4);      //Speed of the motor

  digitalWrite(motorLeft, HIGH);     //Change direction of motor
  analogWrite(pwmLeft, 0);   //Add a slight force in the opposite direction, such that it turns faster.


}

void driveWForward() {
  digitalWrite(motorRight, HIGH);
  digitalWrite(motorLeft, HIGH);

  analogWrite(pwmRight, 110); //110
  analogWrite(pwmLeft, 5); //5

}


 

Udfordringer under udviklingen af UNDER_SCORE

Robotten er en smule skrøbelig, hvilket har forårsaget inkonsistente resultater alt andet værende lige. Dele er faldet af robotten i løbet af tests, hvorefter de er blevet sat på igen forkerte steder. Blandt andet har robottens forhjuls placering en stor indflydelse på hvordan robotten kører, så da dette blev sat forkert på igen, påvirkede det robotten betydeligt. Det samme skete med baghjulene og de forskellige sensorer. Derudover har batterierne vi brugte som strømkilde undervejs forårsaget variation i robottens opførsel, da spændingen disse leverer falder som de aflades, derfor brugte vi en strømforsyning for at holde spændingen konstant. At få robotten til at følge væggen med kun én ultralydssensor var også en udfordring, da det er svært at vide hvad robottens orientering til væggen er, når robotten drejer. Hvis gruppen havde to ultralydssensorer til rådighed ville dette ikke være et problem, da de to ultralydssensorers afstand til væggen kunne holdes mod hinanden.

Serial-print funktionen i arduino’en har været grund til at robotten ikke kunne nå at reagere på, at den var kørt over den sorte streg. Dette resulterede i at den, typisk i svingende, ikke kunne følge linjen da den bare kørte over dem, eller ikke drejede skarpt nok. Serial-print blev brugt til at teste sensorværdier, men de giver samtidig en lille forsinkelse i programmets eksekvering, derfor blev robotten langt mere responsiv da disse blev kommenteret ud af programmet.

 

Konklusion

 

UNDER_SCORE er en autonom robot, der gennemfører banen inden for opgavens tidsramme på 3 minutter. Til konkurrencen klarede robotten banen på 13.3 sekunder, og fik en førsteplads på trods af sporadisk besvær med at dreje væk fra muren. Robottens bedste tid er på 12 sekunder. Dette er muliggjort ved brug af de to Line Sensors samt en ultralydssensor.

 

 

Videoer af vinderrobotten UNDER_SCORE:

Leave a Reply