Thank you William for the prompt reply, your code was very much
appreciated.
The solution you provide is agonisingly close. After limited testing
(its quite late here now, so I will do some more testing in the
morning) it seems that if you have many operations in the action task
in the GoForward class then the program only responds to the touch
sensor being activated once it has finished all of the code in the
action task. I need the code to immediately branch to the action task
in the HitWall no matter how far in we are in the 'main' behaviour.
Is this possible?
The odd thing is, that when the touch sensor is activated the robot
seems to pause as if it recognises this, but it then just continues
executing the code in the GoForward behaviour.
Again this is after limited testing so it may well be my fault, I
will attach the two modified classes so you can see the kind of
effect I'm trying to see. (And see if its just my bad programming
causing the problem :p ).
Thanks again for your help
Gary Stoneman
William J Rust wrote:
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;
}
}
}
------------------------------------------------------------------------
No virus found in this incoming message.
Checked by AVG Free Edition.
Version: 7.1.385 / Virus Database: 268.4.0/305 - Release Date:
08/04/2006
------------------------------------------------------------------------
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 hit = new HitWall();
Behavior move = new GoForward();
Behavior[] behaviorArray = {move, hit};
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() {
//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 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() {
Motor.A.stop();
Motor.C.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() {
//Here is were we respond to the sensor
turnRight();
moveForward();
turnRight();
}
public void moveForward() {
Motor.A.forward();
Motor.C.forward();
try{Thread.sleep(1800);}catch(Exception e) {}
Motor.A.stop();
Motor.C.stop();
}
public void moveBackward() {
Motor.A.backward();
Motor.C.backward();
try{Thread.sleep(1800);}catch(Exception e) {}
Motor.A.stop();
Motor.C.stop();
}
public void turnRight() {
Motor.A.forward();
Motor.C.backward();
try{Thread.sleep(1183);}catch(Exception e) {}
Motor.A.stop();
Motor.C.stop();
}
public void turnLeft() {
Motor.A.backward();
Motor.C.forward();
try{Thread.sleep(1183);}catch(Exception e) {}
Motor.A.stop();
Motor.C.stop();
}
}
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() {
Motor.A.stop();
Motor.C.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() {
//This should always be run in a continuous loop <- which works
//However we only respond to a sensor after we have executed
all of this code
moveForward();
turnLeft();
turnLeft();
turnLeft();
moveForward();
//Down to here, then we respond
}
public void moveForward() {
Motor.A.forward();
Motor.C.forward();
try{Thread.sleep(1800);}catch(Exception e) {}
Motor.A.stop();
Motor.C.stop();
}
public void moveBackward() {
Motor.A.backward();
Motor.C.backward();
try{Thread.sleep(1800);}catch(Exception e) {}
Motor.A.stop();
Motor.C.stop();
}
public void turnRight() {
Motor.A.forward();
Motor.C.backward();
try{Thread.sleep(1183);}catch(Exception e) {}
Motor.A.stop();
Motor.C.stop();
}
public void turnLeft() {
Motor.A.backward();
Motor.C.forward();
try{Thread.sleep(1183);}catch(Exception e) {}
Motor.A.stop();
Motor.C.stop();
}
}
------------------------------------------------------------------------
// 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;
}
}
}