DOC

# Solving the final challenge - pirateshuedu - the homepages of

By Marilyn Gomez,2014-01-20 03:48
10 views 0
Solving the final challenge - pirateshuedu - the homepages of

Challenge 11

Solving this challenge is a multi-stage process with several different tasks. We clearly need two different robots with

different programming needs.

? First robot: differential-drive robot with (at least) a light sensor facing down

? must be able to recognize white, black, and green

? must be able to follow a black line in one direction

? must be able to recognize the end of the black line and stop

? must accurately recognize and count up to three green dots

? must be able to send at least two integers to another NXT via Bluetooth, representing the target coordinates

? Second robot: differential-drive robot with (at least) a distance sensor facing forward and mounted low

? must be able to receive at least two integers via Bluetooth

? must be able to play a siren-tune asynchronously (in addition to its other tasks)

? must be able to head toward a particular point in a coordinate system

? must be able to avoid an obstacle while continuing to head towards a given point

Consequently, I would write my programs incrementally, solving a sub-task, testing my program, then adding code to

solve the next task. In other words, I would for my first robot (the “explorer”):

1. Create code to calibrate the light sensor to get the raw values for the ‘colors’ white, green, and black. In addition,

I would create methods that read the light sensor and return the color that it sees. Note that the standard NXT

light sensors really does not recognize colors but only intensity. It therefore sees the world in shades of grey and

each color to recognize should have a distinct intensity level. There is a true color sensor to purchase, but we

don’t have it.

2. Create code to reliably follow a black line on white background

3. Modify the line-following code to recognize and verify green dots so we can count them

4. Modify the line-following, dot-detecting code to reliably detect the end of the line

5. Add code to send data over a Bluetooth connection

Once that works, I would focus on the second robot (the “rescuer”). It could, for example, work similar to the ‘Goal

Seeker’ program from an earlier challenge, perhaps optimized for speed and constructed with some kind of “loading

bed” to hold the figure.

In addition, I would make use of all the help LeJOS has to offer. In particular, LeJOS offers a “Pilot” class in the

“lejos.navigator” package. That class could be used to drive the first line-following, dot-detecting explorer robot.

Another useful class is the “Navigator” class in lejos.navigator. Not only does it let you drive a robot easily, it also can

keep track of the robot’s current position and allows you to move to an arbitrary (x,y) coordinate. It would be perfect for

our second robot!

Step 1: Recognizing colors

First let’s create a few methods to understand “white”, “green”, and “black”. A little experimentation with the light

sensor shows that white reflects the most (highest value), then green (middle value), and finally black (low values). Our

goal is to read the intensities for these ‘colors’ and store them (calibration). Then we use ‘half-in-between’ cutoff values

to determine which particular color the light sensor has detected. Here is a complete program for the explorer robot so

you can calibrate and test the calibration:

import lejos.nxt.Button;

import lejos.nxt.LCD; import lejos.nxt.LightSensor;

import lejos.nxt.SensorPort; import lejos.nxt.Sound;

/*

* Explorer 1 *

* Code to calibrate light sensors to the exiting colors black, green, * and white. Tests to make sure calibration works. Watch the LCD panel

* for instructions, but read the comments and code below first. */ public class Explorer1

{

// state variables static LightSensor light = new LightSensor(SensorPort.S1);

// variables to store cut-off intensities after calibration static float cut_black = 0.0f; static float cut_green = 0.0f;

static float cut_bw = 0.0f;

// standard main method calibrates colors and verifies it. public static void main(String args[])

{ calibrateColors(); verifyCalibration();

}

/* * Determines the value for the respective colors and sets up the cut-off

* values to define ‘white’, ‘green’, and ‘black’. *

* When prompted, position the light sensor onto the requested color and * press ENTER. Repeat for all three ‘colors’. The function then defines

* cut-off values as halfway between the various color intensities. *

* EFFECT: * Sets state variables cut_green, cut_black, and cut_bw

* NOTES: * You must call this function first to calibrate the light sensor!!

*/ public static void calibrateColors()

{ light.setFloodlight(true);

LCD.clear();

LCD.drawString("Calibrate colors", 0, 0);

LCD.drawString("White [ENTER]", 0, 2);

waitForEnter();

LCD.drawString("Black [ENTER]", 0, 3); waitForEnter();

Sound.beep();

LCD.drawString("Green [ENTER]", 0, 4); waitForEnter();

// Listing colors by decreasing reflective values

LCD.clear(); LCD.drawString("Color codes", 0, 0);

LCD.drawString("white = " + white, 0, 2); LCD.drawString("green = " + green, 0, 3);

LCD.drawString("black = " + black, 0, 4); waitForEnter();

cut_black = (black + green) / 2.0f;

cut_green = (green + white) / 2.0f; cut_bw = (black + white) / 2.0f;

}

/* * Utility function to beep and wait until the user presses "ENTER".

* * EFFECT:

* blocks execution until the user presses ENTER */ private static void waitForEnter()

{ try

{ Sound.beep(); Button.ENTER.waitForPressAndRelease();

} catch(Exception ex)

{ }

} /*

* Function to query the light sensor if it sees a black line. * Assumes that cutoff values have been set via calibration.

* * OUTPUT:

* true if light sensor currently detects black, false * otherwise.

* NOTE: * This function ignores colors other than black & white so

* that it can accurately detect black. */ private static boolean canSeeLine()

if (value < cut_bw) return true;

else return false;

}

/* * Function to query the light sensor if it sees a green dot.

* Assumes that cutoff values have been set via calibration. *

* OUTPUT: * true if light sensor currently detects green, false * otherwise.

*/ private static boolean canSeeDot()

if (value < cut_black) return false;

else if (value < cut_green) return true;

else return false;

}

/* * Function to test color detection. Foes not really add

* to the robot's function but is important to test color * calibration. Move the light sensor onto the color

* indicated and check the color detected. You should * see 'true'.

*/ public static void verifyCalibration()

{ LCD.clear();

LCD.drawString("Verify Colors", 0, 1);

Sound.beep(); LCD.drawString("Black?", 0, 3);

waitForEnter(); LCD.drawString("" + canSeeLine(), 8, 3);

Sound.beep();

LCD.drawString("Green?", 0, 4);

waitForEnter();

LCD.drawString("" + canSeeDot(), 8, 4);

Sound.beep(); LCD.drawString("White?", 0, 5);

waitForEnter(); LCD.drawString("" + (!canSeeLine()), 8, 5);

LCD.drawString("continue ...", 0, 7); waitForEnter();

} }

Step 2: Line-following code

Our line-following algorithm is as follows:

? If we see black, drive forward

? If not, stop and start scanning (rotating) 10 degrees to one side, then 20 to the other, then 40 to the first, etc.

Stop scanning (rotating) when we see black

? Continue driving forward

This algorithm will follow a closed-loop line indefinitely, or an “open” line to the end, then turn around, etc. We will use

the calibration and color-detection code from the above “Explorer1” but add a few constants, the method “followLine”, and a slightly changed “main” method. You could remove the “verifyCalibration” method to shorten the code somewhat

and to save a few bytes. Here are the modifications to “Explorer1”: import lejos.navigation.Pilot;

import lejos.nxt.Button; import lejos.nxt.LCD;

import lejos.nxt.LightSensor; import lejos.nxt.Motor;

import lejos.nxt.SensorPort; import lejos.nxt.Sound;

/*

* Explorer 2 *

* Code to calibrate light sensors to the exiting colors black, green, * and white. After calibration, follows a black line indefinitely. This

* class uses a 'Pilot' from the 'lejos.navigation' package to handle the * details of driving the robot. Make sure to consult the LeJOS API for

* details on using that very convenient Pilot class. */ public class Explorer2

{ // constants for the geometry of differential drive robot final static float WHEEL_DIAM = 5.4f;

final static float AXLE_WIDTH = 10.4f;

// constants for default speed final static int DRIVE_SPEED = 200;

// state variables static LightSensor light = new LightSensor(SensorPort.S1); static Pilot robot = new Pilot(WHEEL_DIAM, AXLE_WIDTH, Motor.A, Motor.C);

// variables to store cut-off intensities after calibration static float cut_black = 0.0f; static float cut_green = 0.0f;

static float cut_bw = 0.0f;

// direction of last successful scan during line detection static int scanDir = 1;

// standard main function calibrates colors and follows line public static void main(String args[])

{ robot.setSpeed(DRIVE_SPEED); robot.regulateSpeed(false);

calibrateColors();

followLine();

}

/* * Follows black line indefinitely. Note that it uses 'scanDir' to keep

* track of the direction of the last successful scan. Next time scanning * begins in that direction assuming that it seems likely that a line is

* curved in one direction for a while. *

* Please consult the LeJOS API for details on using the Pilot class. *

* EFFECT: * drives robot along line and modifies the state variable 'scanDir' to

* keep track of the latest successful scan direction. */ public static void followLine()

{ light.setFloodlight(true);

boolean onTheLine = true; boolean foundDot = false;

while (!foundDot)

{ onTheLine = canSeeLine();

if (onTheLine) robot.forward();

else

{ robot.stop();

// set angle to 10 degrees, same direction as last successful sweep int scanAngle = scanDir*10;

// start scanning until we find BLACK while (!onTheLine)

{ // begin rotate robot but return immediately ( robot.rotate(scanAngle, true);

// loop until BLACK or scan complete while ((!onTheLine) && (robot.isMoving()))

{ onTheLine = canSeeLine();

} if (!onTheLine)

{ // no luck here, increase angle & scan in other direction scanDir *= (-1); scanAngle = scanDir*Math.abs(2*scanAngle);

} }

// found BLACK so stop robot and end else part robot.stop();

} }

}

// no change to the following functions public static void calibrateColors()

private static void waitForEnter() private static boolean canSeeLine()

private static boolean canSeeDot()

// the verifyCalibration function is removed

}

Next we add ‘dot detection’ to our code. This is a little tricky since when the light sensor passes from black to white it

may (and indeed will) detect ‘grey values’ that just might be interpreted as green. To avoid wrong detection we do the following:

? If we detect green, move forward 4 cm

? If we still see green, rotate left by 4 degrees

? If we still see green, rotate right by 8 degrees

? If we still see green, rotate back by 4 degrees and drive backwards for 1 cm

? If we still see green, we believe that we found a green dot

The function that will accomplish this is ‘verifyDot’ and is listed below. Also, to make use of the new function, we change

the “followLine” to “followLineToDot” , also listed below and add a ‘dotCount’ state variable. Finally, we modify the main method to bring our “Explorer2” to an “Explorer3”:

/*

* Explorer 3 *

* Code to calibrate light sensors to the exiting colors black, green, * and white. After calibration, follows a black line until it sees a

* green dot. After verifying the green dot, it continues to follow a * line. Uses a 'Pilot' from the 'lejos.navigation' package to handle the * details of driving the robot. Make sure to consult the LeJOS API for

* details on using that very convenient Pilot class. *

* Note that we renamed the function 'followLine' in 'Explorer2' to

* 'followLineToDot' to reflect its new functionality.

* */ public class Explorer3

{

// all constants and state variables from ‘Explorer2’ as well as one new variable:

// counts the number of dots detected and verified static int dotCount = 0;

// standard main function calibrates colors and follows line until

// it encounters three green dots. public static void main(String args[])

{ robot.setSpeed(DRIVE_SPEED);

robot.regulateSpeed(false); calibrateColors();

dotCount = 0;

while (dotCount < 2)

{

followLineToDot(); verifyDot();

} }

/*

* Follows black line until it encounters a possible green dot. It uses * 'scanDir' to keep track of the direction of the last successful scan.

*

* EFFECT:

* Drives robot along line and modifies the state variable 'scanDir' to * keep track of the latest successful scan direction until it finds a

* possible green dot. */ public static void followLineToDot()

{ light.setFloodlight(true); LCD.clear();

boolean onTheLine = true; boolean foundDot = false;

while (!foundDot)

{ onTheLine = canSeeLine(); foundDot = canSeeDot();

if (foundDot) // if we find a (suspected) green dot we quit by return; // returning immediately

else if (onTheLine) // if we find part of the line we continue robot.forward(); // driving forward

else // otherwise we are lost and must scan for the line

{ robot.stop();

// set angle to 10 degrees, same direction as last successful sweep int scanAngle = scanDir*10;

// start scanning until we find the line while (!onTheLine)

{

// begin rotate robot and return immediately robot.rotate(scanAngle, true);

// loop until line found or scan rotation is complete while ((!onTheLine) && (robot.isMoving()))

{ onTheLine = canSeeLine();

} if (!onTheLine)

{ // No luck here; increase angle & switch scan direction scanDir *= (-1);

scanAngle = scanDir*Math.abs(2*scanAngle);

}

} // found the line so stop robot (end of the 'else' part) robot.stop();

} }

}

/* * Verifies that we are currently on a dot: First we stop. Then we

* measure the color where we stand. If it's a dot, move forward by * 4 cm and measure again. If we are still on a dot, rotate right/left

* by 4 degrees. If we are still on green, move back by 1 cm. If we are * still on a dot, we (finally) confirm a dot.

* * When we confirm a dot, we beep and wait for ENTER. Then we move

* forward by 10 cm to move past the current dot. *

* EFFECT: * increments dotCount if green dot is verified

* waits for user to press ENTER before continuing */ public static void verifyDot()

{ LCD.clear(); LCD.drawString("Dot ???", 0, 1);

robot.stop(); if (canSeeDot())

{ robot.travel(4.0f);

if (canSeeDot())

{ robot.rotate(4); if (canSeeDot())

{ robot.rotate(-8);

if (canSeeDot())

{ robot.rotate(4); robot.travel(-1.0f);

if (canSeeDot())

{ dotCount++; LCD.drawString("Dot confirmed", 0, 2);

Sound.beepSequenceUp(); waitForEnter(); robot.travel(10.0f);

} }

} }

} }

Step 4: Wrapping things up:

The final version of the Explorer program could be based on “Explorer3” but:

? Change followLineToDot to also detect end-of-line. You could do that by checking the scan angle: if it's more

than 180 or so you should be at the end of the line

? Change the loop in the main function to stop when you have detected the end of the line (since you do not

know how many dots there are)

? After detecting the end of the line, transmit the appropriate target coordinates to the second robot and move

out of the way, using one more function like 'sendCoordinates'

I did not test this, but I could imagine something like this to work:

/* * Explorer

* * Programs differential drive robot to follow a black line until the end,

* counting green dots along the way. Once it determines the how many dots * are there it transmits the respective coordinates to a second robot and

* moves out of the way. */ public class Explorer

{

// the x and y coordinates of possible target areas specified at start of challenge final static int[] DOTS_X = { 250, 250, 250 };

final static int[] DOTS_Y = { 60, 80, 100 };

// the name f the NXT to send coordinates to final static String PARTNER_NAME = "sea3peo";

// rest of the constants/state variable as before,, but with one more addition:

// flag to indicate the end of the line has been found static boolean endOfLine = false;

// standard main function public static void main(String args[])

{ robot.setSpeed(DRIVE_SPEED); robot.regulateSpeed(false);

calibrateColors();

dotCount = 0; endOfLine = false;

while (!endOfLine)

{ followLineToDot();

if (!endOfLine) verifyDot();

} sendCoordinates();

}

/*

* Follows black line until it encounters a possible green dot or the end * of the black line. It uses 'scanDir' to keep track of the direction of

* the last successful scan.

* EFFECT:

* Drives robot along line and modifies the state variable 'scanDir' to * keep track of the latest successful scan direction until it finds a

* possible green dot or the end of the line. * Sets 'endOfLine' to true if it detects the end of the line.

*/ public static void followLineToDot()

{ light.setFloodlight(true);

LCD.clear(); LCD.drawString("Follow line ...", 0, 0);

boolean onTheLine = true;

boolean foundDot = false;

while ((!foundDot) && (!endOfLine))

{ onTheLine = canSeeLine(); foundDot = canSeeDot();

if (foundDot) // if we find a (suspected) green dot we quit by return; // returning immediately

else if (onTheLine) // if we find part of the line we continue robot.forward(); // driving forward

else // otherwise we are lost and must scan for the line

{ robot.stop();

// set angle to 10 degrees, same direction as last successful sweep int scanAngle = scanDir*10;

// start scanning until we find the line while ((!onTheLine) && (!endOfLine))

{ // begin rotate robot and return immediately

robot.rotate(scanAngle, true);

// loop until line found or scan rotation is complete while ((!onTheLine) && (robot.isMoving()))

{ onTheLine = canSeeLine();

} if (!onTheLine)

{ // No luck here; increase angle & switch scan direction

scanDir *= (-1); scanAngle = scanDir*Math.abs(2*scanAngle);

} endOfLine = (Math.abs(scanAngle) >= 200);

} // found the line, a dot, or end of line so stop robot robot.stop();

}

} }

/*

* Sends the coordinates to the partner brick via Bluetooth using * 'dotCount' as index. Assumes that dotCount < 3. After sending

* coordinates we move out of the way. This is only simulated code, the * actual code is similar to what we discussed in class.

*/ public static void sendCoordinates()

{ int x = -1;

int y = -1; if (dotCount < 3)

{ x = DOTS_X[dotCount]; y = DOTS_Y[dotCount];

} LCD.clear(); LCD.drawString("Sending Coords", 0, 0);

LCD.drawString("x = " + x, 0, 2); LCD.drawString("y = " + y, 0, 3);

// here would be the true sending code

LCD.drawString(" !Sent! ", 0, 6);

// moving out of the way light.setFloodlight(false); robot.setSpeed(600);

robot.travel(50.0f);

}

Report this document

For any questions or suggestions please email
cust-service@docsford.com