def testParallel_AllSucceed_Failure(self): """Can we visit a failing parallel (all succeed)? """ tree = owyl.parallel(owyl.sequence(owyl.succeed(), owyl.fail()), owyl.sequence(owyl.succeed(), owyl.succeed()), policy=owyl.PARALLEL_SUCCESS.REQUIRE_ALL) v = owyl.visit(tree) results = [x for x in v if x is not None] self.assertEqual(results, [False])
def testParallel_DelayedSuccess(self): """Can parallel succeed if child succeeds later (one succeeds)? """ # Succeed after 5 iterations. after = 5 tree = owyl.parallel(owyl.fail(), owyl.succeedAfter(after=after), policy=owyl.PARALLEL_SUCCESS.REQUIRE_ONE) v = owyl.visit(tree) results = [x for x in v if x is not None] self.assertEqual(results, [True]) v = owyl.visit(tree) results = [x for x in v if x is not None] self.assertEqual(results, [True])
def testParallel_DelayedFailure(self): """Can parallel fail if child fails later (all succeed)? """ # Fail after 5 iterations. after = 5 tree = owyl.parallel(owyl.succeed(), owyl.failAfter(after=after), policy=owyl.PARALLEL_SUCCESS.REQUIRE_ALL) v = owyl.visit(tree) results = [x for x in v if x is not None] self.assertEqual(results, [False]) v = owyl.visit(tree) results = [x for x in v if x is not None] self.assertEqual(results, [False])
def testParallel_OneSucceeds_Success(self): """Can we visit a suceeding parallel (one succeeds)? """ tree = owyl.parallel(owyl.sequence(owyl.succeed(), owyl.succeed()), owyl.sequence(owyl.succeed(), owyl.fail()), policy=owyl.PARALLEL_SUCCESS.REQUIRE_ONE) v = owyl.visit(tree) results = [x for x in v if x is not None] self.assertEqual(results, [True]) v = owyl.visit(tree) results = [x for x in v if x is not None] self.assertEqual(results, [True])
def buildTree(self): """ Build the behaviour buildTree Building a behaviour tree is as simple as nesting behaviour constructor calls. Building the behaviour tree ============================ We use parallel to have many behaviour tree run at the same time - check the internet connection - check new tweets and reply - check for new emails Core Behaviours =============== The core behaviour are documented below in each method's docstring. They are : - Brain.query : queries all modules for a response to a question - Brain.checkinternet : check that internet connexion is available - Brain.checknewtweets : check that new tweets are available. - Brain.checknewemails : check that there are new emails """ tree = owyl.parallel( ### Check that internet is available #################################### owyl.limit( owyl.repeatAlways(self.checkinternet(), debug=True, limit_period=2.4) ), ### Check new tweets #################################### self.checknewtweets(), ### Check new emails #################################### self.checknewemails(), policy=owyl.PARALLEL_SUCCESS.REQUIRE_ALL ) return owyl.visit(tree, blackboard=self.bb)
def testStructureBig(self): tree = owyl.parallel(owyl.sequence(owyl.repeatAlways(blackboard.setBB()), owyl.log()), owyl.selector(owyl.repeatUntilSucceed(blackboard.checkBB())), owyl.repeatUntilFail(owyl.fail())) viztree = OwylTree(tree) structure = truncate(viztree.get_structure(), ['name', 'children']) self.assertEquals(structure, {'name': 'parallel', 'children': [{'name': 'sequence', 'children': [{'name': 'repeatAlways', 'children': [{'name': 'setBB', 'children': []}]}, {'name': 'log', 'children': []}]}, {'name': 'selector', 'children': [{'name': 'repeatUntilSucceed', 'children': [{'name': 'checkBB', 'children': []}]}]}, {'name': 'repeatUntilFail', 'children': [{'name': 'fail', 'children': []}]}]})
def buildTree(self): '''as simple as nesting thr behavior constructor calls''' tree = parallel( limit(repeatAlways(self.checkMyBody(), debug=True), limit_period=1), #*children, **kwargs ### Look & See ############################ repeatAlways( sequence( self.seeSomethingNew( ), #*children, **kwargs. run until fails self.headtrack(), self.recogThat()), ), self.sayRecog(), ### Mutter ############################# self.mutter(), ### Chatterbox ############################# self.chat(), policy=PARALLEL_SUCCESS.REQUIRE.ALL) return visit(tree, blackboard=self.bb)
def buildTree(self): """Build the behavior tree. Building the behavior tree is as simple as nesting the behavior constructor calls. Building the Behavior Tree ========================== We'll use a L{parallel<owyl.core.parallel>} parent node as the root of our tree. Parallel is essentially a round-robin scheduler. That is, it will run one step on each its children sequentially, so that the children execute parallel to each other. Parallel is useful as a root behavior when we want multiple behaviors to run at the same time, as with Boids. The first call to a task node constructor returns another function. Calling I{that} function will return an iterable generator. (This behavior is provided by the "@task..." family of python decorators found in L{owyl.core}.) Generally, you won't have to worry about this unless you're writing new parent nodes, but keep it in mind. Also note that keyword arguments can be provided at construction time (call to task constructor) or at run-time (call to visit). The C{blackboard} keyword argument to C{visit} will be available to the entire tree. (This is also why all nodes should accept C{**kwargs}-style keyword arguments, and access. Skipping down to the end of the tree definition, we see the first use of L{visit<owyl.core.visit>}. L{visit<owyl.core.visit>} provides the external iterator interface to the tree. Technically, it's an implementation of the Visitor pattern. It visits each "node" of the behavior tree and iterates over it, descending into children as determined by the logic of the parent nodes. (In AI terminology, this is a depth-first search, but with the search logic embedded in the tree.) L{visit<owyl.core.visit>} is also used internally by several parent behaviors, including L{parallel<owyl.core.parallel>}, L{limit<owyl.decorators.limit>}, and L{repeatAlways<owyl.decorators.repeatAlways>} in order to gain more control over its children. L{limit<owyl.decorators.limit>} =============================== The next parent node we see is L{limit<owyl.decorators.limit>}. L{limit<owyl.decorators.limit>} is a decorator node designed to limit how often its child is run (given by the keyword argument C{limit_period} in seconds). This is useful for limiting the execution of expensive tasks. In the example below, we're using L{limit<owyl.decorators.limit>} to clear memoes once every 0.4 seconds. This implementation of Boids uses L{memojito<examples.memojito>} to cache (or "memoize") neighbor data for each Boid. Neighbor data is used by each of the core behaviors, and is fairly expensive to calculate. However, it's constantly changing, so adjusting the limit_period will affect the behavior of the flock (and the frame rate). L{repeatAlways<owyl.decorators.repeatAlways>} ============================================= We next see the L{repeatAlways<owyl.decorators.repeatAlways>} decorator node. This does exactly as you might expect: it takes a behavior that might only run once, and repeats it perpetually, ignoring return values and always yielding None (the special code for "I'm not done yet, give me another chance to run"). L{sequence<owyl.decorators.sequence>} ============================================= Runs a sequence of actions. If any action yields False, then the rest of the sequence is not executed (the sequence is halted). Otherwise, the next sequence item is run. In this example, a boid accelerates away only if it is too close to another boid. Core Behaviors ============== The core behaviors are documented below in each method's docstring. They are: - L{Boid.hasCloseNeighbors}: conditional to detect crowding - L{Boid.accelerate}: accelerate at a given rate - L{Boid.matchSpeed}: accelerate to match a given speed - L{Boid.move}: move straight ahead at current speed - L{Boid.seek}: seek a fixed goal position - L{Boid.steerToMatchHeading}: match neighbors' average heading - L{Boid.steerForSeparation}: steer away from close flockmates - L{Boid.steerForCohesion}: steer toward average position of neighbors. """ tree = owyl.parallel( owyl.limit(owyl.repeatAlways(self.clearMemoes(), debug=True), limit_period=0.4), ### Velocity and Acceleration ############################# owyl.repeatAlways( owyl.sequence( self.hasCloseNeighbors(), self.accelerate(rate=-.01), ), ), self.move(), self.matchSpeed(match_speed=300, rate=.01), ### Steering ############ self.seek(goal=(0, 0), rate=5), self.steerToMatchHeading(rate=2), self.steerForSeparation(rate=5), self.steerForCohesion(rate=2), policy=owyl.PARALLEL_SUCCESS.REQUIRE_ALL) return owyl.visit(tree, blackboard=self.bb)
def buildTree(self): """Build the behavior tree. Building the behavior tree is as simple as nesting the behavior constructor calls. Building the Behavior Tree ========================== We'll use a L{parallel<owyl.core.parallel>} parent node as the root of our tree. Parallel is essentially a round-robin scheduler. That is, it will run one step on each its children sequentially, so that the children execute parallel to each other. Parallel is useful as a root behavior when we want multiple behaviors to run at teh same time, as with Boids. The first call to a task node constructor returns another function. Calling I{that} function will return an iterable generator. (This behavior is provided by the "@task..." family of python decorators found in L{owyl.core}.) Generally, you won't have to worry about this unless you're writing new parent nodes, but keep it in mind. Also note that keyword arguments can be provided at construction time (call to task constructor) or at run-time (call to visit). The C{blackboard} keyword argument to C{visit} will be available to the entire tree. (This is also why all nodes should accept C{**kwargs}-style keyword arguments, and access Skipping down to the end of the tree definition, we see the first use of L{visit<owyl.core.visit>}. L{visit<owyl.core.visit>} provides the external iterator interface to the tree. Technically, it's an implementation of the Visitor pattern. It visits each "node" of the behavior tree and iterates over it, descending into children as determined by the logic of the parent nodes. (In AI terminology, this is a depth-first search, but with the search logic embedded in the tree.) L{visit<owyl.core.visit>} is also used internally by several parent behaviors, including L{parallel<owyl.core.parallel>}, L{limit<owyl.decorators.limit>}, and L{repeatAlways<owyl.decorators.repeatAlways>} in order to gain more control over its children. L{limit<owyl.decorators.limit>} =============================== The next parent node we see is L{limit<owyl.decorators.limit>}. L{limit<owyl.decorators.limit>} is a decorator node designed to limit how often its child is run (given by the keyword argument C{limit_period} in seconds). This is useful for limiting the execution of expensive tasks. In the example below, we're using L{limit<owyl.decorators.limit>} to clear memoes once every second. This implementation of Boids uses L{memojito<examples.memojito>} to cache (or "memoize") neighbor data for each Boid. Neighbor data is used by each of the core behaviors, and is fairly expensive to calculate. However, it's constantly changing, so adjusting the limit_period will affect the behavior of the flock (and the frame rate). L{repeatAlways<owyl.decorators.repeatAlways>} ============================================= We next see the L{repeatAlways<owyl.decorators.repeatAlways>} decorator node. This does exactly as you might expect: it takes a behavior that might only run once, and repeats it perpetually, ignoring return values and always yielding None (the special code for "I'm not done yet, give me another chance to run"). Core Behaviors ============== The core behaviors are documented below in each method's docstring. They are: - L{Boid.hasCloseNeighbors}: conditional to detect crowding - L{Boid.accelerate}: accelerate at a given rate - L{Boid.matchSpeed}: accelerate to match a given speed - L{Boid.move}: move straight ahead at current speed - L{Boid.seek}: seek a fixed goal position - L{Boid.steerToMatchHeading}: match neighbors' average heading - L{Boid.steerForSeparation}: steer away from close flockmates - L{Boid.steerForCohesion}: steer toward average position of neighbors. """ tree = owyl.parallel( owyl.limit( owyl.repeatAlways(self.clearMemoes(), debug=True), limit_period=0.4), ### Velocity and Acceleration ############################# owyl.repeatAlways(owyl.sequence(self.hasCloseNeighbors(), self.accelerate(rate=-.01), ), ), self.move(), self.matchSpeed(match_speed=300, rate=.01), ### Steering ############ self.seek(goal=(0, 0), rate=5), self.steerToMatchHeading(rate=2), self.steerForSeparation(rate=5), self.steerForCohesion(rate=2), policy=owyl.PARALLEL_SUCCESS.REQUIRE_ALL ) return owyl.visit(tree, blackboard=self.bb)
def makeBasicZenoTree(self): ## Zeno's Body Paint subtree zenoBodyPaint = \ owyl.selector( owyl.sequence( self.isNotSameBrushStroke(), self.isGreater(num1=self.animationOutputDur, num2=2), self.setVariable(var=self.actionName, value="BrushStrokeGesture"), owyl.selector( # try the action sequence (subtree) or report failure owyl.sequence( owyl.visit(self.announceAndResetTree, blackboard=self.blackboard), self.showAction(action=self.actionName, part=self.HEAD_NECK) # Finally play the action's animation ), owyl.sequence( self.say(utterance="I'm not feeling inspired today..."), owyl.fail() ) ) ), owyl.sequence( self.setVariable(var=self.actionName, value="Idle"), owyl.selector( # try the command sequence (subtree) or report failure owyl.sequence( owyl.visit(self.announceAndResetTree, blackboard=self.blackboard), self.showAction(action=self.actionName, part=self.HEAD_NECK) # Finally play the action's animation ), owyl.sequence( self.say(utterance="Why can't I stand?"), owyl.fail() ) ) ) ) ## body behavior subtree zenoBodySubtree = \ owyl.limit( owyl.repeatAlways( owyl.selector( # Select response to command or natural behavior owyl.sequence( # If the last audio or blender input is a command, then select a response self.isCommand(commandName=self.commandName), self.setVariable(var=self.bodyOrFace, value=self.UPPER_BODY), owyl.visit(self.selectBasicCommandSubtree, blackboard=self.blackboard) ), # It's not a command, so start checking for natural behaviors owyl.sequence( # self.isNoSalientTarget(), # self.isNoFaceTarget(), # self.isNoAudioInput(), # self.isNoRosInput(), # self.isNoEmotionInput(), self.isIdle(), # There's nothing to do, so let's paint! owyl.visit(zenoBodyPaint, blackboard=self.blackboard) ), owyl.sequence( # TODO: the other natural actions, once we have a saliency target, etc. ) ) ), limit_period=0.4 # Yield to the other behaviors after every 400 milliseconds of processing ) # face & neck behavior subtree zenoFaceSubtree = \ owyl.limit( owyl.repeatAlways( owyl.selector( # Select from one of several mutually exclusive face & neck behaviors owyl.sequence( # If the last audio or blender input is a command, then select a response self.isCommand(commandName=self.commandName), self.setVariable(var=self.bodyOrFace, value=self.HEAD_NECK), owyl.visit(self.selectBasicCommandSubtree, blackboard=self.blackboard) ), owyl.sequence( # self.isSalientTarget(), # self.isNoFaceTarget(), # self.isNoAudioInput(), # self.isNoRosInput(), # self.isNoEmotionInput(), self.isIdle(), owyl.visit(self.faceGaze, blackboard=self.blackboard) ), owyl.sequence( self.isFaceTarget() ) ) ), limit_period=0.4 # Yield to the other behaviors after every 400 milliseconds of processing ) # Zeno's root tree zenoTree = \ owyl.parallel( # At the highest level, run several parallel behaviors owyl.visit(zenoBodySubtree, blackboard=self.blackboard), owyl.visit(zenoFaceSubtree, blackboard=self.blackboard), owyl.limit( owyl.repeatAlways( owyl.sequence( # Poll for input coming from blender, for example buttons to stop movement, etc. # May move this logic out of the behavior tree... # self.pollForBlenderInput(), # Listen for audio input from people talking, etc. # Again, this might not be the best place for this... # self.listenForAudioInput() ) ), limit_period=0.4 # Yield to the other behaviors after every 400 milliseconds of processing ), policy=owyl.PARALLEL_SUCCESS.REQUIRE_ALL ) return owyl.visit(zenoTree, blackboard=self.blackboard)
def makeBasicZoidSteinTree(self): ## The scripted dance of ZoidStein, used when no faces or salient targets are detected. # Assumes body state has been reset to starting state zoidSteinBodyDance = \ owyl.sequence( self.showCommand(commandName="WalkForward", part=self.LOWER_BODY), self.showCommand(commandName="WalkBackward", part=self.LOWER_BODY), self.showAction(commandName="PointUp", part=self.UPPER_BODY), self.showAction(commandName="PointDown", part=self.UPPER_BODY) ) ## body behavior subtree # TODO: Attach subtrees properly zoidSteinBodySubtree = \ owyl.limit( owyl.repeatAlways( owyl.selector( # Select response to command or natural behavior owyl.sequence( # If the last audio or blender input is a command, then select a response self.isCommand(commandName=self.commandName), self.setVariable(var=self.bodyOrFace, value="body"), owyl.visit(self.selectBasicCommandSubtree, blackboard=self.blackboard) ), # It's not a command, so start checking for natural behaviors owyl.sequence( # self.isNoSalientTarget(), # self.isNoFaceTarget(), # self.isNoAudioInput(), # self.isNoRosInput(), # self.isNoEmotionInput(), self.isIdle(), # There's nothing to do, so let's dance! owyl.visit(zoidSteinBodyDance, blackboard=self.blackboard) ), owyl.sequence( #TODO: the other natural actions, once we have a saliency target, etc. ) ) ), limit_period=0.4 # Yield to the other behaviors after every 400 milliseconds of processing ) ## face & neck behavior subtree # TODO: Attach subtrees properly zoidSteinFaceSubtree = \ owyl.limit( owyl.repeatAlways( owyl.selector( # Select from one of several mutually exclusive face & neck behaviors owyl.sequence( # If the last audio or blender input is a command, then select a response self.isCommand(commandName=self.commandName), self.setVariable(var=self.bodyOrFace, value=self.HEAD_NECK), owyl.visit(self.selectBasicCommandSubtree, blackboard=self.blackboard) ), owyl.sequence( # self.isSalientTarget(), # self.isNoFaceTarget(), # self.isNoAudioInput(), # self.isNoRosInput(), # self.isNoEmotionInput(), self.isIdle(), owyl.visit(self.faceGaze, blackboard=self.blackboard) ), owyl.sequence( self.isFaceTarget(), ) ) ), limit_period=0.4 # Yield to the other behaviors after every 400 milliseconds of processing ) ## ZoidStein's root tree zoidSteinTree = \ owyl.parallel( # At the highest level, run several parallel behaviors owyl.visit(zoidSteinBodySubtree, blackboard=self.blackboard), owyl.visit(zoidSteinFaceSubtree, blackboard=self.blackboard), owyl.limit( owyl.repeatAlways( owyl.sequence( # Poll for input coming from blender, for example buttons to stop movement, etc. # May move this logic out of the behavior tree... # self.pollForBlenderInput(), # Listen for audio input from people talking, etc. # Again, this might not be the best place for this... # self.listenForAudioInput() ) ), limit_period=0.4 # Yield to the other behaviors after every 400 milliseconds of processing ), policy=owyl.PARALLEL_SUCCESS.REQUIRE_ALL ) return owyl.visit(zoidSteinTree, blackboard=self.blackboard)
def makeBasicTree(self): robotTree = \ owyl.parallel( ######################################## BodySubtree ######################################## owyl.limit( owyl.repeatAlways( owyl.sequence( self.updateFrontVariables(), self.determineCurrentTarget(), self.removeFace(), owyl.selector( # Gaze at face targets owyl.sequence( self.isFaceTarget(), self.isNoSalientTarget(), self.isNoAudioInput(), self.isNoRosInput(), self.isNoEmotionInput(), self.faceGaze() ), # Gaze at salient targets # owyl.sequence( # self.isSalientTarget(), # # self.isNoFaceTarget(), # self.isNoAudioInput(), # self.isNoRosInput(), # self.isNoEmotionInput(), # self.faceGaze() # ), # Handle commands owyl.sequence( owyl.selector( self.isAudioInput(), self.isRosInput() ), owyl.selector( self.isCommand(key="audioInput"), self.isCommand(key="rosInput") ), owyl.selector( owyl.sequence( owyl.selector( self.isCommandPhrase(commandName="StopSpeech", actionPhrase="actionName"), self.isCommandPhrase(commandName="WalkForward", actionPhrase="actionName"), self.isCommandPhrase(commandName="WalkBackward", actionPhrase="actionName"), self.isCommandPhrase(commandName="TurnLeft", actionPhrase="actionName"), self.isCommandPhrase(commandName="TurnRight", actionPhrase="actionName"), self.isCommandPhrase(commandName="StopSpeaking", actionPhrase="actionName"), self.isCommandPhrase(commandName="Smile", actionPhrase="actionName"), self.isCommandPhrase(commandName="Frown", actionPhrase="actionName"), # self.isCommandPhrase(commandName="FrownMouth", actionPhrase="actionName"), self.isCommandPhrase(commandName="Surprise", actionPhrase="actionName"), self.isCommandPhrase(commandName="TakeThis", actionPhrase="actionName"), self.isCommandPhrase(commandName="GiveBack", actionPhrase="actionName"), self.isCommandPhrase(commandName="Wave", actionPhrase="actionName"), ), self.isNotSpeaking(), # self.sayStartAction(key="actionName"), self.showAction(key="actionName") ), self.printStatus(msg="I'm sorry, Dave, I'm afraid I can't do that...") ) ), # Play emotion detection game owyl.sequence( owyl.selector( #TODO: change to sequence self.isAudioInput(), self.isEmotionInput(), ), self.isNotStopEmotionDetection(key="audioInput"), self.isEmotionDetection(key="audioInput"), self.isNotSpeaking(), self.startEmotionDetection(), ), # Play object recognition game owyl.sequence( owyl.selector( self.isAudioInput(), self.isObjInput(), ), self.isNotStopObjRecognition(key="audioInput"), self.isObjRecognition(key="audioInput"), self.isNotSpeaking(), self.startObjRecognition() ), # Send to the dialogue system owyl.sequence( self.isAudioInput(), self.isNotSpeaking(), self.toZenoDial(key="audioInput") ) ) ) ), limit_period=0.001 ), ######################################## General tree ######################################## owyl.limit( owyl.repeatAlways( owyl.sequence( self.test(), self.updateVariables() ) ), limit_period=0.001 ), policy=owyl.PARALLEL_SUCCESS.REQUIRE_ALL ) return owyl.visit(robotTree, blackboard=self.blackboard)