Bot-kit - having trouble using light sensors

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Bot-kit - having trouble using light sensors

Mike Richards
We're currently newbies to Smalltalk, so go easy. We're programming the RCX
brick using Dolphin Smalltalk 2.x and the associated Bot-kit. We want to use
a light sensor to return a numeric value; if the value passes a threshold,
the robot will perform an action. However, we're getting inconsistent
results from the lightSensor object.

Our robot has a light sensor attached to port 2 of the brick.

We are sub-classing from LineFollowingBug (which we know works) - not good
Smalltalk, but we're having trouble and we want to keep things simple.

Our class definition is as follows:

LineFollowingBug subclass: #ProximityBug
    instanceVariableNames: 'lightSensorLastLevel lightSensorThreshold
collided'

    classVariableNames: ''

    poolDictionaries: ''

Our initialization is as follows:

    initialize

    super initialize.

    lightSensorLastLevel := 0.

    lightSensorThreshold := 800.

    collided := #false.

    lightSensor := (rcx lightSensor: 2) beRaw.

(Keep an eye on lightSensor - this is causing the trouble)

There is an accessor method for lightSensor in the parent class
(LineFollowingBug), which we know works for that class.

In a workspace we define a robot as follows:

    bug := ProximityBug new.

If we inspect the bug (running in immediate mode) we can examine the value
of lightSensor and see values like 747 - GREAT!

In our program we want to compare the value from the light sensor with the
current values of two instance variables:

    checkSignal

    "detect IR signal bouncing off wall and take action"

        lightSensorLastLevel := lightSensor.

        (lightSensor > (lightSensorLastLevel + lightSensorThreshold))
ifTrue: [

        collided := #false.

        self stop].

Returning to the workspace, if we use dummy variables like below:

    (bug lightSensor > (0 + 9))

We get:

    a RCXCondition(a RCXLightSensor(2=747) > a RCXLiteral(9)=true)

Which is correct!

But if we try to run the entire program in retained mode (autonomous
operation), Smalltalk produces a walkback entitled 'RCXLightSensor does not
understand #+' which is being raised in the method checkSignal (see above
for code).

Examining the walkback we see that the problem seems to lie in Smalltalk
trying to add the lightSensor object to two numeric values. the lightSensor
has the value a RCXLightSensor(2=unknown) - which is not consistent with the
behaviour in immediate mode.

If we then try to create a new ProximityBug in the workspace and examine its
properties, its lightSensor will also report 'unknown' values.

*****

Has anyone seen this before?

Is it a simple Smalltalk error on our part?

Are we not accessing the lightSensor object correctly?

Or is it something else?

Thoughts, code, anything are gratefully received.

Best wishes,

Mike.


Reply | Threaded
Open this post in threaded view
|

Re: Bot-kit - having trouble using light sensors

Ian Bartholomew-4
Mike,

I've never used the Lego "stuff" so bear that in mind while reading this.

I would think there are two problems -

You are accessing the lightSensor directly in the code where as your
workspace example code is using the accessor. If the superclasses
implementation of #lightSensor does more than just answer the RCXLightSensor
itself, performs a scan of the input for example, this could explain the
error. You should access #lightSensor by using an accessor rather than
directly i.e.

       x := self lightSensor

rather than

    x := lightSensor

The logic of your code is possibly wrong. Assuming two successive calls to
#lightSensor will answer the same, or very similar, values then the code you
gave for #checkSignal is equivalent to (using 5 as the lightSensor value)

    lastLightSensorValue := 5.
    5 > (lastLightSensorValue + 800)
        ifTrue: [....

which, unless there is something sneaky going on, will never evaluate to
anything other than false.

I guess that your code is waiting till the _change_ in the light level
between two successive reading is greater than 800. If so then try something
like -

checkSignal
    | current |
    current := self lightSensor.
    current > (lightSensorLastLevel + lightSensorThreshold)
        ifTrue: [
            collided := #false. "should be #true?"
            self stop].
    lightSensorLastLevel := current

Regards
    Ian


Reply | Threaded
Open this post in threaded view
|

Re: Bot-kit - having trouble using light sensors

Andy Bower
In reply to this post by Mike Richards
Mike,

I think your problems are caused by the differences between using Bot-Kit in
Immediate and Retained. mode. In Immediate mode (where the program runs in
Dolphin on the PC and transmits commands to the robot by infrared) you can
use Smalltalk code pure and simple. I imagine your code might work correctly
in Immediate mode, although the use of infrared for controlling a roving bot
is a little unrealistic since you are liable to run into "out of sight"
problems fairly easily.

However, you are using Retained mode (where the program is coverted to RCX
code and downloaded to the brick to be run autonomously by the "Spirit"
interpreter). In this mode you can only use a subset of Smalltalk and you
have to be quite careful how you go about things. This is hinted at in the
Bot-Kit manual at:

http://www.object-arts.com/Bower/Bot-Kit/Documentation/BuildingANewRobot.htm

The reason for the limitations is that, in this mode, you are not executing
real Smalltalk inside the bot but, instead, the program is converted to
bytecodes that are understood by the RCX interpreter in firmware. In
particular this has:

1) Only 32 global variables
2) No stack local variables
3) Limited expressions allowed between variables, sensors and constants so
complex expressions such as a>(b+c) are not allowed.

Your program will need to be modified to use RCX variables in a number of
places. For example. your variables, lightSensorLastLevel and collided, need
to be set up to use variables allocated in the RCX. The manual mentions that
this initialization should be done in the #initialize method so in your
outermost program method (the one mentioned in #defaultProgram - call it
#myProgram) you should have:

myProgram
    lightSensorLastLevel := self newVariable set: 0.
    collided := self newVariable set: 0.

See LineFollowingBug>>followLine for an example.

I take it that your variable called lightSensorThreshold is really a
constant, in which case you don't need to allocate a variable to hold the
value (although you could). Instead just create a method to answer the
threshold value:

lightSensorThreshold
    ^800

You can then use "self lightSensorThreshold" whereever you originally
referred to the lightSensorThreshold variable.

Now your #checkSignal method becomes:

checkSignal
    "detect IR signal bouncing off wall and take action"

    lightSensorLastLevel set: lightSensor.
    lightSensorLastLevel += self lightSensorThreshold.
    (lightSensor > lightSensorLastLevel ) ifTrueDo: [
        collided beFalse.
        self stop].
    lightSensorLastLevel -= self lightSensorThreshold.

Alternatively you could use a temporary variable to accumulate the last
level plus threshold so you don't need to subtract it again at the end.

Anyway, this means that it's a lot harder (and more confusing) to write
Retained mode programs rather than Immediate mode ones. This is the main
reason why I actually prefer to mess around with the Cybermaster Lego set
rather than the Mindstorms set. The Cybermaster has a radio link rather than
infrared so it is much more feasible to write immediate mode programs that
look like "real" Smalltalk. For a comparison of the two sets see:

http://www.object-arts.com/wiki/html/Lego-Robotics/MindStormsAndCyberMasterC
omparison.htm

I hope this reply has been helpful.

Best Regards,

Andy Bower
Dolphin Support
http://www.object-arts.com
---
Are you trying too hard?
http://www.object-arts.com/Relax.htm
---





"Mike Richards" <[hidden email]> wrote in message
news:9nat2i$5c9$[hidden email]...
> We're currently newbies to Smalltalk, so go easy. We're programming the
RCX
> brick using Dolphin Smalltalk 2.x and the associated Bot-kit. We want to
use

> a light sensor to return a numeric value; if the value passes a threshold,
> the robot will perform an action. However, we're getting inconsistent
> results from the lightSensor object.
>
> Our robot has a light sensor attached to port 2 of the brick.
>
> We are sub-classing from LineFollowingBug (which we know works) - not good
> Smalltalk, but we're having trouble and we want to keep things simple.
>
> Our class definition is as follows:
>
> LineFollowingBug subclass: #ProximityBug
>     instanceVariableNames: 'lightSensorLastLevel lightSensorThreshold
> collided'
>
>     classVariableNames: ''
>
>     poolDictionaries: ''
>
> Our initialization is as follows:
>
>     initialize
>
>     super initialize.
>
>     lightSensorLastLevel := 0.
>
>     lightSensorThreshold := 800.
>
>     collided := #false.
>
>     lightSensor := (rcx lightSensor: 2) beRaw.
>
> (Keep an eye on lightSensor - this is causing the trouble)
>
> There is an accessor method for lightSensor in the parent class
> (LineFollowingBug), which we know works for that class.
>
> In a workspace we define a robot as follows:
>
>     bug := ProximityBug new.
>
> If we inspect the bug (running in immediate mode) we can examine the value
> of lightSensor and see values like 747 - GREAT!
>
> In our program we want to compare the value from the light sensor with the
> current values of two instance variables:
>
>     checkSignal
>
>     "detect IR signal bouncing off wall and take action"
>
>         lightSensorLastLevel := lightSensor.
>
>         (lightSensor > (lightSensorLastLevel + lightSensorThreshold))
> ifTrue: [
>
>         collided := #false.
>
>         self stop].
>
> Returning to the workspace, if we use dummy variables like below:
>
>     (bug lightSensor > (0 + 9))
>
> We get:
>
>     a RCXCondition(a RCXLightSensor(2=747) > a RCXLiteral(9)=true)
>
> Which is correct!
>
> But if we try to run the entire program in retained mode (autonomous
> operation), Smalltalk produces a walkback entitled 'RCXLightSensor does
not
> understand #+' which is being raised in the method checkSignal (see above
> for code).
>
> Examining the walkback we see that the problem seems to lie in Smalltalk
> trying to add the lightSensor object to two numeric values. the
lightSensor
> has the value a RCXLightSensor(2=unknown) - which is not consistent with
the
> behaviour in immediate mode.
>
> If we then try to create a new ProximityBug in the workspace and examine
its

> properties, its lightSensor will also report 'unknown' values.
>
> *****
>
> Has anyone seen this before?
>
> Is it a simple Smalltalk error on our part?
>
> Are we not accessing the lightSensor object correctly?
>
> Or is it something else?
>
> Thoughts, code, anything are gratefully received.
>
> Best wishes,
>
> Mike.
>
>
>


Reply | Threaded
Open this post in threaded view
|

Bot-Kit Limitations? (was Re: Bot-kit - having trouble using light sensors)

Mike Richards
Thanks Andy, that explains a lot. I think we now know most of the
limitations of using a subset of Smalltalk in the bot-kit, but we are
observing a problem that we think might be due to a program size limitation
or something similar.  When we try to compile the rcx bytecode with
createProgam, it fails near the end of
this method:

hitSomething

    "commmon code if either right or left bumper hit something"

    collided := 1.
    lightSensorThreshold /= 2.
    self backward.
    self sleep: 200.
    self forward.



The method forward is inherited from BugBot and the code was irregularly
failing to compile to bytecode at the fullPower method because it didn't
seem to have anArrayOfMotors.  We were able to make it work by moving the
self forward outside this method to the method that invoked hitSomething.
However, adding more code seems to have caused it to fail again after.  It
can compile bytecode to send the first motor forward, but not the second
(doesn't matter if it is left or right, if we reverse the order in the code
it always fails on the second motor forward).

On another point, our code needs to send IR pulses from the robot so the
light sensor can try to detect it (we are trying to implement the Proximity
sensing robot from http://www.generation5.org/aisolutions/rob00.shtml). The
code we are using is:

sendSignal

    "send IR signal out"

    self rcx booleanSpiritCall: [self rcx spirit SendPBMessage: 0 Number:
0 ]. "Send and IR Message pulse 0"
    self sleep: 10.
    self rcx soundBip.



Is this the right booleanSpiritCall ?  Looking at the RCX LCD, it looks like
it only sends out one IR pulse when the robot starts up if you believe the
IR indicator.  We have this in a whileTrueRepeat loop in the "main" method
invoked from defaultProgram:

lookForWalls

    "start going forward, send out IR and look for it bouncing back"

    lightSensorLastLevel := self newVariable set: 0.
    stopRobot := self newVariable set: 0.
    lightSensorThreshold := self newVariable set: 800.
    collided := self newVariable set: 0. "Boolean to look for collisions -
initialise to 0 (False)."

    self forward.
    (stopRobot = 0) whileTrueRepeat: [
        self sendSignal.
        self checkSignal].

I don't think we're actually detecting an IR pulse, we are just halving our
threshold for recognizing it repeatedly until it hits zero!

As always, we're grateful for any light you can shed.

Mike (& Blaine)

"Andy Bower" <[hidden email]> wrote in message
news:9nifj6$7r6pq$[hidden email]...
> Mike,
>
> I think your problems are caused by the differences between using Bot-Kit
in
> Immediate and Retained. mode. In Immediate mode (where the program runs in
> Dolphin on the PC and transmits commands to the robot by infrared) you can
> use Smalltalk code pure and simple. I imagine your code might work
correctly
> in Immediate mode, although the use of infrared for controlling a roving
bot

> is a little unrealistic since you are liable to run into "out of sight"
> problems fairly easily.
>
> However, you are using Retained mode (where the program is coverted to RCX
> code and downloaded to the brick to be run autonomously by the "Spirit"
> interpreter). In this mode you can only use a subset of Smalltalk and you
> have to be quite careful how you go about things. This is hinted at in the
> Bot-Kit manual at:
>
>
http://www.object-arts.com/Bower/Bot-Kit/Documentation/BuildingANewRobot.htm
>
> The reason for the limitations is that, in this mode, you are not
executing

> real Smalltalk inside the bot but, instead, the program is converted to
> bytecodes that are understood by the RCX interpreter in firmware. In
> particular this has:
>
> 1) Only 32 global variables
> 2) No stack local variables
> 3) Limited expressions allowed between variables, sensors and constants so
> complex expressions such as a>(b+c) are not allowed.
>
> Your program will need to be modified to use RCX variables in a number of
> places. For example. your variables, lightSensorLastLevel and collided,
need
> to be set up to use variables allocated in the RCX. The manual mentions
that

> this initialization should be done in the #initialize method so in your
> outermost program method (the one mentioned in #defaultProgram - call it
> #myProgram) you should have:
>
> myProgram
>     lightSensorLastLevel := self newVariable set: 0.
>     collided := self newVariable set: 0.
>
> See LineFollowingBug>>followLine for an example.
>
> I take it that your variable called lightSensorThreshold is really a
> constant, in which case you don't need to allocate a variable to hold the
> value (although you could). Instead just create a method to answer the
> threshold value:
>
> lightSensorThreshold
>     ^800
>
> You can then use "self lightSensorThreshold" whereever you originally
> referred to the lightSensorThreshold variable.
>
> Now your #checkSignal method becomes:
>
> checkSignal
>     "detect IR signal bouncing off wall and take action"
>
>     lightSensorLastLevel set: lightSensor.
>     lightSensorLastLevel += self lightSensorThreshold.
>     (lightSensor > lightSensorLastLevel ) ifTrueDo: [
>         collided beFalse.
>         self stop].
>     lightSensorLastLevel -= self lightSensorThreshold.
>
> Alternatively you could use a temporary variable to accumulate the last
> level plus threshold so you don't need to subtract it again at the end.
>
> Anyway, this means that it's a lot harder (and more confusing) to write
> Retained mode programs rather than Immediate mode ones. This is the main
> reason why I actually prefer to mess around with the Cybermaster Lego set
> rather than the Mindstorms set. The Cybermaster has a radio link rather
than
> infrared so it is much more feasible to write immediate mode programs that
> look like "real" Smalltalk. For a comparison of the two sets see:
>
>
http://www.object-arts.com/wiki/html/Lego-Robotics/MindStormsAndCyberMasterC

> omparison.htm
>
> I hope this reply has been helpful.
>
> Best Regards,
>
> Andy Bower
> Dolphin Support
> http://www.object-arts.com
> ---
> Are you trying too hard?
> http://www.object-arts.com/Relax.htm
> ---
>
>
>
>
>
> "Mike Richards" <[hidden email]> wrote in message
> news:9nat2i$5c9$[hidden email]...
> > We're currently newbies to Smalltalk, so go easy. We're programming the
> RCX
> > brick using Dolphin Smalltalk 2.x and the associated Bot-kit. We want to
> use
> > a light sensor to return a numeric value; if the value passes a
threshold,
> > the robot will perform an action. However, we're getting inconsistent
> > results from the lightSensor object.
> >
> > Our robot has a light sensor attached to port 2 of the brick.
> >
> > We are sub-classing from LineFollowingBug (which we know works) - not
good

> > Smalltalk, but we're having trouble and we want to keep things simple.
> >
> > Our class definition is as follows:
> >
> > LineFollowingBug subclass: #ProximityBug
> >     instanceVariableNames: 'lightSensorLastLevel lightSensorThreshold
> > collided'
> >
> >     classVariableNames: ''
> >
> >     poolDictionaries: ''
> >
> > Our initialization is as follows:
> >
> >     initialize
> >
> >     super initialize.
> >
> >     lightSensorLastLevel := 0.
> >
> >     lightSensorThreshold := 800.
> >
> >     collided := #false.
> >
> >     lightSensor := (rcx lightSensor: 2) beRaw.
> >
> > (Keep an eye on lightSensor - this is causing the trouble)
> >
> > There is an accessor method for lightSensor in the parent class
> > (LineFollowingBug), which we know works for that class.
> >
> > In a workspace we define a robot as follows:
> >
> >     bug := ProximityBug new.
> >
> > If we inspect the bug (running in immediate mode) we can examine the
value
> > of lightSensor and see values like 747 - GREAT!
> >
> > In our program we want to compare the value from the light sensor with
the

> > current values of two instance variables:
> >
> >     checkSignal
> >
> >     "detect IR signal bouncing off wall and take action"
> >
> >         lightSensorLastLevel := lightSensor.
> >
> >         (lightSensor > (lightSensorLastLevel + lightSensorThreshold))
> > ifTrue: [
> >
> >         collided := #false.
> >
> >         self stop].
> >
> > Returning to the workspace, if we use dummy variables like below:
> >
> >     (bug lightSensor > (0 + 9))
> >
> > We get:
> >
> >     a RCXCondition(a RCXLightSensor(2=747) > a RCXLiteral(9)=true)
> >
> > Which is correct!
> >
> > But if we try to run the entire program in retained mode (autonomous
> > operation), Smalltalk produces a walkback entitled 'RCXLightSensor does
> not
> > understand #+' which is being raised in the method checkSignal (see
above

> > for code).
> >
> > Examining the walkback we see that the problem seems to lie in Smalltalk
> > trying to add the lightSensor object to two numeric values. the
> lightSensor
> > has the value a RCXLightSensor(2=unknown) - which is not consistent with
> the
> > behaviour in immediate mode.
> >
> > If we then try to create a new ProximityBug in the workspace and examine
> its
> > properties, its lightSensor will also report 'unknown' values.
> >
> > *****
> >
> > Has anyone seen this before?
> >
> > Is it a simple Smalltalk error on our part?
> >
> > Are we not accessing the lightSensor object correctly?
> >
> > Or is it something else?
> >
> > Thoughts, code, anything are gratefully received.
> >
> > Best wishes,
> >
> > Mike.
> >
> >
> >
>
>


Reply | Threaded
Open this post in threaded view
|

Re: Bot-Kit Limitations? (was Re: Bot-kit - having trouble using light sensors)

Peter van Rooijen
Mike,

I don't know anything about the Bot Kit or the Lego stuff, but spotted this,
which seems wrong:

>     self forward.
>     (stopRobot = 0) whileTrueRepeat: [
>         self sendSignal.
>         self checkSignal].

I'd expect #whileTrueRepeat: has to be sent to a block, as in:

[stopRobot = 0] whileTrueRepeat: [

Regards,

Peter van Rooijen


Reply | Threaded
Open this post in threaded view
|

Re: Bot-Kit Limitations? (was Re: Bot-kit - having trouble using light sensors)

Andy Bower
In reply to this post by Mike Richards
Mike,

> Thanks Andy, that explains a lot. I think we now know most of the
> limitations of using a subset of Smalltalk in the bot-kit, but we are
> observing a problem that we think might be due to a program size
limitation

> or something similar.  When we try to compile the rcx bytecode with
> createProgam, it fails near the end of
> this method:
>
> hitSomething
>
>     "commmon code if either right or left bumper hit something"
>
>     collided := 1.
>     lightSensorThreshold /= 2.
>     self backward.
>     self sleep: 200.
>     self forward.
>
> The method forward is inherited from BugBot and the code was irregularly
> failing to compile to bytecode at the fullPower method because it didn't
> seem to have anArrayOfMotors.  We were able to make it work by moving the
> self forward outside this method to the method that invoked hitSomething.
> However, adding more code seems to have caused it to fail again after.  It
> can compile bytecode to send the first motor forward, but not the second
> (doesn't matter if it is left or right, if we reverse the order in the
code
> it always fails on the second motor forward).

It's possible you are running out of program space but I haven't seen this
before. One other thing to remember is that method calls (like "self
backward", "self forward") aren't executed as subroutine calls by the RCS
bytecode but, instead they are effectively "macro-expanded"  by Bot-Kit and
the code generated is placed inline as it is sent out to the brick. This
means that it may well be possible to overflow the program space by
inadvertent coding. I can't see anything wrong with what you've done but if,
for example you'd wrapped one of your methods in a standard Smalltalk loop
like #whileTrue: (rather than the Bot-Kit equivalent of #whileTrueRepeat:)
then you would end up repeatedly generating code for the same block until
the RCX became full. If you want to call a particular block of code as a
subroutine then you should use RCX>>createSub:with: and then call it by
using RCX>>goSub:.

In short I can't see what is wrong with the above but, depending on the size
of the rest of your program, you may have hit a limit.

> On another point, our code needs to send IR pulses from the robot so the
> light sensor can try to detect it (we are trying to implement the
Proximity
> sensing robot from http://www.generation5.org/aisolutions/rob00.shtml).
The

> code we are using is:
>
> sendSignal
>
>     "send IR signal out"
>
>     self rcx booleanSpiritCall: [self rcx spirit SendPBMessage: 0 Number:
> 0 ]. "Send and IR Message pulse 0"
>     self sleep: 10.
>     self rcx soundBip.
>
> Is this the right booleanSpiritCall ?  Looking at the RCX LCD, it looks
like
> it only sends out one IR pulse when the robot starts up if you believe the
> IR indicator.  We have this in a whileTrueRepeat loop in the "main" method
> invoked from defaultProgram:

I *think* it's the right call and certainly appears to be the same one used
in proximity.nqc.

> lookForWalls
>
>     "start going forward, send out IR and look for it bouncing back"
>
>     lightSensorLastLevel := self newVariable set: 0.
>     stopRobot := self newVariable set: 0.
>     lightSensorThreshold := self newVariable set: 800.
>     collided := self newVariable set: 0. "Boolean to look for collisions -
> initialise to 0 (False)."
>
>     self forward.
>     (stopRobot = 0) whileTrueRepeat: [
>         self sendSignal.
>         self checkSignal].
>
> I don't think we're actually detecting an IR pulse, we are just halving
our
> threshold for recognizing it repeatedly until it hits zero!

Surely, the problem here is that you need to use separate tasks for
#sendSignal and #checkSignal. The RCX has a multi-tasking OS that can run
five tasks within it (strange when you consider all the other limitations).
In the above, the #sendSignal method will expand to bytecodes send the IR
pulse and, light being what it is, this will have long gone before the RCX
gets around to executing the following expansion for #checkSignal. If you
look at that proximity.nqc file you'll see that they use separate tasks for
the pulses transmission and detection. You can create tasks in Bot-Kit using
RCX>>createTask:with: and you can start them using RCX>>startTask:.

I hope this helps.

Best Regards,

Andy Bower
Dolphin Support
http://www.object-arts.com
---
Are you trying too hard?
http://www.object-arts.com/Relax.htm
---