Maze misfortunes

After working, on and off, for months I finally took the robot for a spin. I have heard the phrase,
"If you want things done properly you got to do them yourself". Never gave much credit to it, but
I guess I should've heed. It only took a few minutes of testing before the pulse button attached to
the wheel made enough effort and generated enough heat up the H-Bridge to the point of no return.
This wouldn't matter much but as it turns out this design was not using an opto coupler to isolate the
Omniflash card from the H-Bridge.

Needless to say the effect was disastrous, and it only took a matter of minutes before the destruction
took place and just 2 days before the regional contest! You just can't make up things like these!

But it wasn't all bad; after all I did enjoy messing around with the Linux kernel, writing a driver to
handle IRQ routines, and interact with the main robot program.

This was necessary, as any maze transverse robot, it requires a way to identify its location when loops
are present. Right/Left hand rule won't suffice. This document has a lot of information required to do a successful robot

The program worked like this (or at least worked in theory and software):

You set it at the track and turned it on with one switch. It had two switches, one switch was the on/off combo,
and the second switch was used to indicate either a learning run (to transverse the map once and find
the exit) and save the map in a binary file. The other setting allowed running the map using the shortest path.

It communicated with the kernel driver so during forward walking it would keep track of the distance in order
to be able to generate a matrix of locations, although it was represented in memory more like a graph with 4 exits.
The code is based on a MUD mapper I wrote for windows a few years ago so it keep the original essence,
that is north, south, east, west turns.

Let’s put in some sample code:

void advanceTick()
{

writeModule(1);
motor_one(ADELANTE);
motor_two(ADELANTE);

for(;;)
{
checkSensors();

if ((sensors[0] && sensors[4]) || sensors[0] || sensors[4])
{
motor_two(APAGAR);
motor_one(APAGAR);

/* encontre entronque */
break;
}

if (sensors[1] && sensors[2] && sensors[3]) // 01110
{
motor_one(ADELANTE);
motor_two(ADELANTE);
continue;
}

if (sensors[1] && sensors[2] && !sensors[3]) // 01100
{
motor_one(APAGAR);
motor_two(ADELANTE);
continue;
}

if (!sensors[1] && sensors[2] && sensors[3]) // 00110
{
motor_one(ADELANTE);
motor_two(APAGAR);
continue;
}

if (sensors[1] && !sensors[2] && !sensors[3]) // 01000
{
motor_one(ATRAS);
motor_two(ADELANTE);
continue;
}

if (!sensors[1] && !sensors[2] && sensors[3]) // 00010
{
motor_one(ADELANTE);
motor_two(ATRAS);
continue;
}

if (!sensors[1] && sensors[2] && !sensors[3]) // 00100
{
motor_one(ADELANTE);
motor_two(ADELANTE);
continue;
}

}

switch(facing)
{
case north:
mapX -= 1;
break;
case south:
mapX += 1;
break;
case east:
mapY += 1;
break;
case west:
mapY -= 1;
break;
}
}

The writeModule function would clear the interrupt counter on the pulse button attached to the wheel in order
start counting and generate the distance with the proper formula. I did not use an encoder due to the lack of
time but the information on how to use an encoder can be found here.

Checksensors is a simple function to poll the sensors the rest would be the source required to navigate.
The robot had 5 sensors on the front, the logic behind having these sensors on front can be found on the
link I previously put up there regarding line maze algorithms.

Now the function that does most of the work goes like this:

int robotDFS(Graph * strt_room)
{
stack * dfs = NULL;
Graph * current_room = NULL, * prev_room = NULL, * work_room = NULL;

int foundNewNodes = 0;
int ticks = 0;
int prevdir = -1;
cleanRooms();

strt_room->visit = GRAY;
stack_push(&dfs, (void*)strt_room);

while(!stack_empty(dfs))
{
int i;
int * dirs;
foundNewNodes = 0;

current_room = (Graph *) stack_pop((stack**)&dfs);

if (prev_room != NULL)
{
for (i=0; i<4; i++)
if ((Graph_getDirections(prev_room))[i] ==
Graph_getId(current_room))
{

if (!move(i, FALSE))
{
tgrt = prev_room;

while (!stack_empty(dfs))
stack_pop((stack**)&dfs);

return 1;
}
}
}

/* pulsador */
ticks = readModule(1);

while (ticks >= 20 && current_room != NULL)
{
prev_room = current_room;
current_room->visit = BLACK;

work_room = Graph_addRoom(current_room, facing);
work_room->distance = current_room->distance;
work_room->pred = current_room;
work_room->visit = GRAY;

coordinateMove();

current_room = work_room;

ticks -= 20;
}

if (atExit())
{
tgrt = prev_room;
goal = tgrt->id;

while (!stack_empty(dfs))
stack_pop((stack**)&dfs);

return 1;
}

scanNeighbors(current_room);
dirs = Graph_getDirections(current_room);

for (i=0; i < 4; i++)
{

Graph * gfr = CMapper_findRoomById(theRooms->rooms, dirs[i]);

if (gfr && gfr->visit == WHITE)
{
gfr->visit = GRAY;
gfr->distance = current_room->distance;
gfr->pred = current_room;

stack_push(&dfs, gfr);
foundNewNodes = 1;
}

}

prev_room = current_room;
current_room->visit = BLACK;

if (!foundNewNodes)
{
Graph * end = NULL;
Graph * backtrack = current_room;

if (!stack_empty(dfs))
end = (Graph *)stack_pop((stack**)&dfs);
else
break;

prevdir = -1;
while(backtrack != NULL)
{
if (end->pred == backtrack)
break;

for(i=0; i < 4; i++)
{
if (backtrack->directions[i] == backtrack->pred->id)
{
if (prevdir == i)
{

}
else
{

if(!move(i, FALSE))
{
tgrt = prev_room;

while (!stack_empty(dfs))
stack_pop((stack**)&dfs);

return 1;
}
}
prevdir = i;
}
}

backtrack = backtrack->pred;
}

for (i=0; i<4;i++)
{
if (end->pred->directions[i] == end->id)
{
move(i, FALSE);
prev_room = NULL;
stack_push(&dfs,end);
}
}
}
}

return 0;
}

This is the function that makes the robot navigate and generate the map. You can up there is a readModule
function which interacts with the kernel driver in order to keep up with the distance walked. The 20 is
just a value that I determined in correspondence with the size of the wheel. You can find more info on the
encoder link. Now the read module is just a function that interacts with the driver “/dev/robot” that I
wrote heavily based on the OMNIflash example.

int readModule(int counter)
{
int retval = 0;
char buff[SOMENUMBER], *ptr = NULL, *end = NULL;

if (counter < 1)
return -1;

if (counter > 3)
return -1;

if ((module = open("/dev/robot", O_RDONLY)) == -1)
{
fprintf(stderr, "Unable to open /dev/robot\n\r");
exit(-1);
}

retval = read(module, buff, SOMENUMBER);

if (retval == -1)
perror("retval");

if (retval > 0)
{
buff[retval] = 0;

ptr = strchr(buff, ':');

if (counter > 1)
ptr = strchr(ptr+1, ':');

if (counter > 2)
ptr = strchr(ptr+1, ':');

ptr += 2;
end = ptr;

while(end && *end)
{
if(!isdigit(*end))
break;
end++;
}
*end = 0;

retval = atoi(ptr);
}

close(module);

return retval;
}

I will upload the code eventually, I am not sure it will be of use for anyone, but if anyone is interested
drop me a mail maplechori at gmail.com. A Line Maze robot is a LOT of fun, and you learn a lot.
It was particularly interesting for me because I don’t come from an electronics background, so getting to play with pull
down resistors, interacting with IO ports, bit operations , motors, timing etc. is quite an experience and you learn loads.
I might take up on more of these projects in the future.