Industriële fabricage
Industrieel internet der dingen | Industriële materialen | Onderhoud en reparatie van apparatuur | Industriële programmering |
home  MfgRobots >> Industriële fabricage >  >> Manufacturing Technology >> Productieproces

Maze Solver Robot, met behulp van kunstmatige intelligentie

Componenten en benodigdheden

Arduino Nano R3
× 1
SparkFun RedBot-sensor - lijnvolger
× 1
ZX03 (gebaseerd op TCRT5000) Reflecterende infraroodsensoren (analoge output)
× 2
Android-apparaat
× 1
RobotGeek continue rotatieservo
× 2
4xAA-batterijhouder
× 2

Apps en online services

Arduino IDE
MIT App Inventor 2

Over dit project

Inleiding

Deze tutorial is ontwikkeld op mijn laatste project:Line Follower Robot - PID Control - Android Setup. Als je eenmaal een robot hebt met lijnvolgmogelijkheden, is de volgende natuurlijke stap hem een ​​zekere mate van intelligentie te geven. Dus onze lieve "Rex, de robot" zal nu proberen op de kortste en snelste manier uit een "labyrint" te ontsnappen (hij heeft trouwens een hekel aan de Minotaurus.

Om te beginnen, wat is het verschil tussen Maze en Labyrinth ? Volgens http://www.labyrinthos.net wordt in de Engelstalige wereld vaak gedacht dat een ontwerp, om als doolhof te worden gekwalificeerd, keuzes moet hebben in het traject. Het is duidelijk dat dit veel van de moderne installaties in amusementsparken en toeristische attracties omvat, waaronder ons 2D-doolhof hier. Populaire consensus geeft ook aan dat labyrinten één pad hebben dat onverbiddelijk van de ingang naar het doel leidt, zij het vaak via de meest complexe en bochtige routes.

De meeste doolhoven, hoe complex hun ontwerp ook mag lijken, werden in wezen gevormd uit één doorlopende muur met veel knooppunten en vertakkingen. Als de muur rond het doel van een doolhof is verbonden met de omtrek van het doolhof bij de ingang, kan het doolhof altijd worden opgelost door één hand in contact te houden met de muur, hoeveel omwegen ook. Die 'eenvoudige' doolhoven staan ​​terecht bekend als "Simply-connected " of "perfect doolhof " of met andere woorden, die geen loops bevatten .

Terugkerend naar ons project, zal het in twee delen worden gesplitst (of "passen "):

  • (Eerste pas) :De robot vindt zijn weg naar buiten uit een "onbekend perfect doolhof ". Het maakt niet uit waar je het in het doolhof plaatst, het zal een "oplossing vinden ".
  • (Tweede pas) :zodra de robot een mogelijke doolhofoplossing heeft gevonden, moet hij zijn oplossing optimaliseren door het "kortste pad van begin tot eind te vinden" ".

De video hieronder laat een voorbeeld zien van Rex die zijn weg naar buiten vindt. In de eerste keer dat de robot het doolhof verkent, kost het natuurlijk veel tijd "denken " over wat te doen op elk kruispunt. Door de talloze mogelijkheden te testen, zal het verschillende verkeerde paden en doodlopende wegen nemen, wat hem dwingt langere paden te lopen en onnodige "U-Bochten uit te voeren ". Tijdens deze "1st Pass" , zal de robot ervaringen verzamelen, "aantekeningen maken " over de verschillende kruispunten en het elimineren van de slechte takken. In zijn "2nd Pass ", de robot gaat recht en snel naar het einde zonder enige fout of twijfel. Tijdens deze tutorial zullen we in detail onderzoeken hoe dit te doen:

Stap 1:​Bill of Materials

De lijst met materialen is in principe hetzelfde als degene die wordt gebruikt met de Line Follower Robot, behalve dat ik 2 extra sensoren heb toegevoegd voor een betere nauwkeurigheid bij het detecteren van de LINKER- en RECHTS-kruisingen:

De uiteindelijke robot is nog steeds erg goedkoop (ongeveer $ 85,00):

Lichaam (u kunt het aanpassen aan uw behoeften of beschikbare materialen):

  • 2 X Houten vierkanten (80X80mm)
  • 3 X bindmiddelclips
  • 2 X Houten wielen (diameter:50 mm)
  • 1 X Ballcaster
  • 9 X elastische banden
  • 3M Commandolijststrook
  • Kunststof verbindingen voor sensorbevestiging
  • BreadBoard en bedrading
  • 2 X sets van 4XNi-metaalhydridebatterijen (5V elke set)
  • 2 X SM-S4303R continue rotatie 360 ​​graden plastic servo
  • Arduino Nano
  • HC-06 Bluetooth-module
  • 5 X-lijnsensoren (TCRT5000 4CH infrarood lijnvolgvolgersensormodule + 1 onafhankelijke spoorsensor)
  • 2 X ZX03 (gebaseerd op TCRT5000) Reflecterende infraroodsensoren (analoge uitgang)
  • 1 LED
  • 1 knop

Opmerking :Ik heb item 7 hierboven gebruikt met analoge uitgang, omdat ik geen handsensoren met digitale uitgang had zoals die op item 6. Het ideaal is om alle sensoren gelijk te hebben, indien mogelijk. Ook heb ik het project getest met alleen de originele 5 sensoren. Het zal werken, maar vereist meer gevoelige aanpassingen bij het ontdekken van kruispunten.

Stap 2:Veranderingen in het lichaam

Verwijder de originele set van 5 lijnvolgsensoren en repareer de nieuwe "Far LEFT" en "Extreem RECHTS " reflecterende sensoren aan elk uiteinde van de ondersteunende plastic balk. Het is aan te raden om de 7 sensoren zo gelijnd mogelijk te hebben.

Stap 3:Installeren en testen van de nieuwe sensoren

De nieuwe reeks van nu 7 sensoren , is zo gemonteerd dat de 5 originele uitsluitend worden gebruikt voor PID-regeling (en detectie van de "volledige lijn", later uitgelegd) en de 2 nieuwe, links om uitsluitend te worden gebruikt voor detectie van kruispunten LINKS en RECHTS.

Laten we als snel overzicht onthouden hoe de 5 originele "digitale" sensoren werken:

Als één sensor gecentreerd is ten opzichte van de zwarte lijn, zal alleen die specifieke sensor een HOOG produceren. Aan de andere kant moet de ruimte tussen sensoren zo worden berekend dat 2 sensoren tegelijkertijd de volledige breedte van de zwarte lijn kunnen bestrijken, waardoor ook een HOOG signaal op beide sensoren wordt geproduceerd.

Hoe de 2 nieuwe "analoge" sensoren werken:

Als een van de sensoren gecentreerd is ten opzichte van de zwarte lijn, zal de output een analoge waarde zijn, die gewoonlijk een output produceert op Arduino ADC balg "100" (onthoud dat de ADC een output produceert van 0 tot 1023). Met lichtere oppervlakken zal de uitvoerwaarde hoger zijn (ik heb bijvoorbeeld 500 tot 600 op wit papier getest). Deze waarde moet worden getest op verschillende situaties van licht en oppervlaktematerialen om de juiste THRESHOLD-constante te definiëren die in uw geval moet worden gebruikt (zie de afbeelding hier).

Kijkend naar de Arduino-code, zal elk van de sensoren worden gedefinieerd met een specifieke naam (denk eraan dat de originele lijnvolgsensor meer naar links moet worden toegewezen met een label "0 "):

const int lineFollowSensor0 =12; // Digitale inputconst gebruiken int lineFollowSensor1 =18; // Analoge pin A4 gebruiken als digitale invoerconst int lineFollowSensor2 =17; // Analoge pin A3 gebruiken als digitale invoerconst int lineFollowSensor3 =16; // Analoge pin A2 gebruiken als digitale invoerconst int lineFollowSensor4 =19; // Analoge pin A5 gebruiken als digitale invoerconst int farRightSensorPin =0; // Analoge pin A0const int farLeftSensorPin =1; //Analoge pin A1 

Om te onthouden, de mogelijke 5 originele sensorarray-uitvoer bij het volgen van een lijn zijn:

1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 0 

Met de toevoeging van de 2 nieuwe, zijn hun mogelijke outputs:

  • Veruit LINKS-sensor:analoge uitgang groter of lager dan een THRESHOLD
  • Uiterst RECHTS-sensor:analoge uitgang groter of lager dan een THRESHOLD

Om de waarden van elke sensor op te slaan, wordt een arrayvariabele gemaakt voor de oorspronkelijke 5 digitale sensoren:

int LFSensor[5]={0, 0, 0, 0, 0}; 

En twee integer-variabelen voor de 2 nieuwe analoge sensoren:

int farRightSensor =0;int farLeftSensor =0; 

Elke positie van de array en variabelen wordt constant bijgewerkt met de output van elk van de sensoren:

LFSensor[0] =digitalRead(lineFollowSensor0);LFSensor[1] =digitalRead(lineFollowSensor1);LFSensor[2] =digitalRead(lineFollowSensor2);LFSensor[3] =digitalRead(lineFollowSensor3);LFSensor[4] =digitalRead(lineFollowSensor4);farRightSensor =analoogRead(farRightSensorPin);farLeftSensor =analoogRead(farLeftSensorPin); 

Met 5 sensoren, zoals gezien in het Follower Line Robot-project, kan een "foutvariabele" worden gegenereerd die helpt om de positie van de robot over de lijn te regelen. Er wordt ook een variabele met de naam "mode" gebruikt voor definitie als de robot een lijn volgt , over een doorlopende lijn , een kruispunt of geen lijn helemaal niet.

Deze variabele "mode " wordt ook gebruikt met de "Uiterst LINKS/RECHTS " sensoren. Laten we voor de weergave eens kijken naar de uiterst linkse en rechtse sensoren met 3 mogelijke toestanden:

  • H (hoger dan THRESHOLD),
  • L (kleiner dan THRESHOLD) en
  • X (niet relevant).

Voor de digitale uitgangen zal de gebruikelijke 0, 1 zijn en zullen we ook de X introduceren:

  • H 0 X X X X L ==> modus =RIGHT_TURN; fout =0; (zie het voorbeeld bij de afbeelding hierboven)
  • L X X X X 0 H ==> modus =LEFT_TURN; fout =0;
  • X 0 0 0 0 0 X ==> modus =NO_LINE; fout =0;
  • H 0 0 0 0 1 H ==> modus =VOLGENDE_LINE; fout =4;
  • H 0 0 0 1 1 H ==> modus =VOLGENDE_LINE; fout =3;
  • H 0 0 0 1 0 H ==> modus =VOLGENDE_LINE; fout =2;
  • H 0 0 1 1 0 H ==> modus =VOLGENDE_LINE; fout =1;
  • H 0 0 1 0 0 H ==> modus =VOLGENDE_LINE; fout =0;
  • H 0 1 1 0 0 H ==> modus =VOLGENDE_LINE; fout =-1;
  • H 0 1 0 0 0 H ==> modus =VOLGENDE_LINE; fout =-2
  • H 1 1 0 0 0 H ==> modus =VOLGENDE_LINE; fout =-3;
  • H 1 0 0 0 0 H ==> modus =VOLGENDE_LINE; fout =-4;
  • X 1 1 1 1 1 X ==> modus =CONT_LINE; fout =0;

Dus, de bovenstaande logica implementeren in de functie:

void readLFSsensors() 

retourneert de variabelen "mode " en "fout " die zal worden gebruikt bij de programmalogica. Het is belangrijk om de logica van de sensoren te testen voordat u met het project doorgaat. De balgfunctie is opgenomen in de code en kan worden gebruikt voor testdoeleinden:

void testSensorLogic(void) { Serial.print (farLeftSensor); Serial.print (" <==LINKS RECHTS==> "); Serial.print (farRightSensor); Serial.print ("modus:"); Serieafdruk (modus); Serial.print ("fout:"); Serial.println (fout);} 

Stap 4:Het doolhof oplossen - de regel voor de linkerhand

Zoals in de inleiding werd besproken, werden de meeste doolhoven, hoe complex hun ontwerp ook mag lijken, in wezen gevormd uit één doorlopende muur met veel knooppunten en vertakkingen. Als de muur rond het doel van een doolhof is verbonden met de omtrek van het doolhof bij de ingang, kan het doolhof altijd worden opgelost door één hand in contact te houden met de muur, hoeveel omwegen ook. Deze 'eenvoudige' doolhoven staan ​​terecht bekend als "Simply-connected ."

Als we op Wikipedia zoeken, leren we dat:

Kortom, de Linkerhandregel kan worden beschreven als:

Houd bij elke kruising en in het doolhof je linkerhand tegen de muur aan je linkerhand.

  • Plaats je linkerhand op de muur.
  • Begin vooruit te lopen
  • Uiteindelijk bereik je het einde van het doolhof. Je gaat waarschijnlijk niet de kortste en meest directe weg, maar je komt er wel.

De sleutel hier is dus om de kruispunten te identificeren , bepalen welke cursus te volgen op basis van de bovenstaande regels. Specifiek in ons soort 2D-doolhof kunnen we 8 verschillende soorten kruispunten vinden (zie de eerste afbeelding hierboven):

Als we naar de foto kijken, kunnen we ons realiseren dat de mogelijke acties op kruispunten zijn:

1. Bij een "Kruis ":

  • Ga naar links, of
  • Ga naar rechts, of
  • Ga rechtdoor

2. Bij een "T ":

  • Ga naar links, of
  • Ga naar rechts

3. Bij een "Alleen Rechts ":

  • Ga naar rechts

4. Bij een "Alleen links ":

  • Ga naar links

5. Bij "Recht of Links ":

  • Ga naar links, of
  • Ga rechtdoor

6. Bij "Recht of Rechts ":

  • Ga naar rechts, of
  • Ga rechtdoor

7. Op een "Doodlopende weg ":

  • Ga terug ("U draait")

8. Bij "Einde van het doolhof ":

  • Stop

Maar door de "Linkerhandregel" toe te passen, worden de acties teruggebracht tot elk één optie:

  • Bij een "kruis":ga naar links
  • Bij een "T":Ga naar links
  • Bij een "Alleen Rechts":Ga naar Rechts
  • Bij een "Alleen Links":Ga naar Links
  • Bij een "Recht of Links":Ga naar Links
  • Bij een "recht of rechts":ga rechtdoor
  • Bij een "doodlopende weg":ga terug ("U-bocht")
  • Aan het "Einde van het doolhof":stop

We zijn er bijna! "Wees kalm!"

Wanneer de robot een "doodlopend einde" of het "einde van een doolhof" bereikt, is het gemakkelijk om ze te identificeren, omdat er geen dubbelzinnige situaties zijn (we hebben die acties al geïmplementeerd op de lijnvolgerrobot, weet je nog?). Het probleem is wanneer de robot bijvoorbeeld een "LIJN" vindt, omdat een lijn een "Kruis" (1) of een "T" (2) kan zijn. Ook wanneer het een "LINKS of RECHTS TURN" bereikt, kunnen dat een simpele bocht zijn (optie 3 of 4) of opties om rechtdoor te gaan (5 of 6). Om precies te ontdekken op welk type kruising de robot zich bevindt, moet een extra stap worden genomen:de robot moet een "extra inch" rennen en zien wat de volgende stap is (zie de tweede afbeelding hierboven, als voorbeeld).

Dus, in termen van stroom, kunnen de mogelijke acties nu worden beschreven als:

1. Op een "DEAD END":

  • Ga terug ("U draait")

2. Bij een "LINE":Run een extra inch

  • Als er een regel is:het is een "Kruis" ==> Ga naar LINKS
  • Als er geen regel is:het is een "T" ==> Ga naar LINKS
  • Als er nog een regel is:het is de "End of Maze" ==> STOP

3. Bij een "RECHTS TURN":Run een extra inch

  • als er een lijn is:Het is een Recht of Rechts ==> Ga STRAIGHT
  • Als er geen regel is:het is alleen een Rechts ==> Ga naar RECHTS

4. Bij een "LEFT TURN":Run een extra inch

  • als er een lijn is:Het is een Rechte of LEFT ==> Ga naar LINKS
  • Als er geen regel is:het is alleen een LEFT ==> Ga naar LINKS

Merk op dat in het geval van een "LEFT TURN", u de test kunt overslaan, omdat u toch LINKS gaat. Ik heb de uitleg alleen voor de duidelijkheid algemener gelaten. Bij de echte code sla ik deze test over. De bovenstaande 3e foto toont een heel eenvoudig doolhof op mijn laboratoriumvloer, gebruikt voor testdoeleinden.

Stap 5:Implementatie van het "Left Hand on the Wall"-algoritme bij Arduino Code

Zodra we de readLFSsensors() . hebben functie aangepast om de extra 2 sensoren op te nemen, kunnen we de lusfunctie herschrijven om het algoritme uit te voeren zoals beschreven in de laatste stap:

void loop(){ readLFSsensors(); schakelaar (modus) {geval NO_LINE:motorStop(); goAndTurn (LINKS, 180); pauze; geval CONT_LINE:runExtraInch(); leesLFSsensoren(); if (modus ==CONT_LINE) mazeEnd(); anders goAndTurn (LINKS, 90); // of het is een "T" of "Kruis"). In beide gevallen gaat naar LINKS breken; geval RIGHT_TURN:runExtraInch(); leesLFSsensoren(); if (mode ==NO_LINE) goAndTurn (RECHTS, 90); pauze; case LEFT_TURN:goAndTurn (LEFT, 90); pauze; case FOLLOWING_LINE:volgendeLine(); pauze; }} 

Hier verschijnen enkele nieuwe functies:

  • followingLine() is hetzelfde dat wordt gebruikt met de volgende lijnrobot, waar, als het alleen een lijn volgt, het berekenPID() moet ; en regel de motoren afhankelijk van de PID-waarden:motorPIDcontrol();
  • runExtraInch(): duwt de robot een klein beetje naar voren. Hoeveel de robot zal draaien, hangt af van de tijd die u in de vertragingsfunctie gebruikt, voordat u de motor opdracht geeft om te stoppen.
void runExtraInch(void){ motorPIDcontrol(); vertraging (extra inch); motorStop();} 
  • goAndTurn (richting, hoek): deze speciale functie is belangrijk omdat je de robot niet kunt draaien zodra je je realiseert wat voor soort kruising je bent. Onthoud dat we een differentiële robot hebben geprojecteerd die bij het maken van bochten "om zijn as draait" en dus, om 90o te bewegen en continu de lijn te volgen, het midden van de wielen moet worden uitgelijnd met het midden van de kruising. Zodra de rij sensoren voor zijn as is, moet je de robot naar voren laten rennen om ze uit te lijnen. De constante van tijd adjGoAndTurn moet worden aangepast afhankelijk van de afstand tussen bijl en sensorlijn ("d "), snelheid en grootte van de wielen (zie de bovenstaande afbeelding ter illustratie).
void goAndTurn(int richting, int graden){ motorPIDcontrol(); vertraging (adjGoAndTurn); motorTurn(richting, graden);} 

Op dit punt is de robot in feite "een doolhof aan het oplossen"! Je maakt gewoon de "First Pass" af. Het maakt niet uit waar je begint in een doolhof, je zult altijd het einde bereiken.

Bellow, een test van deze fase van het project:

Stap 6:Het pad opslaan

Laten we het voorbeeld bekijken zoals weergegeven in de bovenstaande foto. Op het gekozen startpunt vindt de robot 15 kruispunten voordat hij het einde van het doolhof bereikt:

  • Links (L)
  • Terug (B)
  • Links (L)
  • Links (L)
  • Links (L)
  • Terug (B)
  • Recht (S)
  • Terug (B)
  • Links (L)
  • Links (L)
  • Terug (B)
  • Recht (S)
  • Links (L)
  • Links (L)
  • Einde

Wat op elk van die kruispunten moet worden gedaan, is om elke uitgevoerde actie in precies dezelfde volgorde op te slaan als waarin deze plaatsvindt. Laten we daarvoor een nieuwe variabele (array) maken die het pad opslaat dat de robot heeft afgelegd:

char path[100] =" "; 

We moeten ook 2 indexvariabelen maken om samen met de array te gebruiken:

unsigned char pathLength =0; // de lengte van de pathint pathIndex =0; // gebruikt om een ​​specifiek array-element te bereiken. 

Dus als we het voorbeeld in de afbeelding uitvoeren, eindigen we met:

pad =[LBLLLBSBLLBSLL]en padLengh =14 

Stap 7:Het pad vereenvoudigen (optimaliseren)

Laten we terugkeren naar ons voorbeeld. Toen we naar de eerste groep kruispunten keken, realiseerden we ons dat de eerste linkertak in feite een "Doodlopende weg" is en dus, als de robot in plaats van een "Links-Terug-Links" alleen rechtdoor zou gaan bij dat eerste kruispunt, veel energie en tijd zou worden bespaard! Met andere woorden, een reeks "LBL" zou in feite hetzelfde zijn als "S". Dat is precies hoe het volledige pad kan worden geoptimaliseerd. Als je alle mogelijkheden analyseert waarbij een "U-bocht" wordt gebruikt, kan de set van 3 kruispunten waar deze "U-bocht" ("B") verschijnt ("xBx") worden teruggebracht tot slechts één.

Het bovenstaande is slechts één voorbeeld, hieronder vindt u de volledige lijst met mogelijkheden (probeer het):

  • LBR =B
  • LBS =R
  • RBL =B
  • SBL =R
  • SBS =B
  • LBL =S

Door het volledige pad of ons voorbeeld te nemen, kunnen we het verkleinen:

path =[LBLLLBSBLLBSLL] ==> LBL =Spath =[SLLBSBLLBSLL] ==> LBS =Rpath =[SLRBLLBSLL] ==> RBL =Bpath =[SLBLBSLL] ==> LBL =Spath =[SSBSLL ] ==> SBS =Bpad =[SBLL] ==> SBL =Rpad =[RL] 

Geweldig! Kijkend naar het voorbeeld is het heel duidelijk dat als de robot RECHTS neemt bij het eerste kruispunt en daarna LINKS, hij het einde van het doolhof zal bereiken via de kortste weg!

De totale code van het eerste pad van Maze Solver wordt geconsolideerd in de functie mazeSolve() . Deze functie is in feite de eerder gebruikte loop()-functie, maar met al die stappen van opslag en padoptimalisatie. Wanneer het eerste pad eindigde, heeft de pad[]-array het geoptimaliseerde pad. Er wordt een nieuwe variabele geïntroduceerd:

unsigned int-status =0; // oplossen =0; bereik doolhofeinde =1 

Onder de functie Eerste pad:

void mazeSolve(void){ while (!status) { readLFSsensors(); schakelaar (modus) {geval NO_LINE:motorStop(); goAndTurn (LINKS, 180); recIntersection('B'); pauze; geval CONT_LINE:runExtraInch(); leesLFSsensoren(); if (modus!=CONT_LINE) {goAndTurn (LINKS, 90); recIntersection('L');} // of het is een "T" of "Cross"). Ga in beide gevallen naar LEFT else mazeEnd(); pauze; geval RIGHT_TURN:runExtraInch(); leesLFSsensoren(); if (modus ==NO_LINE) {goAndTurn (RECHTS, 90); recIntersection('R');} else recIntersection('S'); pauze; case LEFT_TURN:goAndTurn (LEFT, 90); recIntersection('L'); pauze; case FOLLOWING_LINE:volgendeLine(); pauze; } }}  

Hier is een nieuwe functie geïntroduceerd:recIntersection (richting). Deze functie wordt gebruikt om de kruising op te slaan en ook om een ​​andere functie simplifyPath() aan te roepen , dat zal de groep van 3 kruispunten met een "U-Turn" verkleinen, zoals we eerder zagen.

void recIntersection(char direction){ path[pathLength] =direction; // Sla de kruising op in de padvariabele. padLengte ++; vereenvoudigPad(); // Vereenvoudig het aangeleerde pad.} 

Het CREDIT voor de simplifyPath( ) functie is voor Patrick McCabe voor het pad Solving Code (ga voor details naar https://patrickmccabemakes.com)! De strategie van padvereenvoudiging is dat wanneer we een reeks xBx tegenkomen, we deze kunnen vereenvoudigen door de doodlopende weg te verwijderen. Bijvoorbeeld LBL ==> S zoals we bij het voorbeeld zagen.

void simplePath(){ // vereenvoudig het pad alleen als de voorlaatste bocht een 'B' was if(pathLength <3 || path[pathLength-2] !='B') return; int totaalHoek =0; int ik; for(i=1;i<=3;i++) { switch(path[pathLength-i]) { case 'R':totalAngle +=90; pauze; geval 'L':totaalAngle +=270; pauze; geval 'B':totaalAngle +=180; pauze; } } // Verkrijg de hoek als een getal tussen 0 en 360 graden. totale hoek =totale hoek % 360; // Vervang al die beurten door een enkele. switch(totalAngle) { case 0:path[pathLength - 3] ='S'; pauze; geval 90:path[pathLength - 3] ='R'; pauze; geval 180:path[pathLength - 3] ='B'; pauze; geval 270:path[pathLength - 3] ='L'; pauze; } // Het pad is nu twee stappen korter. padLengte -=2; }  

Stap 8:Second Pass:het doolhof zo snel mogelijk oplossen!

Het hoofdprogramma:loop () is zo simpel:

void loop() { ledBlink(1); leesLFSsensoren(); doolhofOplossen(); // Eerste pas om het doolhof op te lossen ledBlink(2); while (digitalRead(buttonPin) {} pathIndex =0; status =0; mazeOptimization(); // Second Pass:voer het doolhof zo snel mogelijk uit ledBlink(3); while (digitalRead(buttonPin) {} mode =GESTOPT; status =0; // 1e doorgang pathIndex =0; pathLength =0;} 

Dus wanneer de First Pass eindigt, moeten we de robot alleen voeden met de geoptimaliseerde padarray. Het begint te rennen en wanneer een kruispunt wordt gevonden, bepaalt het nu wat er moet gebeuren op basis van wat het is opgeslagen op path[] .

void mazeOptimization (void){ while (!status) { readLFSsensors(); switch (modus) { case FOLLOWING_LINE:volgendeLine(); pauze; case CONT_LINE:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (pad [padIndex]); pathIndex++;} pauze; case LEFT_TURN:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (pad [padIndex]); pathIndex++;} pauze; case RIGHT_TURN:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (pad [padIndex]); pathIndex++;} pauze; } } } 

Om opdracht te geven wat te doen, een nieuwe functie mazeTurn(path[]) werd gecreëerd. De functie mazeTurn (pad[]) zal zijn:

void mazeTurn (char dir) { switch(dir) { case 'L':// Sla linksaf goAndTurn (LEFT, 90); pauze; case 'R':// Sla rechtsaf goAndTurn (RECHTS, 90); pauze; geval 'B':// Draai terug goAndTurn (RECHTS, 800); pauze; case 'S':// Ga rechtdoor runExtraInch(); pauze; }} 

De tweede pas is klaar! De onderstaande video toont het volledige voorbeeld dat hier werkte, eerste en tweede doorgang. Hieronder de Arduino-code die in deze tutorial is gebruikt:

FV6XNJWINJ45XWM.ino F2FXS8MINJ45XX6.h FX5MHFMINJ45XX7.ino FT2S1WXINJ45XXA.ino F9IC3HQINJ45XXB.ino FU2HRXJINJ45XXV.ino

Stap 9:Android gebruiken om af te stemmen

De Android-app die is ontwikkeld voor het volgende lijn-project kan hier ook worden gebruikt (indien nodig zijn de Android-app en de bijbehorende code beschikbaar op:Line Follower Robot - PID Control - Android Setup. De Arduino-code die bij de laatste stap wordt gepresenteerd, bevat al communicatie met het Android-apparaat. Als u de Android-app niet wilt gebruiken, geen probleem want de code is "transparant ".

Ik heb de Android veel gebruikt tijdens het project om testgegevens van de robot naar het apparaat te sturen met behulp van de "Bericht ontvangen " veld. Verschillende variabelen moeten goed gedefinieerd zijn om te garanderen dat de robot de juiste hoek zal draaien. De belangrijkste staan ​​hieronder (degene die vet zijn gemarkeerd. Ik heb ze verschillende keren gewijzigd):

const int adj =0; float adjTurn =8;int adjGoAndTurn =800;THRESHOLD =150const int power =250; const int iniMotorPower =250; int extraInch =200;  

Stap 10:Conclusie

Dit is het tweede en laatste deel van een complex project, waarin de potentie van een lijnvolgerrobot wordt onderzocht, waarbij Kunstmatige Intelligentie (AI) eenvoudige concepten werden gebruikt om een ​​doolhof op te lossen.

Ik ben geen AI expert en op basis van wat informatie die ik van internet kreeg, begreep ik dat wat onze kleine Rex, de robot, deed, het doolhof oplossen, als een toepassing van AI kon worden beschouwd. Laten we eens kijken naar de 2 bronnen hieronder:

Van Wikipedia:

Of uit deze universitaire paper:"Maze Solving Robot Using Freeduino and LSRB Algorithm International Journal of Modern Engineering Research (IJMER)"

De bijgewerkte bestanden voor dit project zijn te vinden op GITHUB. Ik hoop dat ik kan bijdragen voor anderen om meer te leren over elektronica, robot, Arduino, enz. Ga voor meer tutorials naar mijn blog:MJRoBot.org

Saludos uit het zuiden van de wereld!

Bedankt

Marcelo

Code

  • Codefragment #1
  • Codefragment #4
  • Codefragment #5
  • Codefragment #6
  • Codefragment #7
  • Codefragment #8
  • Codefragment #12
  • Codefragment #13
  • Codefragment #14
  • Codefragment #15
  • Codefragment #16
  • Codefragment #17
Codefragment #1Platte tekst
const int lineFollowSensor0 =12; // Digitale inputconst gebruiken int lineFollowSensor1 =18; // Analoge pin A4 gebruiken als digitale invoerconst int lineFollowSensor2 =17; // Analoge pin A3 gebruiken als digitale invoerconst int lineFollowSensor3 =16; // Analoge pin A2 gebruiken als digitale invoerconst int lineFollowSensor4 =19; // Analoge pin A5 gebruiken als digitale invoerconst int farRightSensorPin =0; // Analoge pin A0const int farLeftSensorPin =1; //Analoge pin A1
Codefragment #4Platte tekst
LFSensor[0] =digitalRead(lineFollowSensor0);LFSensor[1] =digitalRead(lineFollowSensor1);LFSensor[2] =digitalRead(lineFollowSensor2);LFSensor[3] =digitalRead(lineFollowSensor3);LFSensor[4] =digitalRead( lineFollowSensor4);farRightSensor =analogRead(farRightSensorPin);farLeftSensor =analogRead(farLeftSensorPin);
Codefragment #5Platte tekst
ongeldige testSensorLogic(void) { Serial.print (farLeftSensor); Serial.print (" <==LINKS RECHTS==> "); Serial.print (farRightSensor); Serial.print ("modus:"); Serieafdruk (modus); Serial.print ("fout:"); Serial.println (fout);}
Codefragment #6Platte tekst
void loop(){ readLFSsensors(); schakelaar (modus) {geval NO_LINE:motorStop(); goAndTurn (LINKS, 180); pauze; geval CONT_LINE:runExtraInch(); leesLFSsensoren(); if (modus ==CONT_LINE) mazeEnd(); anders goAndTurn (LINKS, 90); // of het is een "T" of "Kruis"). In beide gevallen gaat naar LINKS breken; geval RIGHT_TURN:runExtraInch(); leesLFSsensoren(); if (mode ==NO_LINE) goAndTurn (RECHTS, 90); pauze; case LEFT_TURN:goAndTurn (LEFT, 90); pauze; case FOLLOWING_LINE:volgendeLine(); pauze; }}
Codefragment #7Platte tekst
void runExtraInch(void){ motorPIDcontrol(); vertraging (extra inch); motorStop();}
Codefragment #8Platte tekst
void goAndTurn(int richting, int graden){ motorPIDcontrol(); vertraging (adjGoAndTurn); motorTurn(direction, degrees);}
Code snippet #12Plain text
void mazeSolve(void){ while (!status) { readLFSsensors(); switch (mode) { case NO_LINE:motorStop(); goAndTurn (LEFT, 180); recIntersection('B'); pauze; case CONT_LINE:runExtraInch(); readLFSsensors(); if (mode !=CONT_LINE) {goAndTurn (LEFT, 90); recIntersection('L');} // or it is a "T" or "Cross"). In both cases, goes to LEFT else mazeEnd(); pauze; case RIGHT_TURN:runExtraInch(); readLFSsensors(); if (mode ==NO_LINE) {goAndTurn (RIGHT, 90); recIntersection('R');} else recIntersection('S'); pauze; case LEFT_TURN:goAndTurn (LEFT, 90); recIntersection('L'); pauze; case FOLLOWING_LINE:followingLine(); pauze; } }}
Code snippet #13Plain text
void recIntersection(char direction){ path[pathLength] =direction; // Store the intersection in the path variable. pathLength ++; simplifyPath(); // Simplify the learned path.}
Code snippet #14Plain text
void simplifyPath(){ // only simplify the path if the second-to-last turn was a 'B' if(pathLength <3 || path[pathLength-2] !='B') return; int totalAngle =0; int ik; for(i=1;i<=3;i++) { switch(path[pathLength-i]) { case 'R':totalAngle +=90; pauze; case 'L':totalAngle +=270; pauze; case 'B':totalAngle +=180; pauze; } } // Get the angle as a number between 0 and 360 degrees. totalAngle =totalAngle % 360; // Replace all of those turns with a single one. switch(totalAngle) { case 0:path[pathLength - 3] ='S'; pauze; case 90:path[pathLength - 3] ='R'; pauze; case 180:path[pathLength - 3] ='B'; pauze; case 270:path[pathLength - 3] ='L'; pauze; } // The path is now two steps shorter. pathLength -=2; } 
Code snippet #15Plain text
void loop() { ledBlink(1); readLFSsensors(); mazeSolve(); // First pass to solve the maze ledBlink(2); while (digitalRead(buttonPin) { } pathIndex =0; status =0; mazeOptimization(); // Second Pass:run the maze as fast as possible ledBlink(3); while (digitalRead(buttonPin) { } mode =STOPPED; status =0; // 1st pass pathIndex =0; pathLength =0;}
Code snippet #16Plain text
void mazeOptimization (void){ while (!status) { readLFSsensors(); switch (mode) { case FOLLOWING_LINE:followingLine(); pauze; case CONT_LINE:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (path[pathIndex]); pathIndex++;} break; case LEFT_TURN:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (path[pathIndex]); pathIndex++;} break; case RIGHT_TURN:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (path[pathIndex]); pathIndex++;} break; } } }
Code snippet #17Plain text
void mazeTurn (char dir) { switch(dir) { case 'L':// Turn Left goAndTurn (LEFT, 90); pauze; case 'R':// Turn Right goAndTurn (RIGHT, 90); pauze; case 'B':// Turn Back goAndTurn (RIGHT, 800); pauze; case 'S':// Go Straight runExtraInch(); pauze; }}
Github
https://github.com/Mjrovai/MJRoBot-Maze-Solverhttps://github.com/Mjrovai/MJRoBot-Maze-Solver

Schema's

z7IdLkxL1J66qOtphxqC.fzz

Productieproces

  1. Robot die Raspberry Pi &Bridge Shield gebruikt
  2. Gebaargestuurde robot met Raspberry Pi
  3. Wifi-gestuurde robot met Raspberry Pi
  4. SONBI ROBOT MENSELIJKE DETECTIE MET KINECT EN RASPBERRY PI
  5. Bosch voegt kunstmatige intelligentie toe aan industrie 4.0
  6. Is kunstmatige intelligentie fictie of rage?
  7. Kunstmatige intelligentie krijgt enorme Kubernetes-boost
  8. Kunstmatige intelligentie helpt robot objecten te herkennen door aanraking
  9. Kunstmatige intelligentie gebruiken om ontbossing te volgen
  10. Kunstmatige Intelligentie Robots
  11. Waarom een ​​collaboratieve robot gebruiken?