CS 5541 Programming Assignments

Fall 2003


Lab #2: A Simulated Robot Agent (30 points)

Turned out: Tuesday, 16 September 2003

Due date: Tuesday, 30 September 2003

You are to write, in Python, a "brain" for the pyrorobotics simulator, in Python. The goal for your simulated robot is to follow walls. It should move until it finds a wall, and then follow that wall around the environment, turning as the wall changes.

The environment and simulated robots

First you need to connect to a computer science department csdev computer (e.g., csdev01, csdev02, ...). I recommend you use the Secure Shell program to connect.

Next, start the pyrorobotics simulator by typing:

pyro

This starts the simulator and displays the Pyro window. To gain access to the needed environment, load the test world by clicking on the "Simulator:" button in the Pyro window, click on "Khepera" , and then "OK", then click on "square.world" and then "OK". This will cause a new World window to open, and provides an environment for the simulated robot you will write.

Now, load the KheperaSimulated robot. You do this by clicking on the "Robot:" button in the Pyro window, clicking on "KheperaSimulated.py", then clicking on "OK". This defines the generic methods and properties available for the simulated robot you are programming.

Now, load a sample robot brain. Use the brain WallFollow.py. You will be working from this initial Python program to program a brain for your own simulated robot. Put a copy of WallFollow.py in your home directory. To load this brain, click on "Home", and load WallFollow.py.

Now you have everything ready to try the WallFollow brain. To do this, click on "Run" in the Pyro window. To stop the simulation, click on "Stop" in the Pyro window.

Robot Motor Control

Now that you have a simulated robot in the environment, you can get a sense of what it takes to control the simulated robot by entering commands at the "Command:" portion of the screen. Some of the commands are:

robot.translate(X)

moves the robot; X ranges between -1.0 and +1.0; positive numbers indicate forward movement, negative numbers indicate reverse movement; unless you stop the robot, it keeps moving.

robot.stop()

stops the robot from moving

robot.rotate(X)

causes the robot to rotate; X ranges between -1.0 and +1.0; positive numbers indicate counter clockwise movement, negative numbers indicate clockwise movement; unless you stop the robot, it keeps rotating

robot.move(T, R)

rotate and translate combined; T (for translation) and R (for rotation) range between -1.0 and +1.0

For some more detail, see the Pyrorobotics web page. (Note: I have had some problems viewing these web pages using Internet Explorer-- you might want to try Netscape if you have any problems seeing the examples).

Robot Sensors

The simulated Khepera robot has infrared (8) and light sensors (8), and a camera sensor. The infrared sensors can be accessed through the range abstraction, which gives you distance to objects (e.g., walls) in the environment. From the Command line of the Pyro window, try using the range sensor abstraction.

robot.get('range', 'count')

Returns a count of the number of range sensors (this should be 8).

robot.get('range', 'value', N)

Returns the value of range sensor N, 0 <= N < 8 (for Khepera). Smaller values returned by this method mean shorter distances (e.g., to obstacles). Larger values mean greater distances.

robot.get('range', 'units')

Returns the current units for the range sensors. By default, the units are 'ROBOTS', which is in terms of the robot's diameter.

robot.set('range', 'units', NEWUNITS)

Changes the units returned by calls to get the range sensor values. NEWUNITS can be 'CM' (centimeters), 'SCALED' (between +1.0 and -1.0) and a few others.

NOTE: So far I don't really see that changing the units in this system really does what you think it might do. Yes, I can call this "set" method, but when I change the units to "ROBOTS" (for example), I don't seem to get values returned that are really in relation to the size of the robot body. I personally am finding that with "ROBOTS" units, with "CM" units, or with "SCALED" units, when the values from the range sensors reach near 0, this means that that particular sensor is near an obstacle. Larger numbers don't really reflect what you might think, however.

The sensor topology (distribution of the sensors around the robot) is displayed in the Pyro World window, and you can also see it on the sensors page. Some other related details can also be found on this sensors page of the Pyrorobotics site.

The Cheat Sheet also has some sensor and motor information.

In the Lab

Past the above, in the first in-lab session with Pyro you should gain familiarity with the basic structure of robot controllers in Python (e.g., see WallFollow.py), and also learn how the controllers operate within the Pyro environment.

The basic structure of a robot brain is a class. There is only one required method for a Pyro brain, named step, but the WallFollow brain also has a constructor method. The WallFollow brain doesn't actually do wall following-- that is your goal-- but rather it does two things. First, in the constructor, the WallFollow.py brain sets the units of the range sensors to SCALED and then causes the robot to move forward (translate) at 1/2 half speed. The constructor is called when the Brain is first loaded, and when a Reload is performed. Try pressing Reload and observe that the robot moves forward.

Now, with the robot moving forward, having pressed the Reload button, try pressing the Run button. If the robot is moving into the center of the environment, you should note that the robot is moving forward and rotating. The Run button causes the the step method to be successively called. The step method in the initial WallFollow brain prints out some sensor values, and causes the robot to rotate at 1/5 speed, counter clockwise. Because the constructor was called, and then the step method is called, the robot both rotates and translates. When one movement method is called (e.g., translate or rotate), unless the robot.stop() method is called, the particular movement will keep on happening.

Now, press the Stop button. Both the translate and rotate behaviors will stop. Now press the Run button. Note that now only the rotate movement occurs. The Run button does not cause the constructor to be called again, and so with only the step method called, the robot just rotates.

As another familiarization step here, you should use the Reload, Stop, and Run buttons to cause the robot to move near to a wall. With the robot near a wall, press the Run button. Note the output in the Pyro window and the values that the Left, Front, and right sensors take on. When the sensor is near the wall, the value returned by the sensor drops to lower values.

Sensor Updating: A perhaps unfortunate property of the current pyrobotics sensors (at least with the Khepera simulation) is that it seems that the sensors only get updated before the step method is called. For example, the range sensors only get updated before a call to the step method in the robot brain. This makes certain solution strategies to this lab not useful. For examle, it is not useful to have your robot attempt to change heading in a loop, and poll the sensors for a change. The sensors will not change while the robot is in the loop, and thus you will be in an infinite loop! Instead, you should make small adjustments to the robot's behavior or movement with each call to the step method, and use the step method as a way to iteratively solve the problem.

The following pyrorobotics web page (see the bottom portion) gives a sketch of the Python program structure of a robot brain.

Designing your own robot controller (Brain)

Your programming goal in this lab is to modify the WallFollow.py Python code to actually perform wall following. What do I mean by wall following? Basically, your robot should continuously move around the perimeter of the environment, never getting "too far" away from the wall. It should also successively deal with corners in the environment, continuing to follow the wall. The following is a Quicktime movie (1.5 GB) of my implementation performing one kind of wall following. You may choose to have your robot implement wall following somewhat differently, and so your implementation may not look identical to mine. I am not saying, by any means, that my implementation of wall following is the "best" in any way.

You may find it useful to first focus on having your robot approach a long wall (e.g., not a corner) in a perpendicular manner. Once the robot nears the wall, have it stop, turn, and start to follow along the wall.

Once you have this working, next have your robot approach the wall from an angle (e.g., 30 degrees, approximately). Modify your code so that it still enable the robot to turn and follow the wall.

It is pretty easy to get your robot brain into infinite loops. Hitting the stop button does not always seem to cause these loops to terminate! It seems that sometimes you have to kill the Pyro simulator to clear this up, and restart the simulator.

I think you will find it easiest to approach this problem incrementally. Don't try to write a complete algorithm right away, but first write an algorithm that will solve the problem of approaching a wall in a perpendicular manner, get that algorithm running, then move on. This should help you incrementally gain an idea of what is required to program a solution to this problem.

Some Notes Using Python and Pyro

Indentation is insidious in Python. I would advise you to definitely not mix tabs and spaces. If you use spaces only (and have your text editor convert any tabs to spaces) you will always see the level of indentation properly. Indentation changes the meaning of the program code in Python, so beware!

It seems that the class name in your file must have the same name as your file. For example, my class name is called WallFollowing, and my file name is WallFollowing.py. I have run into some obscure looking errors without following this convention. I'm assuming the errors are coming from Pyro, but I may be wrong.

I am getting the strong impression that if you load your robot brain into the system and this first time loading has an error, then the Pyro system must be restarted. Under at least some conditions I have been unable to correct the error and reload the brain using an external text editor. This causes the effect that sometimes, when you attempt to reload a "Brain:", you may get the problem that Pyro does not actually load the new code from the Brain file (e.g., WallFollow.py). You may need to stop the main Pyro window and the World window, and restart the simulator.

Grading

I will grade for quality and innovation in wall following.

Your programs also need to adhere to good software engineering style for documentation and program writing and structure. In terms of documentation in your program, provide at least the following: 1) a header comment giving your name, the date, the course number, your TA's name, your instructor's name, and a description of the objective of the program, 2) comments at points in the program, especially description of what complicated sections of code do, and 3) documentation of the purpose of variables and constants when the name of the variable or constant is not fully self-documenting. Generally speaking, the use of global variables is considered to make programs more difficult to read and change and hence is not good software engineering style.

Submission

You must email to the TA your Python program code. You also need to submit hard copy (paper) of your code. All of this needs to be submitted on or before the due date.