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. |
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 |
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 > 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. > > > |
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: > > > > 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, > 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 > 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 > > 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 > > 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 > > 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. > > > > > > > > |
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 |
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 > 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 > 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 > 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 --- |
Free forum by Nabble | Edit this page |