-
Notifications
You must be signed in to change notification settings - Fork 7
Behaviour
Our behaviours are written in Python. At a high level, our C++ perception thread reads information from the world (through vision / sonars, etc), localises based on that information, and then calls a python function in an embedded interpreter which has to return an action to perform. This action is relayed to the relevant robot outputs (LEDs directly, or to the walk engine for physical motion).
There’s three core Classes that are important:
- BehaviourTask: This is the superclass for any task.
- World: Every task has a world object that gives it access to shared information about the world. It also sets a behaviour request in that world which gets read at the end of the behaviour tick.
- TaskState: This is for Tasks that are complex enough to warrant states / state transitions / hysteresis.
Often while writing behaviours you'll need to perform general tasks that are not unique to your behaviour (e.g calculating geometries, checking information about the team, etc). To be more efficient and have fewer places to change when we perform actions, we have utility modules that contain a lot of this information. These are listed here:
- Constants.py: Wrapper for
robot
module constants and python constants. Please use these over directly accessing the robot module so that it's easier to change in one place if the C++ blackboard changes. This one will also include any other constants that aren't from C++, so it'll all be in one place. - Global.py: Information about the world and you (e.g canSeeBall(), myPose(), amILost(), offNetwork().
- util (various useful utilities like timers, etc.)
- MathUtil.py: Additional maths on top of what's in the math library
- FieldGeometry.py - Geometry functions for common field calculations (angles, etc)
- TeamStatus.py: Team information (things like player numbers, whether an obstacle is a teammate, etc).
- Timer.py: A simple timer utility so you don't have to keep doing the thing where you subtract starttime from blackboard.vision.timestamp. Also provides useful stopwatch like features so you can time the total amount of something in pieces.
- Vector2D.py: Simple class for vectors.
These modules have access to the latest blackboard on each tick, so you don't need to pass the blackboard around to them.
Python code is found here:
image/home/nao/data/behaviours/
If you want to see how the C++ code that starts the behaviours, it's in the perception folder:
robot/perception/behaviour/
nao_sync robot-name
# e.g: nao_sync luigi
Have a look at a simple one, like Default. Create a filename with the same name as your skill, so MyNewSkill.py would have a class:
from skills.Task import BehaviourTask
import robot
class MyNewSkill(BehaviourTask):
def init(self):
# Any special initialisation code. Note that if you don't override __init__, it will set up the world
# variables, etc. If you decide to override __init__ (there's valid cases to do so), see what
# BehaviourTask.__init__ and make sure to also do what it does.
pass
def transition(self):
# Any transition code if you have taskstates.
pass
def _tick(self):
# What to run in each timestep.
# Note: use this if you want to let the system manage transitions for you. Essentially it will call
# transition and then _tick.
# If you want to manage that yourself, then you override tick()
req = robot.BehaviourRequest()
request.actions = robot.All()
# Some request stuff.
self.world.b_request = req # This will automatically get returned to C++.
The world object your task has access to will have a blackboard. This matches the blackboard in c++.
self.world.blackboard.vision.timestamp
Sync it to the robot. Test it in isolation by running it with runswift:
runswift -s YourSkillName
Default
The highest level of the architecture, behaviour.py will create a new World() object and pass it to the first skill it creates (the one you passed it with -s or GameController by default). This world object stays the same throughout, the only thing that changes is the blackboard variable within it which we set. This structure allows us to change the blackboard once and have every skill that's looking at the same world have the new blackboard. Otherwise, if each skill had a "self.blackboard" we would need to update that reference in each tick by calling an update function on each item in the tree (which adds more code complexity).