The Controller
class is the brain of the Line-follower robot. It integrates color sensor and differential drive, and it makes the decisions that allow the robot to follow a line/path. We will rewrite the main LineFollower
class (inside LineFollower.java
) to simply create a Controller
object and hand over control to it.
Let's look at a basic (initial) implementation of Controller
to get an idea of what we are talking about.
We start with the modified LineFollower
class that simply hands control over to Controller
:
import lejos.hardware.port.MotorPort; import lejos.hardware.port.SensorPort; public class LineFollower { public static void main(String[] args) { Controller controller = new Controller(SensorPort.S1, MotorPort.B, MotorPort.C); controller.run(); } }
It is immediately obvious that this, the main class where execution of the program begins, has been drastically reduced in size and complexity. There is a single main()
method that is required in the main class of the project and is where execution actually begins. It contains only two statements.
The first statement creates a new object of type Controller
. The constructor takes three arguments where you specify the ports to which the color sensor, the left Large Motor, and the right Large Motor are attached respectively.
The second and the last statement takes the newly created Controller
object and executes its run()
method. This is designed to be a blocking call, that is, execution will remain inside the
run()
method until the robot is done with its task. When execution finally passes out of the method, the program will come to an end.
So we now know that Controller
should be initiated with the ports for the various components and that it should have a run()
method that should hold on to the execution until the robot has completely traversed the path.
We present a basic implementation of the Controller
object, which will allow the program to be compiled and executed but will not perform any meaningful actions (for now). The remainder of this chapter will consist solely of slowly evolving the Controller
class until we end up with a fully functional autonomous Line-follower robot:
import lejos.hardware.Sound; import lejos.hardware.port.Port; public class Controller { private ColorSensor sensor; private DifferentialDrive drive; public Controller(Port sensor_port, Port left_port, Port right_port) { log("Initializing Controller"); sensor = new ColorSensor(sensor_port); drive = new DifferentialDrive(left_port, right_port); } public void run() { log("Starting controller"); end(); } private void end() { Sound.beepSequence(); log("Program ends"); } private static void log(String msg) { System.out.println("log> " + msg); } }
The first two lines simply import objects that we use in the rest of the code. For now, these consist only of the Port
object that represents both Sensor and Motor ports on EV3 and the Sound
class that we will use to generate a sequence of beeps to denote the end of the program.
The first two lines inside the definition of the Controller
class:
private ColorSensor sensor; private DifferentialDrive drive;
These lines declare members (variables) of the types ColorSensor
and DifferentialDrive
. These will store the ColorSensor
and DifferentialDrive
objects we will create to access and control these components.
The constructor takes the three ports that are passed in and uses this information to create and store the ColorSensor
and DifferentialDrive
objects:
public Controller(Port sensor_port, Port left_port, Port right_port) { log("Initializing Controller"); sensor = new ColorSensor(sensor_port); drive = new DifferentialDrive(left_port, right_port); }
The constructor starts off with a log that indicates to the user (by printing the log message to the terminal) that we are initializing (creating) the Controller
object. It then goes on to create and store the ColorSensor
and DifferentialDrive
objects (using the ports passed in) in anticipation of using them later.
Next, we define the crucial run()
method:
public void run() { log("Running controller"); end(); }
Note that the run()
method is declared public
, since it needs to be accessible from outside the class (as is the constructor). All other methods are declared private
, since they are for internal use only. For our basic implementation, we simply print a log indicating that we are beginning the execution of the run()
method followed immediately to a call to the private end()
method:
private void end() { Sound.beepSequence(); log("Program ends"); }
The preceding method uses the LeJOS Sound
class and its associated beepSequence()
method to play a sequence of beeps (on EV3's speaker) to indicate to the user that the execution of the program is coming to an end. A log is also printed to the terminal to indicate the same.
So, when the project is compiled, transferred to EV3, and executed (which you can do in one go using Makefile
and issuing make run
), the program starts in the main()
method of the LineFollower
class. There, it creates a new Controller
object (by running the constructor from the Controller
class). This prints the Initializing Constructor
log and creates the ColorSensor
and DifferentialDrive
objects.
The second (and last) statement in main()
(inside LineFollower
) calls the run()
method of Controller
. This prints the Running Constructor
log and then calls the end()
method, which in turn plays the beep sequence on the speaker and prints the Program ends
log. The end()
method finishes, which allows the run()
method to finish. This, in turn, allows the main()
method to finish bringing the entire program to an end.
52.14.85.76