UserPreferences

BehaviorBasedControl


1. BehaviorBasedControl

This page describes the Behavior based control system as used in Pyro.


BehaviorClass, BehaviorEngineClass, StateClass

There are two methods of using behaviors: sequences and blends. To use blending, you need only have multiple behaviors active simultaneously. These are usually in the same state, but could be different states.

The easiest method to produse sequencing is to go from one state to another. (You could also activate and deactivate behaviors, but that sounds more difficult).

Here is an example called BBSquare.py:

# A Behavior sequencing sample
# D.S. Blank

# This Pyro example will go (roughly) in a square

# This example has two states, "edge" that goes straight, and "turn"
# that turns 90 degrees to the left. It bounces back and forth between
# these two states.

# Note how it uses onActivate() to remember where it was when it
# started in both cases. It then moves (forward or to the left) until
# it has moved enough.

# Exercises left to reader:

# 1. this doesn't avoid obstacles; what must you do to do that?

# 2. this relies completely on odometry for localization (ie, it uses
# dead reckoning); wouldn't it be better to add some type of
# landmark-based system, if landmarks are available?

# 3. This doesn't use very sophisticated behaviors for turning or
# moving. It would be better, for example, if the turning slowed down
# when it got closer to its desired angle. How would you do that?

# 4. If you wanted to traverse a map, you would need to have a
# different state for each position in the map. You could get around
# that by using the onGoto() and Goto() methods. But you would have to
# make the next state to goto a parameter that you pass in. Why?

from pyrobot.brain.fuzzy import *
from pyrobot.brain.behaviors import *
from pyrobot.brain.behaviors.core import *   # import distance function

import math
from random import random

class TurnLeftBehavior (Behavior):
    def init(self):
        self.Effects('rotate', .1)
        self.Effects('translate', .1) 

    def update(self):
        self.IF(1, 'rotate', .1)
        self.IF(1, 'translate', 0)

class StraightBehavior (Behavior):
    def init(self): # method called when created
        self.Effects('translate', .1) 
        self.Effects('rotate', .1) 

    def update(self):
        self.IF(1, 'translate', .1) 
        self.IF(1, 'rotate', 0)

class edge (State):
    def init(self):
        self.add(StraightBehavior(1))

    def onActivate(self): # method called when activated or gotoed
        self.startX = self.robot.x
        self.startY = self.robot.y
        
    def update(self):
        x = self.robot.x
        y = self.robot.y
        dist = distance( self.startX, self.startY, x, y) 
        print "actual = (%f, %f) start = (%f, %f); dist = %f" \
              % (x, y, self.startX, self.startY, dist)
        if dist > 1.0:
            self.goto('turn')

class turn (State):
    def init(self):
        self.count = 0
        self.add(TurnLeftBehavior(1))

    def onActivate(self):
        self.th = self.robot.th

    def update(self):
        th = self.robot.th
        print "actual = %f start = %f" % (th, self.th)
        if angleAdd(th, - self.th) > 90: 
            self.goto('edge')

def INIT(engine): # passes in robot, if you need it
    brain = BehaviorBasedBrain({'translate' : engine.robot.translate, \
                                'rotate' : engine.robot.rotate, \
                                'update' : engine.robot.update }, engine)
    # add a few states:
    brain.add(edge(1))
    brain.add(turn())

    brain.init()
    return brain

Andrew and Daniel ask the following questions:

Q: We tried using the distance function as DSB did above, but we get a global name 'distance' is not defined error. we definitely did import behviors.core, so that's not the problem how do we make the distance function work?

A: Distance is now defined in the pyro.brain.behaviors.core. It is defined as follows:

def distance(x1, y1, x2, y2):
   return sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))

Q: When we load a world or move the robot, the dead-reckoning is not reset accordingly. To work around this we stopped moving the robot around and offset our dead-reckoning in our code by the initial values set by the world, but this is an inelegant and not robust solution.

A: I also found that bug and fixed the robot class. Now, when you initialize a brain, it will automatically re-localize the robot to (0, 0, 0). That is, the location (X, Y, Theta) will be set to zeros. If you want to relocalize it to someplace else, you can call robot.localize(X, Y, Th) after the robot has been initialized.

Q: If we create a variable in a state, can we access it from a behavior? If so, how?

Behavior objects now have a field called 'state' that you may read and write to. It is a pointer to a behavior's parent state (i.e., the state to which it belongs).

   ... inside a behavior
   self.state.var1 = 4

Q: Is there a way to set which states are true depending on certain conditions?

A: You can add states to the "graph" with behaviorEngine.add(stateConstructor(1)) where 1 means "active". The default is "non-active" (zero). Of course, the value could also be a variable that was set prior to adding the state.

If you want to have states that are active based on variable values, use the goto() method. You can also goto() more than one state. WARNING: having multiple states active has not been thoroughly tested, but should work.

Q: Can you pass variables into state parameters?

You can easily pass parameters when you activate a state with goto(STATENAME, *args). *args will be sent on to the state named STATENAME via the method onActivate(). You can also directly change another states variables by calling behaviorEngine.getState(STATENAME).field. For example:

    ... inside State1
    self.behaviorEngine.getState("State2").field1 = 23

Q: Is there a way to get the angle of an individual sensor?

Sure. Try self.robot.sonar[0][3].angle() from inside a state or behavior. This will return the angle, in degrees, that the sensor is pointing. You can also get the 'arc' this way.

Q: Is there a way to put a print statement inside a fuzzy if?

A: I think what you are asking for is a way to see what behaviors are contributing to the current effect of the motors, and see what fuzzy IF/rules have fired. You can now select a brain for detailed viewing. This is a bit hacky right now (version 7.2), but may be useful. Try this:

  1. Select 'brain' from the 'view' menu option

  2. Run your behavior as before

  3. You will now see each rule that effects a controller as a colored piece of a pie.

If you only see blue, then only one behavior is in control, and it has full control. If you see red pie, that means there is blending going on.

Q: We are getting wacky results from our fuzzy ifs. for instance, in a state with only two behaviors, one of which has self.Effects('rotate', 0.1) and the other has self.Effects('rotate', 1.0) (so the latter behavior should dominate rotation), the latter behavior looks like

   ...
   self.IF(truthValue, 'rotate', 1.0)

when truthValue is 0.0, it doesn't rotate, but if truthValue is 0.01 it rotates just as fast as if truthValue is 1.0. this seems very non-fuzzy.

A: This could be true. If there are no other values to balance against your behavior, then the total desire may be equal to the one and only behavior's wishes. For example, 0.1/0.1 means that the behavior gets full effect. Likewise, 1.0/1.0 also gets full effect. Solution: you need another behavior that provides a base (default) effect for that controller.

/!\ If a behavior has listed an effect on a controller, but there are no fuzzy IF's associated with the controller in that behavior, or if the IF's do not actually contributed to the effect (eg, compute to zero effect), then that behavior will be ignored for that controller and the effect will not be used to compute total effect. For example, consider a behavior Beh1 such that:

class Beh1(Behavior):
   init(self):
      self.Effects('rotate', 1.0)
   update(self):
      self.IF(0, 'rotate', 0.5)

Since Beh1 does not actually contribute to the effect on the controller (it has zero truth value), the self.Effects('rotate', 1.0) line is ignored.


Related topics:

  1. PyroCourseModules . . . . 2 matches
  2. PyroCurriculumExamples . . . . 2 matches
  3. fixem . . . . 2 matches
  4. BehaviorBasedControl . . . . 1 match
  5. FuzzyLogic . . . . 1 match
  6. Progress Report . . . . 1 match
  7. PyroBrains . . . . 1 match
  8. PyroDeveloper . . . . 1 match
  9. PyroIndex . . . . 1 match
  10. PyroModuleBehaviorBasedControl . . . . 1 match
  11. PyroModulesContents . . . . 1 match
  12. PyroTOCModules . . . . 1 match