Attached are two classes, BehaviorMazeRunner and Arbitrator.
BehaviorMazeRunner is just what the name suggests, a maze runner program
that uses behaviors. Note that I don't have a sensor listener because
the arbitrator queries the sensors at about the same rate as the
listener thread does so using a listener doesn't buy you much. I also
noticed as I reviewed the program that I don't explicitly initialize the
sensors. That is a theoretical problem but I know that this program
works so it isn't a practical one. What you are describing seems like a
problem that a K-State professor had with Arbitrator (and which I just
replicated while testing to make sure that BehaviorMazeRunner more or
less works). When I tried BMR with the standard 2.1.0 Arbitrator, it
didn't loop. With the code I've attached, it does.
Good luck and happy trails,
wjr
Gary Stoneman wrote:
Hi,
I'm trying to program my robot to do some simple task. Basically all
the robot needs to do is run a set of ten instructions (consisting of:
move forward for 2 seconds, move backward, turn right, turn left) in a
loop for all eternity. However whilst doing this it needs a sensor
listener to determine when one touch sensor is activated. When this
happens another separate set of ten instructions should be run. After
these new instructions have been completed we return to executing the
first set of ten instructions.
So basically the robot will roam around using a random set of ten
instructions forever, until it hits something, then it runs a
different set of instructions once, then returns back to the first
block of instructions.
I have currently tried using behaviors, but i cannot get the 'main'
behavior to loop. If we take the simple bumper car example (in the
lejos tutorial) I effectively want to loop the DriveForward routine
indefinitely, but in such a way that we are continually looping
through instructions, so it may go forward, turn left, froward, turn
right and it just keeps looping. And of course in a way where it still
responds to the touch sensor being activated.
Can anybody offer some ideas on how to solve this problem (hopefully
I've explained myself well :-) ) ? Any help will be truly appreciated.
Many Thanks
Gary Stoneman
-------------------------------------------------------
This SF.Net email is sponsored by xPML, a groundbreaking scripting
language
that extends applications into web and mobile media. Attend the live
webcast
and join the prime developer group breaking into this new coding
territory!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642
_______________________________________________
Lejos-discussion mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/lejos-discussion
package MyRobot;
import josx.platform.rcx.*;
import josx.robotics.*;
public class BehaviorMazeRunner {
/**
* This maze runner works by using a technique called behaviors. A
* behavior is implemented as a Java class. This file contains 3
separate
* classes instead of the usual one class per file. The first class is
* BehaviorMazeRunner and, like the other maze runners, it simply
contains
* a main method so it is a program. The other two classes implement
the
* Behavior interface, which simply means that these classes implement
the
* methods as defined by Behavior, i.e. takeControl, suppress and
action.
* takeControl is a method called to determine if an instance of a
Behavior
* wants to run. For example, if a sensor is pressed indicating that
the
* robot has run into a wall then the Behavior that deals with running
into
* a wall wants to be called; its takeControl method would return
true. The
* action method is called after an instance of a Behavior has
returned
* true. That is, after the robot has run into a wall, the action
routine
* is called to respond to that event. suppress is called when a higher
* priority Behavior wants to take control. Priorities are assigned to
* Behaviors by the order they are listed when the behavior array is
* created, the leftmost Behavior having the lowest priority. Finally,
the
* lowest priority Behavior, in this case move, should always want to
take
* control since it will be interrupted when higher priority Behaviors
* want to take control.
*/
public static void main(String[] args) {
Behavior turnRight = new HitWall(HitWall.TURN_RIGHT);
Behavior turnLeft = new HitWall(HitWall.TURN_LEFT);
Behavior move = new GoForward();
Behavior[] behaviorArray = {move, turnLeft, turnRight};
Arbitrator arbitrator = new Arbitrator(behaviorArray);
arbitrator.start();
}
}
/**
* HitWall is a Behavior for a robot when it encounters a wall. The constructor
* takes an argument to set up for a wall on the right, TURN_LEFT, or a wall on
* the left, TURN_RIGHT.
*/
class HitWall implements Behavior {
public static int TURN_RIGHT = 1;
public static int TURN_LEFT = 2;
private int direction = TURN_LEFT;
HitWall(int direction) {
this.direction = direction;
}
/**
* A method required by the Behavior interface, the instance of the
* Behavior will take control iff the appropriate sensor is pressed.
*/
public boolean takeControl() {
return (direction == TURN_LEFT) ?
Sensor.S3.readBooleanValue() :
Sensor.S1.readBooleanValue();
}
/**
* A method required by the Behavior interface, the instance of the
* Behavior will stop the motors if is preempted by a higher priority
* Behavior.
*/
public void suppress() {
MyMotors.stop();
}
/**
* A method required by the Behavior interface, the instance of the
* Behavior will execute the action if the Behavior has taken control.
*/
public void action() {
MyMotors.goBackward(500);
if (direction == TURN_LEFT) {
MyMotors.turnLeft(500);
} else {
MyMotors.turnRight(500);
}
}
}
class GoForward implements Behavior {
/**
* A method required by the Behavior interface, the instance of the
* Behavior always returns true since this is the default Behavior.
That is,
* at least one Behavior must always be willing to run. That Behavior
must
* be the lowest priority Behavior because anything lower than the
default
* Behavior will never be asked if it wants to take control.
*/
public boolean takeControl() {
return true;
}
/**
* A method required by the Behavior interface, the instance of the
* Behavior will stop the motors if is preempted by a higher priority
* Behavior. Since this is the default Behavior, any other Behavior
taking
* control was cause this method to be called.
*/
public void suppress() {
MyMotors.stop();
}
/**
* A method required by the Behavior interface, the instance of the
* Behavior will execute the action if the Behavior has taken control.
*/
public void action() {
MyMotors.goForward(1000);
}
}// package josx.robotics;
package MyRobot;
import josx.robotics.*;
import josx.platform.rcx.*;
/**
* Arbitrator controls which behavior should currently be active in
* a behavior control system. Make sure to call start() after the
* Arbitrator is instantiated.
* @see Behavior
* @version 0.21 28-April-2003
*/
/**
* Modified by Masaaki Mizuno ([EMAIL PROTECTED]) and
* Letchumanan Muthaiah ([EMAIL PROTECTED])
* 15-May-2003
*
* This implementation is based on the time triggered design.
* In leJOS, each sensor can be registered with a listener object.
* A native thread (implemented inside the virtual machine)
* checks all the sensors (hardware) once every 3msec. When it has detected
* any change in the hardware state, it wakes up the listener thread, and the
* listener thread in turn invokes associated methods in the registered
* listener objects.
* Therefore, no sensor events arrive faster than once every 3msec.
* The Arbitrator object uses this fact to implement the time-triggered
* design. The primary thread (PT) (arbitrator thread) wakes up every 3msec
* to work through the takeControl() methods of the behavior objects.
*/
public class Arbitrator {
private Behavior [] behavior;
private final int NONE = 99;
private int totalBehaviors;
private int currentBehavior;
private BehaviorAction actionThread;
// arbitratorLock is used by the arbitrator thread to sleep
private Object arbitratorLock;
/**
* Allocates an Arbitrator object and initializes it with an array of
* Behavior objects. The highest index in the Behavior array will have the
* highest order behavior level, and hence will suppress all lower level
* behaviors if it becomes active. The Behaviors in an Arbitrator can not
* be changed once the arbitrator is initialized.<BR>
* <B>NOTE:</B> Once the Arbitrator is initialized, the method start() must be
* called to begin the arbitration.
* @param behavior An array of Behavior objects.
*/
public Arbitrator(Behavior [] behaviors) {
this.behavior = behaviors;
currentBehavior = NONE;
actionThread = new BehaviorAction();
arbitratorLock = new Object();
totalBehaviors = behavior.length - 1;
actionThread.start();
}
/**
* This method starts the arbitration of Behaviors.
* Modifying the start() method is not recomended. <BR>
* Note: Arbitrator does not run in a seperate thread, and hence the start()
* method will never return.
*/
public void start() {
while(true) {
// Check through all behavior.takeControl() starting at highest level
// behavior
for(int i = totalBehaviors; i >= 0; --i) {
if (behavior[i].takeControl()) {
if ((i > currentBehavior) || (actionThread.getCurrent() == NONE)) {
if (i > currentBehavior) behavior[currentBehavior].suppress();
currentBehavior = i;
actionThread.execute(i);
// because execute is a synchronized method,
// it is executed after the action thread
// has returned from action() and set current to NONE
break;
}
}
}
synchronized(arbitratorLock) {
try {
arbitratorLock.wait(3); // sleep 3 msec
}catch(InterruptedException e){}
}
}
}
/**
* This class handles the action() methods of the Behaviors.
* We call this thread the action thread
*/
private class BehaviorAction extends Thread {
private int current = NONE;
private int i;
public void run() {
while(true) {
synchronized(this) {
if (current != NONE) {
LCD.showNumber(current); LCD.refresh(); // debugging statement
behavior[current].action();
current = NONE;
}
}
Thread.yield(); // since Action thraead is not time triggered,
// it is necessary to yield.
}
}
public synchronized void execute(int index) {
// current is shared by both the arbitrator thead and the action thread
current = index;
}
public int getCurrent() {
// even though this accesses shared variable "current",
// this cannot be a synchornized method since while action() is executed
// the action thread holds "this" lock.
return current;
}
}
}