Difference between revisions of "MazeWalker - Beatriz Ramos, João Estorninho"

From RoboWiki
Jump to: navigation, search
(Map Representation)
(Data Structures)
 
(14 intermediate revisions by the same user not shown)
Line 4: Line 4:
 
== Task ==
 
== Task ==
  
On this project, we built a maze with black tape and the main goal was to make our robot follow the tape lines and explore the maze until it finds an object/obstacle.
+
On this project, the main goal was to build a maze with black tape, make a robot follow the tape lines and explore the maze until it finds an object/obstacle, starting from any place of the maze.
 +
The fact that the start point is not fixed leads the robot to map the maze in order to avoid being trapped in loops.
  
 
== Solution ==
 
== Solution ==
Line 11: Line 12:
 
=== Map Representation ===
 
=== Map Representation ===
  
Our map was built with black lines of tape so that the robot could follow them and understand the roads and crossings. For the crossings, we used binary representation with 3 bits:
+
The map was built with black lines of tape so that the robot could follow them and understand the roads and crossings. For the crossings, were used binary representation with 3 bits:
  
 
-The most significant represent the existence of a road on the left;
 
-The most significant represent the existence of a road on the left;
Line 19: Line 20:
 
-The less significant bit represent the existence of a road on the right.  
 
-The less significant bit represent the existence of a road on the right.  
  
This was how we got our "relative turns".
+
This is useful to get the "relative turns".
As the robot could came for other side of the crossing (for example from the North instead of South, we had to consider the absolute turn value so that we could recognize the same crossing coming from other directions as well and so, we used two variables (dx and dy) to know where in the compass rose.
+
As the robot could come from different sides of the crossing (for example from the North instead of South), is necessary to consider the absolute turn value, in order to recognize the same crossing coming from all directions. Two variables (dx and dy) were created to know from where the robot comes.
  
 
=== Data Structures ===
 
=== Data Structures ===
  
 +
These are the data structures used:
 +
 +
maxNumberOfCrossings = 50
 +
uint8_t ct[maxNumberOfCrossings] : an array to save the cross type of each crossing;
 +
 +
uint8_t et[maxNumberOfCrossings][4] : a matrix to save exits taken from each crossing;
 +
 +
int32_t px[maxNumberOfCrossings],
 +
py[maxNumberOfCrossings] : arrays to save coordinates of each crossing;
 +
 +
uint8_t nCross, lastCrossing : number of crossings discovered and last one discovered;
 +
 +
int8_t dx, dy : current direction (dx = 0 and dy = 1 => North);
 +
 +
uint8_t plan[maxNumberOfCrossings] : constructed plan to follow;
 +
 +
uint8_t planSize : plan's size;
 +
 +
uint8_t queue[maxNumberOfCrossings] : queue to apply breadth-first search, in order to find crossings to explore;
 +
 +
uint8_t qwi, qri : pointers to write and read from the queue;
 +
 +
int32_t cx, cy : current coordinates;
 +
 +
uint16_t minv[8], maxv[8], thrd[8] : used during calibration;
 +
 +
uint32_t timeLastCross : coordinates are calculated based on time occurred between travel from one crossing and next one found;
 +
 +
uint8_t cameFrom[maxNumberOfCrossings] : to save directions already explored in each crossing and used for backwards reconstruction of the path (after the BWS -Backward Search- discovers a new location with unvisited exit);
 +
 +
uint8_t visited[maxNumberOfCrossings] : to save crossings already visited during BWS.
  
 
=== Algorithm ===
 
=== Algorithm ===
  
Our algorithm was composed mainly of 6 functions. Our main function was solveMaze() which called all the other auxiliar functions that we needed. First in the solve maze, the robot walked into the next crossing calling the travelToNext() function leading us straight to the next crossing, getting to the discoverCrossingType() which returned the type of crossing found once it recognized a crossing in its way. After we get the crossing, we needed to update the map, adding this new found crossing to our data structures (ct, py, px and et) or correcting his coordenates if we found some similar coordenates of a known crossing which could be the same with a small error (comparing with a sensible value of tolerance). After the updateMap(), the function decideTurn() was called. This is our most complex auxiliar function, once called it has 3 function nested with 3 options to take. The first option was if there were unvisited exits, if there were, we would take one of the unvisited, set the corresponding exit in our et array to 1, reset the plan to zero and take that turn with the turnAsDecided() function. This function turnAsDecided() receives the information where to turn and calls our movements functions turn90lf(), turn90r(),turn180lf() or turn180r(). If there wasn´t any exit unvisited, we check the value of the plan and if it is different than 0 (means we have a plan), we just call the followPlan(). On this followPlan() function we just return the next exit on the plan array and decrease the planSize (in other words, we take that exit out of the plan as we already took it). At last, if we do not have a plan, we call the function constructPlan(). In this function we pick our current cross, and look for neighbours checking if they have any unvisited nodes, if yes we make a plan of turns to reach them, if not, we user our queue putting this crossing neighbours into our queue and exploring their neighbours, eventually finding an unvisited node somewhere on the maze. This decideTurn() and turnAsDecided() and travelToNext() are called in a loop exiting only if travelToNext() function returns 1 ( in other words, maze exit found).
+
 
 +
The robot program has the option of calibration of all sensors. Saves its thresholds to the EEPROM and automatically loads them when started.
 +
 
 +
The main algorithm was decomposed in 6 functions.
 +
 
 +
The main function is solveMaze() which calls all the other auxiliary functions needed.
 +
Firstly, in the solve maze, the robot walks to the next crossing calling the travelToNext() function, leading straight to the next crossing. Then, using discoverCrossingType(), the type of crossing found is returned  once it recognized a crossing in its way.
 +
 
 +
After getting the crossing type, the map is updated adding this new found crossing to data structures (ct, py, px and et). If already exists similar coordinates (which could be the same with a small error, comparing with a sensible value of tolerance) from a previously found crossing, its coordinates are readjusted.
 +
 
 +
After the updateMap(), the function decideTurn() is called. This is the most complex auxiliary function. Once called, it has 3 nested functions with 3 options to take.
 +
The first option is if there are any unvisited exits. If yes, the robot take one of them, set the corresponding exit as visited in the et array, reset the plan to zero and take that turn with the turnAsDecided() function. This function turnAsDecided() receives the information where to turn and calls movements functions turn90lf(), turn90r(),turn180lf() or turn180r(). If all the exists have already been explored, the value of the plan is checked and if it is different than 0 (check if a plan exists), just calls the followPlan().
 +
 
 +
On this followPlan() function, the next exit on the plan array is returned and the planSize is decreased (in other words, takes that exit out of the plan as it already took it).
 +
 
 +
At last, if there is no plan, calls the function constructPlan().
 +
In this function the current cross is picked and looks for neighbors, checking if they have any unvisited nodes.
 +
If yes, a plan of turns to reach them is made.
 +
If not, our queue is used to put this crossing neighbors and explore them, eventually finding an unvisited node somewhere on the maze (Breadth-First Search - BFS).
 +
These decideTurn(), turnAsDecided() and travelToNext() are called in a loop, exiting only if travelToNext() function returns 1 (in other words, maze exit / object found).
  
 
== Photos ==
 
== Photos ==

Latest revision as of 15:50, 10 June 2019

back to projects


Task

On this project, the main goal was to build a maze with black tape, make a robot follow the tape lines and explore the maze until it finds an object/obstacle, starting from any place of the maze. The fact that the start point is not fixed leads the robot to map the maze in order to avoid being trapped in loops.

Solution

Map Representation

The map was built with black lines of tape so that the robot could follow them and understand the roads and crossings. For the crossings, were used binary representation with 3 bits:

-The most significant represent the existence of a road on the left;

-The middle one represent the existence of a straight road;

-The less significant bit represent the existence of a road on the right.

This is useful to get the "relative turns". As the robot could come from different sides of the crossing (for example from the North instead of South), is necessary to consider the absolute turn value, in order to recognize the same crossing coming from all directions. Two variables (dx and dy) were created to know from where the robot comes.

Data Structures

These are the data structures used:

maxNumberOfCrossings = 50 uint8_t ct[maxNumberOfCrossings] : an array to save the cross type of each crossing;

uint8_t et[maxNumberOfCrossings][4] : a matrix to save exits taken from each crossing;

int32_t px[maxNumberOfCrossings], py[maxNumberOfCrossings] : arrays to save coordinates of each crossing;

uint8_t nCross, lastCrossing : number of crossings discovered and last one discovered;

int8_t dx, dy : current direction (dx = 0 and dy = 1 => North);

uint8_t plan[maxNumberOfCrossings] : constructed plan to follow;

uint8_t planSize : plan's size;

uint8_t queue[maxNumberOfCrossings] : queue to apply breadth-first search, in order to find crossings to explore;

uint8_t qwi, qri : pointers to write and read from the queue;

int32_t cx, cy : current coordinates;

uint16_t minv[8], maxv[8], thrd[8] : used during calibration;

uint32_t timeLastCross : coordinates are calculated based on time occurred between travel from one crossing and next one found;

uint8_t cameFrom[maxNumberOfCrossings] : to save directions already explored in each crossing and used for backwards reconstruction of the path (after the BWS -Backward Search- discovers a new location with unvisited exit);

uint8_t visited[maxNumberOfCrossings] : to save crossings already visited during BWS.

Algorithm

The robot program has the option of calibration of all sensors. Saves its thresholds to the EEPROM and automatically loads them when started.

The main algorithm was decomposed in 6 functions.

The main function is solveMaze() which calls all the other auxiliary functions needed. Firstly, in the solve maze, the robot walks to the next crossing calling the travelToNext() function, leading straight to the next crossing. Then, using discoverCrossingType(), the type of crossing found is returned once it recognized a crossing in its way.

After getting the crossing type, the map is updated adding this new found crossing to data structures (ct, py, px and et). If already exists similar coordinates (which could be the same with a small error, comparing with a sensible value of tolerance) from a previously found crossing, its coordinates are readjusted.

After the updateMap(), the function decideTurn() is called. This is the most complex auxiliary function. Once called, it has 3 nested functions with 3 options to take. The first option is if there are any unvisited exits. If yes, the robot take one of them, set the corresponding exit as visited in the et array, reset the plan to zero and take that turn with the turnAsDecided() function. This function turnAsDecided() receives the information where to turn and calls movements functions turn90lf(), turn90r(),turn180lf() or turn180r(). If all the exists have already been explored, the value of the plan is checked and if it is different than 0 (check if a plan exists), just calls the followPlan().

On this followPlan() function, the next exit on the plan array is returned and the planSize is decreased (in other words, takes that exit out of the plan as it already took it).

At last, if there is no plan, calls the function constructPlan(). In this function the current cross is picked and looks for neighbors, checking if they have any unvisited nodes. If yes, a plan of turns to reach them is made. If not, our queue is used to put this crossing neighbors and explore them, eventually finding an unvisited node somewhere on the maze (Breadth-First Search - BFS). These decideTurn(), turnAsDecided() and travelToNext() are called in a loop, exiting only if travelToNext() function returns 1 (in other words, maze exit / object found).

Photos

MazeWalker


MazeWalker


MazeWalker

Video

Source Code

mazewalker.zip