Programming Robot
Python Classes
Programming Robot
Python Classes Require Politeness:
Python does not have the privacy mechanisms of C++ and Java and relies upon the user to not take advantage of this.
The class hierarchy allows multiple inheritance
derived class methods can override base class methods
derived class methods can invoke methods of the same name in the base class
In C++ jargon, all methods are public and virtual (overridable).
operation overloading is possible
Programming Robot
Objects:
Objects have individuality:
Objects can have multiple references and a mutable object, passed as an argument to a method, can be modified inside the method
We only need a single syntax for parameters (unlike Pascal, C, C++).
x = 3y = 3# both reference the same place in memory
Programming Robot
Scopes and Namespaces
A namespace is a mapping of names to objects.
Namespaces are implemented with a Python dictionary but this is not noticeable except for its effect on performance.
Namespace examples:
reserved words and built-in names (like abs()). global names in a module local names within a method invocation
Different namespaces can reuse the same name.
If a namespace is associated with a module then
import myModule
print myModule.myName
Programming Robot
Scopes and Namespaces
An attribute is anything that follows a “.”.
Attributes are read-only or writable.
Module attributes are writeable
You can delete a writable attribute
Namespace survival:
the built-in namespace is created as the interpreter loads a module namespace is created when the module loads method namespaces survive only while the method executes
print myModule.myName
an attribute just like a datafield or method in a class
del myModule.myName
Programming Robot
Well-known Namespaces:
__main__: the top-level namespace
__builtin__: - the namespace for built-in names.
Programming Robot
Scope:
A scope is a text region where unqualified names can be used from some namespace.
There are three or more namespaces active at any moment:
the inner-most namespace the calling environment namespaces (at least 1) the built-in namespace
Names from an outer namespace are read-only unless labeled global.
If you attempt to write to an outer namespace name you will just create a new local name
a = 3def foo(): a = 4; print afoo()print a
# prints
43
Programming Robot
Local and Global Scopes
Local Scope inside a method references the local names of the method
Outside a function or method, local scope is the module scope.
Class definitions introduce a new scope
The global scope of a function is scope of the module it is defined in; not where it is used. We describe this by saying that scope is defined “textually”.
By default, global scope is not in effect. You must specify it explicitly
a = 3def foo(): global a a = 4; print afoo()print a
# prints
44
Programming Robot
Classes
Class definitions must be executed before the classes are used.
Entering a class definition creates a new namespace which becomes the local scope and new names are part of this scope.
Exiting a class definition creates a class object (not an instance) which wraps the class namespace and the original scope is reinstated.
Inside the new scope the new class object is bound to the name, MyClass.
class MyClass: <statement-1> ... <statement-N>
most statements are method definitions
Programming Robot
Class Objects:
Support two kinds of operations:
attribute references: Standard notation (MyClass.a). instantiation: Doesn't use new.
class MyClass: """ A simple class example""" a = 4 def f(self): return "Hello, world!"
print MyClass.aMyClass.b = 5
myClass = MyClass()
print myClass.b
print myClass.__doc__print MyClass.__doc__
a docstring
__doc__ is a valid attributeof all classes that prints outdocstrings.
creates a new MyClassattribute; future instantiationswill include this attribute
# output45 A simple class example A simple class example
Programming Robot
ClassAttributes:
Classes are usually defined with no data attributes but rather just a list of method definitions.
Data attributes are added to the instantiated class objects during instantiation by invoking the method __init__().
class Complex: def __init__(self, realpart, imagpart): self.r = realpart self.i = imagpart
x = Complex(3.0, -4.5)print x.r, x.i(3.0, -4.5)
define an __init__() methodto add data attributes to a class
self is the local scope name of the instantiate object.
Programming Robot
Data Attributes:
The same as “data members” in C++.
Don't need to be pre-declared before use.
They can spring into existence and disappear just as easily
x = AnotherClass() . . .x.counter = 1while x.counter < 10: x.counter = x.counter * 2print x.counterdel x.counter
Programming Robot
Method Attributes:
Just notation but function attributes in a class are called method attributes in an instantiation of the class.
Methods are objects just like other things and so you can create new references to them as you wish
x = MyClass()
xf = x.fwhile True: print xf()
xf is just another reference to the method f() in the MyClassobject called x.
Programming Robot
What happens when a method is called?
In the MyClass definition we defined f() with parameter self.
When we call x.f() we don't pass f an argument.
What is happening?
Referencing a method, x.f(), causes a new method object to be created consisting of (pointers to) the function object in the class definition packed together with the instance object.
Calling a method, x.f(), the method object is unpacked and the necessary arguments are passed to it.
x.f() is really MyClass.f(x)
Programming Robot
Random Observations:
There is no shorthand for referencing an instance data attribute inside a method; you must use self.x.
The name, self, is just a convention.
Class methods can actually be defined outside the class:
# Function defined outside the classdef f1(self, x, y): return min(x, x+y)
class C: f = f1 def g(self): return 'hello world' h = g
f, g and h are allfunction objects inthe class, C.This example showswhat is “possible” butits use would onlyconfuse people.
Programming Robot
Inheritence:
What would a class be without inheritance?
Resolving attribute or variable references proceeds from the derived class scope to the base class scope to the module scope.
Method overriding is ok.
All Python methods are virtual in C++ terms.
Calling a base class method of the same name is ok:
class DerivedClassName(BaseClassName): <statement-1> . . . <statement-N>
BaseClassName(self,arguments)
Programming Robot
Creating an empty class:
Class Employee: pass
Programming Robot
Robot class:
Myro allows you to create objects from the robot class:
In your robot object I want you to save two lists:
List of all methods you have written or inherited List of all methods you will use in a particular program
The first list gives me a way to access all MyScribbler methods by their string name.
The second list tells me the methods used in the current program in the order in which they will appear in the behaviours list.
from myro import *
class MyScribbler(Scribbler):
Programming Robot
MyScribbler.__init__():
def __init__(self,port): Scribbler.__init__(self,port,t=0,r=0) # a dictionary of <str(methodname),methodName> pairs self.masterMethodsList = {'move':Scribbler.move, 'beep':Scribbler.beep, 'motors':Scribbler.motors, 'avoidLight':self.avoidLight, 'nudgeTowardsLight':self.nudgeTowardsLight, 'nudgeAwayFromLight':self.nudgeAwayFromLight, 'obstacleHit':self.obstacleHit, 'obstacleSensed':self.obstacleSensed, 'dflt':self.dflt } # remember, no comma after the # last method name self.programMethodslist = [ ] # a list of method names in use self.T = t self.R = r self.setAmbientLight() self.brightestLight = 50.0 self.debug = False
Programming Robot
Printing out Info about Scribbler:
The Scribbler class has a method called __str__() that needs to be overridden.
This method prints out info about the base class and we can extend it to print our info about our class.
def __str__(self):retVal = ''
retVal += '\nCurrent Program Methods:' for meth in self.programMethodsList: for (methodName,method) in self.masterMethodsList.items(): if method == meth: retVal += '\n %s ' % methodName retVal += '\nTranslate: %f' % self.T retVal += '\nRotate: %f' % self.R retVal += '\nAmbient: %f' % self.ambient retVal += '\nDebug: %r'% self.debug return retVal
Programming Robot
Additional Useful Methods (more to come):
def getMasterList(self):return self.masterMethodsList
def getProgramList(self):return self.programMethodsList
def addToProgramList(self,methodName): if methodName in self.masterMethodsList.keys(): self.programMethodslist.append(self.masterMethodsList[methodName])
def setAmbientLight(self): self.ambient = sum(getLight('all'))/3.0
Programming Robot
Main Methods:
def arbitrate(self): for behaviour in self.programMethodsList: if self.debug: print behaviour option,T,R,action = behaviour() if option: return T,R,action def main(self,tr): while(timeRemaining(tr)): T,R,action = self.arbitrate() if self.debug: print T, R, action action(T,R) stop()
Programming Robot
MyScribbler Behavioural Methods (behaviours):
# turns Left if dir == 1; Right if dir == -1 # speed = speed of turn def reactToLight(self,dir,speed): if dir != 1: dir = -1 return True, self.T, (dir*speed),self.move def nudgeTowardsLight(self): (L,C,R) = self.getLight('all') print L,C,R if (L < R - 300): return self.reactToLight(1,0.2) elif (R < L - 300): return self.reactToLight(-1,0.2) else: return False,self.T,self.R,self.move
Programming Robot
MyScribbler Behavioural Methods (behaviours):
# obstacleHit() really only useful if speed above 0.75 def obstacleHit(self): # obstacle if getStall() returns 1; ob = self.getStall() if ob: return True, 0,-1,self.move else: return False,self.T,self.R,self.move def obstacleSensed(self): L,R = getIR() L = 1 - L R = 1 - R if (L == 1 and R == 1): return True,0, 0.5,self.move elif (L == 1): return True,self.T,-0.5,self.move elif (R == 1): return True,self.T,0.5,self.move else: return False,self.T,self.R,self.move
Programming Robot
MyScribbler Behavioural Methods (behaviours):
def dflt(self): if self.debug: print 'dflt' return True,self.T,self.R,self.move
Programming Robot
How to Use MyScribbler:
from myro import *from myRobot import *
class Template (MyScribbler): def __init__(self,port,t=0,r=0): MyScribbler.__init__(self,port,t,r) # add additional behaviours here for this program only self.masterMethodsList['myBehaviour'] = self.myBehaviour self.masterMethodsList['dflt'] = self.dflt self.debug = True # turn on debug if wanted def __str__(self): retVal = MyScribbler.__str__(self) return retVal # determines if it is time to charge the cape def myBehaviour(self): # this test behaviour alwasy fails so only dflt is used if self.debug: print 'myBehaviour' return [False,self.T,self.R,self.move]
change the name
Programming Robot
How to Use MyScribbler:
# a normal functiondef main(): temp = Template('/dev/rfcomm0',0.5,0)
temp.addToProgramList('myBehaviour') temp.addToProgramList('dflt') print temp.__str__() MyScribbler.main(temp,2)
main()
Programming Robot
Dictionary:
A dictionary is a set of <key,value> pairs.
You can initialize a dictionary by putting elements in the dictionary as a key:value pair.
self.masterMethodsList = {'move':Scribbler.move, 'beep':Scribbler.beep, 'moveForward5':self.moveForward5 }
Programming Robot
How To Write Programs:
You first need to think about the behaviours you want to capture. For example, in simulating the bull in a bull ring you need to think about
bull-behaviour (find and charge the red cape)
incidental behaviour (avoiding other obstacles).
Next you have to think about the behaviours in the order in which you should determine their appropriateness.
chargeCape(), followLight()
obstacleHit(), obstacleSensed(), dflt()
Programming Robot
How To Write Programs:
Next you have to think about the behaviours in the order in which you should determine their appropriateness.
For example, the bull begins to detect a cape by sensing a light source. As it gets closer to the light source it detects an obstacle.
The program mechanism asks for the first desirable behaviour.
Since the cape is an obstacle together with a light, if we use the behaviours1 list we will never find the cape since we will always see the light first and only perform light-seeking behaviour.
behaviours1 = [..., 'followLight', 'chargeCape', ... ]
vs
behaviours2 = [..., 'chargeCape', 'followLight', ... ]
Programming Robot
bull_class.py
from myro import *from myRobot import *
class Bull (MyScribbler): def __init__(self,port,t=0,r=0): MyScribbler.__init__(self,port,t,r) self.foundObstacle = False self.masterMethodsList['chargeCape'] = self.chargeCape self.masterMethodsList['obstacleHit'] = self.obstacleHit self.masterMethodsList['obstacleSensed'] = self.obstacleSensed self.masterMethodsList['followLight'] = self.followLight self.masterMethodsList['dflt'] = self.dflt self.lightThreshhold = 0.4*self.ambient self.debug = True
Programming Robot
bull_class.py
def __str__(self): retVal = MyScribbler.__str__(self) retVal += '\nThreshhold: %f' % self.lightThreshhold retVal += '\nDebug: %r' % self.debug return retVal # determines if it is time to charge the cape def chargeCape(self): C = getLight('center') lightAhead = (C < self.lightThreshhold) if self.debug: print 'chargeCape: ', 'center < thresh: %r'% lightAhead if lightAhead: [Option,T,R,action] = MyScribbler.obstacleSensed(self) if Option and T == 0: return [Option,1,0,self.doCharge] return [False,self.T,self.R,self.move]
Programming Robot
bull_class.py
# no cape, but perhaps an obstacle def obstacleSensed(self): [Option,T,R,action] = MyScribbler.obstacleSensed(self) return [Option,T,R,action]
# keep going towards the light def followLight(self): L, C, R = getLight() if L < R-200: # straight but adjust slightly left if self.debug: print 'followLight: left' return [True, self.T,0.2,self.move] elif R < L-200: # straight but adjust slightly right if self.debug: print 'followLight: right' return [True, self.T,-0.2,self.move] else: if self.debug: print 'followLight: no' return [False, self.T, self.R, self.move]
Programming Robot
bull_class.py
def doCharge(self,t,r): if self.debug: print 'doCharge: ' self.backward(0.5,0.3) self.beep(0.5,800) self.move(t,r) wait(1.5) self.move(0,-0.3) wait(2)
def flourish(self): self.motors(1,0.8) wait(1.8) #stop() self.rotate(1) wait(1) #stop() self.rotate(-1) wait(1) self.stop() self.rotate(0.4)
Programming Robot
bull_class.py
def main(): bull = Bull('/dev/rfcomm0',0.6,0)
# bull.addToProgramList('chargeCape')# bull.addToProgramList('obstacleHit')# bull.addToProgramList('obstacleSensed')# bull.addToProgramList('followLight') bull.addToProgramList('dflt') print bull.__str__() wait(4) bull.flourish() MyScribbler.main(bull,60)
main()