![]() |
|||||||
| [ Home ] | [ Software ] | [ Curriculum ] | [ Hardware ] | [ Community ] | [ News ] | [ Publications ] | [ Search ] |
|
''This is an introduction to more advanced controllers that have memory, or "state". This module provides an overview of Finite State Machine controllers, and the general notion of sequencing control. When completed, the reader should be ready to apply this knowledge, and explore specific ideas in AI and robotics.''
Pyro Sequencing ControlIn order to create more complex robot controllers, it is useful to be able to group low-level robot commands into logical units, often called behaviors. Each behavior is triggered by a particular condition in the environment, and responds appropriately. Once the initiating condition has been addressed, the current behavior can pass control off to another behavior.
Example: Traversing a SquareConsider the task of getting a robot to travel in a square. This task can be broken up into two simpler behaviors which we'll call edge and turn. In the edge behavior, we'll keep track of how far we've traveled aonlg a side of the square. In the turn behavior, we'll keep track of the angle of a turn and stop once we've gone 90 degrees. We can simply switch back and forth between these two behaviors to traverse a square. One straight-forward method of implementing this style of behavior-based control is through finite state machines (FSMs). Each state in the FSM represents a robot behavior. Using a FSM you can build up a graph of states to go in and out of, sequencing the order of states into a "plan". In the program below, we will use the robot's dead reckoning to monitor its position and heading. Recall that dead reckoning can quickly accumulate errors, so the square-like motion will gradually degrade over time. You can try this program using the Stage simulator with the room.cfg world.
from pyrobot.geometry import * # import distance function
from pyrobot.brain.behaviors import State, FSMBrain
class edge (State):
def onActivate(self): # method called when activated or gotoed
self.startX = self.robot.x
self.startY = self.robot.y
def step(self):
x = self.robot.x
y = self.robot.y
dist = distance( self.startX, self.startY, x, y)
print "EDGE: actual = (%f, %f) start = (%f, %f); dist = %f" \
% (x, y, self.startX, self.startY, dist)
if dist > 1.0:
self.goto('turn')
else:
self.robot.move(.3, 0)
class turn (State):
def onActivate(self):
self.th = self.robot.th
def step(self):
th = self.robot.th
print "TURN: actual = %f start = %f" % (th, self.th)
if angleAdd(th, - self.th) > 90:
self.goto('edge')
else:
self.robot.move(0, .2)
def INIT(engine):
brain = FSMBrain(engine=engine)
# add a few states:
brain.add(edge(1)) # 1 makes it active
brain.add(turn())
return brain
[
Notice that each State in a FSMBrain is defined as its own class with its own methods. The next section describes these State methods. Typically every State will include the step method to describe what computation to do on each time step that the state is active. For a much more complex example that uses blob vision and the gripper see ExamplePickingUpPucks.
State MethodsCommonly used state methods:
Also some less used state methods:
Programmable TransitionsConsider the problem of adding a state to get the robot unstuck if it finds itself against a wall. You might need to go to the "stuck" state from every other state. However, you might want to return to the same state from which you came. Currently, the only way to return back to the same state would be to have a unique "getting unstuck state" for each and every state. This is due to the fact that the only way to have memory in an FSM is in the state. However, there is a simple mechanism in the Pyro FSM which allows you to keep track of a "calling" state, so that you can return to it, and thereby bypass this problem. To use this mechanism, you call the push and pop methods. Consider the following step method from a state in an FSM:
def step(self):
x = self.robot.x
y = self.robot.y
dist = distance( self.startX, self.startY, x, y)
print "EDGE: actual = (%f, %f) start = (%f, %f); dist = %f" \
% (x, y, self.startX, self.startY, dist)
if self.robot.stall:
self.push()
self.goto('stuck')
elif dist > 1.0:
self.goto('turn')
else:
self.move(.3, 0)
Here we have added a call to push() before we head off to the stuck state. In the stuck state, we only need call pop() when we are finished getting unstuck, like so:
class stuck(State):
def step(self):
if done:
self.pop()
The push method defaults to pushing the name of the current state on a stack of states. You can also pass in a different state for which you wish control to pass when the state being called is finished. To indicate that the state is done, simply call the pop method. This will retrieve the last state added to the stack, and then goto it.
If you attempt to pop when no push has been issued, an error will be raised. The stack is a part of the brain, and can be accessed via brain.stack. You may recursively push states onto the stack, or may have a series of goto's before issuing a pop().
Advanced Python users might be interested in implementing a FSM with a true "gosub". A discussion of these ideas are discussed in the section PyroModuleFSM:UsingGenerators. | |||||||||||||||||||||||||||||
| [ Home ] | [ Software ] | [ Curriculum ] | [ Hardware ] | [ Community ] | [ News ] | [ Publications ] | [ Search ] |
View Wiki Source | Edit Wiki Source | Mail Webmaster | |||||||