Wednesday 24 November 2010

Behavioural Therapy

Starting up:
Participants: Johan & Jakob
Date: 18/11-2010
Duration: 6 Hours
Goal: Investigate how LeJOS NXJ implements a behaviour-based architecture with the subsumption API, with specific focus on the the interface lejos.subsumption.Behaviour and the class lejos.subsumption.Arbitrator.
Plan: This weeks programming exercise differs from the previous weeks, by having a very specific set of tasks.
We intend to solve these individual exercises and investigate further using a recent checkout of the LeJOS sourcecode.


Experiments:

Getting BumperCar running on the NXT was a trivial task and afterwards we were able to conduct the following experiments. 


Keeping the touch sensor held in:
When the touch sensor is pressed the DetectWall behaviour takes control and the robot turns to avoid the object. When the sensor button is kept down (or the sound sensor detects something close) the behaviours' takeControl() method continuously returns true thereby making the robot turn around itself over and over.
We must make note here that we were extremely confused at first at the robots behaviour, until we noted that the UltraSonic sensor was attached and had an influence on the actual behavior of the robot.


Properties of the Arbitrator regarding the running of takeControl() methods
As is seen in [2:line 121 through 129] the Arbitrator uses a for loop to select which behaviour will gain control by having its action() method called.
The arbitrator runs through its behaviour list from highest to lowest priority. When a behaviours' takeControl() method returns true, that behavior assumes control and the loop breaks. This means that as long as the DetectWall behaviours' takeControl() method returns true (meaning that the touch sensor is pressed or the SoundSensor detects an obstacle), the takeControl method of the behaviour DriveForward is never run.
This holds true for any ordering of any number of behaviours, the takeControl() methods of the lower priority behaviours will never be run when a higher priority behaviour becomes active.


Adding a behaviour to enable exit by keypress
In [1] we have added a behaviour called Exit that contains a takeControl() method that returns true on keypress and an action() method that exits the system.
This Behaviour can be seen below.



class Exit implements Behavior {
    public boolean takeControl() {
return Button.ESCAPE.isPressed();
    }
    public void suppress() {
//The behavior kills the program so if activated suppress wont matter.
    }
    public void action() {
System.exit(0);
    }
}

A suppress method is deemed unnecessary as the action method unconditionally terminates the program.
When the ESCAPE buttons pressed while the DriveForward behavior is active the program shuts down instantly due to the fact that the DriveForward behavior does not block the program. When the ESCAPE button is pressed the DriveForward behavior is suppressed and the Exit behavior kills the program. 


The DetectWall behavior is forced to block the program in order to assure that its motor rotation calls are not overwritten by the DriveForward behavior. This causes a delay when pressing the ESCAPE button, as the behavior guarantees a correct rotation before yielding control.


The Sound.pause(int x) call in the takeControl() method of the behavior DetectWall makes the entire behavior selection in the arbitrator slower by x-milliseconds because the loop cannot continue until the takeControl() method returns a boolean. This restricts the number of behavior changes possible each minute and makes the robot less responsive since the value the sensors return are only relevant immediately after the Sound.pause(int x) call. This also makes the Exit behavior less responsive since it has to wait for the next behavior selection loop before being able to execute. This can potentially also result in loss of input, for instance if the ESCAPE button is pressed while the takeControl() method of the behaviour DetectWall is running.
When the input of sound.pause() was increased to 2000 ms the robots behaviour became almost useless.
Each time the arbitrator tried to select an active thread there now was a blocking call lasting 2 seconds.
This of course results in a very unresponsive behaviour as the entire system is locked up while waiting for the loop to check for the data.


Using a local thread to avoid blocking call
We now implemented a thread in the behaviour detectWall to be able to continuously optain values from the sensor without blocking the system. This can be seen in [1].
This eliminated the need for the sound.pause(20) in the takeControl behaviour and should thusly make the Arbitrator run more smoothly. The 20 miliseconds is such a small value though, and we did not see any noticable effect from this change. 
Regarding this question we noticed something interesting, in [ref til bumpercar fra nyeste lejos checkout ), that is, the bumperCar example taken from the developer repository via svn, the call sound.pause(20) is commented out.
We also tried this approach and noted no difference in behaviour. Why the line is commented out rather than removed is anyones guess.


Backing up for a full second, before turning
For this rather uninspired exercise we utilized Sound.paused(1000) to wait a second while backing up before turning.


Interrupting a running behaviour
This part of the exercise regards restarting a running behaviour due to the same condition triggered, as caused the  action to run in the first place. This caused us quite a bit of trouble figuring this out, as the question asks us to implement the behaviour Detectwall in such a way that it can be interrupted and reset by the Arbitrator. This assumes that the Arbitrator runs the takeControl() method of the behaviour, get a true value as return value, and there suppresses the current Behaviour and calls the action() method of the new intended behaviour, but as can be seen in [2], the new behaviour only gets to run if it has a strictly higher priority than that of the active behaviour, and as such our Arbitrator model does not support the implementation of such a Behaviour.


Motivation functions:


First it must be noted that the behaviour framework as described in [4] is not suitable for this task, similarly to the last exercise, a behaviour based system is best geared towards an interaction model where you can interchange the different behaviours at any given time, disregarding the state the current behaviour is in at the time of switch.
Nothing more than the suppress call should be needed to make it possible for a smooth transition.


If the touch sensor is pressed again while turning it would still not make sense to reactivate the action. Since the rotation hasn't completed we do not know whether the direction the robot will be pointing after the turn is towards the object which it pressed against or if it is pointing in a completely different direction and therefore does not need further turning. Another action dictating backing off a little before completing the turn might be more suitable.


Let the integer range 0-100 define our motivation values where 0 is the least motivating and 100 the most motivating. We would obviously reserve the Exit behavior to be the most important and it is therefore the only behavior which is allowed to use motivation 100 (since we don't want to wait for our robot to exit.) If we then look at our takeControl methods we would then have them return an integer which represents the importance of the behavior based on the situation. Meaning that if our touch sensor registers an input it is important to start turning and if it doesn't it is very unimportant, returning a high and low motivation respectively. The driving forward behavior would then return an average integer, so it can both be overwritten and seize control easily.



Conclusions and further work:
From this labsession we have gotten the knowledge that Behaviour based models isn't fit for every task in robotics.
Some of the exercises left us trying to figure out how to make the screw work with the hammer, which wasn't an easy task.
In the future, more time could be spent actually trying to implement the integer returning takeControl() methods with a new Behaviour interface, and an Arbitrator that would be able to handle these return values.
This would allow for a significantly more dynamic Behaviour selection.

References:
  1. http://daimi.au.dk/~fx/dLego/BumperCar.java - The modified BumperCar.java
  2. http://daimi.au.dk/~fx/dLego/Arbitrator.java  - The Arbitrator.java from developer checkout of LeJOS source
  3. http://daimi.au.dk/~fx/dLego/Behaviour.java - The Behaviour.java from developer checkout of LeJOS source
  4. http://legolab.daimi.au.dk/DigitalControl.dir/Krink.pdf

Monday 15 November 2010

Finding your place in the world

Starting up:
Participants: Johan & Jakob
Date: 11/11-2010
Duration: 6 Hours
Goal: Experiment with different approaches regarding calculating the position of the robot


Out-of-the-box
In the current version of Lejos ( 0.85 beta ) the package nxt.robotics, provides among other things SimpleNavigator and TachoPilot. SimpleNavigator allows the programmer to control the robot in terms of a cartesian coordinates, if given a pilot class. The pilot class gives the navigator a more abstract control over the robot. The two tables below gives the distance from the starting point to the end point of a four-step route with the robot starting in (0,0) and trying to stop in (0,0). 
Wheel diameter(mm):Error(cm):
81.650
81.665
81.680
81.675
49.615
49.622
49.613
49.67
49.67


As is seen from the above, the smaller wheel size actually did quite well with regards to returning to the robots point of origin, while the big wheels caused the robot to be so bad at returning to its starting position, that an external observer would not have been able to determine that returning to the beginning was the intended behaviour of the robot. We attribute this to the fact that even a tiny error in the motor usage can trigger a large discrepancy of the course when actuated with larger wheels attached.
Regarding the difference in the robots ability to determine distance versus its aptness with regard to do precise turns, this can also be attributed to the same issue. Especially with the large wheels, this is accentuated, but even with the smaller wheels attached, a slight error in angle, will over distance grow to a large error regarding distance from the outset. This only becomes truer with regard to turning as the changes in motorrotation to turn is quite more minute that the more rough movement necessary just to go straight.
We had the added problem of being unable to get the robot to drive in straight lines as it curves a bit to the right when trying to go straight, we attribute this to poor construction and more significantly differences in motors. We tried with a few different motors, and where able to get a bit better results, but it is still a possible course that can be taken to improve further upon the robots general performance.


From A to B ( via Z?)
The challenge now is to propose a solution that takes care of obstacles while navigating the world as a simple cartesian coordinate system. Concretely that means that you must be able to use goTo(x,y) and even though there exists an obstacle between your current position and (x,y) end up at (x,y). In earlier weeks we have built wall-followers, line followers and more generic avoidance bots. We have devised a very basic strategy building on the fact that as long as we only use goTo(x,y) calls rather than interact directly with the motors the state is consistent with the robots actual position, if not absolutely then at least as good as we generally can maintain the state. Thusly we can for each goTo(x,y), when detecting an object ( via touch, ultrasonic or more arcane means ), go off on some non-parellel vector from our current direction, and then when satisfied call goTo(x,y) again. We have tried to illustrate this approach on this picture:


This approach should work, given that we at all times can obtain the vector between our current position and the desirable end position.


Better sensing

In [2] they use "states" to differentiate between turning and moving straight forwards. When moving straight the average of the encoders are used to determine the distance traversed by the robot. This gives a certain error margin which grows as the bot travels further if the wheels or motors are not correctly calibrated or have defects. When turning the method utilizes trigonometric calculations to calculate a new heading based on the number of counts on each wheel, this method (assuming the wheels do not slip) is quite accurate.

The kinematics approach uses both counters constantly and does not differentiate between moving and turning. By calculating a new position for each step it is able to make smooth turns and because it does not assume that moving straight forward equals the average count of each wheel, it is better able to keep track of its position. It also, of course, assumes that the wheels do not slip on the surface, as all the calculations are based around the number of counts on each wheel.

Because the kinematics approach does not use as many assumptions, it is more accurate when determining the position of the robot.

Based on the observations from testing the lejos navigation system we believe that the system is very similar to the algorithm in [2], since the robot sometimes diverged due to crooked tires and it made no attempt to compensate for this on the return trip. We believe that using the kinematics solution would have given the robot a more accurate position of itself, thereby enabling it to compensate for the error on the return trip.



References