Date: 1.11.2007 - 8.11.2007
Duration of activity: 3+4 hours
List of participants: Alessandro, Daniela, Samuele
Note: we are writing a single lab report for two lab sessions because we couldn't complete the first lab assignment on time, so we continued the following session.
Goal of the days: "Many of the proposed experiments could be done with the Lego Mindstorms kit" - Valentino Braitenberg
First plan:
- Build again the same robot car (which we always destroy for some reason at the end of each lab session)
- Write the code for the Braitenberg Vehicle 2a
- Test it
- Invert left and right light sensors cables, so we have Vehicle 2b
- Test it
We used old light sensors, since we only have one NXT light sensor.
We didn't write a normalization with a max and min light, but only converted the light signal into the same range of the motor proportionally.
Phase 3, test:
The motors moved, but the robot didn't react to any light change. Then we remembered that old light sensor have a very tight actual range of values.
With a small program we then got the raw readings from the sensors.
minimum (ambient) light: 50%
maximum (with a lamp in front of it) light: 60%
But then, we discovered an unexpected result from an error. We forgot to passivate sensors, and obtained a much wider range:
minimum: 25%
maximum: 65%
We thought that the red led should have been used only for distinguishing colors even with low ambient light, but then we discovered that even if the red light doesn't reach any near object, it amplifies the range of ambient light.
New plan:
- using light sensors in active mode
- now that we understood why it is useful, write a normalization function, with a fixed max and min value
- test if the robot moves this time
- implementing the dynamic max and min threshold update (we have to decide how long lasting should thresholds be)
- test again
Normalization:
first of all we transform the value read from the light sensor in the motor scale.
lightValue * MAX_SPEED / MAX_LIGHT
but, since we got only values starting from around 40, before applying the proportion, we apply also a translation to the light values, with the new 0 at MIN_LIGHT
(lightValue - MIN_LIGHT) * MAX_SPEED / (MAX_LIGHT - MIN_LIGHT)
Then, as we want our robot to move even when it only sees darkness, we add a fixed MIN_SPEED value to the resulting speed, and in order to do so, we reduce the space in which we project light readings
MIN_SPEED + (lightValue - MIN_LIGHT) * (MAX_SPEED - MIN_SPEED) / (MAX_LIGHT - MIN_LIGHT)
Now the robot behaves in a nice way. While testing the Braitenberg Vehicle 2b, it drives toward light, though it has a small range of sight, and often it misses the light.
Then we got to the dynamic threshold algorithm.
We had to force an explicit sleep (20 ms) in sampling threads (which we didn't include at first), otherwise we would have got too many samples.
We implemented a circular queue, which holds every sampled value, up to N, and then starts to discarding old values. We set the size of the queue as
N = MEMORY_TIMEOUT / THREAD_SLEEP,where MEMORY_TIMEOUT (5000 ms in our tests) is the maximum life of a threshold value.
In a previous implementation we recalculated thresholds, iterating through the whole queue, at each sensor sampling. Then we thought that it's more efficient to recalculate max and min thresholds only when either the current max or min value is removed from the queue.
Our implementation worked, without a sensible slowing, but in practice its memory was actually longer than we thought. Since it is a good approximation anyway, and the delay is probably related to the way the lejos JVM manages thread, we chose to not investigate further, and leave it as it is.
Last plan for lecture 7
- Move to a Multi-threading implementation
We isolated the code for the connection between a single light sensor and a single motor. This code has been then put inside a subclass of Thread.
The main program looked like
new BraitenbergConnection(sensorLeft, motorRight).start();This update made the code more readable and modular, but it didn't affect the overall behaviour of the robot, even if presumably the JVM does not implement a perfect parallelization of the process.
new BraitenbergConnection(sensorRight, motorLeft).start();
Lesson 8 plan:
- Read the assignment, which has a few questions
- Test and analyze the BehaviourTest program
- Refactor the previous lab session code, according to Rodney Brooks' subsumption architecture
>>What is the purpose of making these threads daemon threads?
We read the lejos doc and made a quick test: if we don't, when the main method terminates other threads keep running. And by default, threads are not daemons.
>>How is this boolean used in Locomotion.java? And how would you describe the delay method in Behaviour.java used by the behaviour threads?
In the subsumption architecture paradigm, inputs are inhibited and outputs are suppressed.
The boolean variable is used inside the delay function to suppress the input (i.e. replacing the "wakeup signal" of the returning of the delay method), and inside Locomotion functions to inhibit the output. (Yeah, in this case it is pretty much the same thing).
>>How is the suppression mechanism used in Behaviour.java to obtain controlled access to the motors? Could you think of situations where this method fails?
Every access to the motors is contained between the suppression and the unsuppression of any module that the current module can suppress. Then, if some other [more important] Behaviour needs to access the motors, that Behaviour must suppress modules which are currently using the motors.
If the modules are hierarchically ordered, it should never happen that it fails. If they are ordered in a graph-like shape, then concurrent Behaviours could be acting. In that case it should be assured that it can never happen that two concurrent modules require the same resource at the same time.
Having already adapted the Braitenberg connection between a sensor and a motor inside a single thread, we just added the Behaviour class as parent class, and adapted the code a bit, and we got a "Drive toward light" Behaviour, actually made up by two concurrent Behaviours (accessing half of the resources each).
We then rewrote the main method, according to this architecture:

It worked. Our robot now wanders around, and follows the light when it sees it. It avoids obstacles, even small ones, and plays an annoying sound every 10 seconds.
The only thing that could be fixed is that the PlaySound has no sense to be there. It should not have that high priority, which avoids sometimes the robot to correctly drive away from obstacles, other than being annoying. But we would have too few modules to make a graph with, so it's fine as it is.

Since the robot worked really fine, and showed a really precise light following behaviour, then we started to play with it and forgot about further experiments, so this post ends here.
No comments:
Post a Comment