UserPreferences

Pyro Devices Advanced


Pyro Devices Advanced

Every Pyro robot comes with a base set of features: the ability to move, the ability to roughly know where it is (dead reckoning), and a range sensor (infrared, laser, or sonar) to judge distances to objects. But there are a host of other attachments and abilities that a particular robot might support, or that you might want to add. Recall that we call these items devices. You can add these extensions via the following two ways. First, you can simply start it up by selecting it the menu by clicking Load -> Devices... and selecting one from a file. Secondly, you can programmatically start a device, like:

class MyBrain(Brain):

    def setup(self):
        self.startDevice("ptz")

You can start a device if a robot has built-in support for it, or the device is defined independently. You can see what devices a robot supports natively by clicking on the View button to the right of the robot file name in the main GUI window. You'll see a line mentioning builtinDevices with a list of items. Each of those items can be started by simply telling the robot to do so. For example:

self.startDevice("ptz")
self.startDevice("gripper")

will start a Pan-Tilt-Zoom unit and a gripper on the robot. Other devices are defined and loaded from the menu, as described above.

When you start a device, it will appear in under robot as its plain name, index a number. For example, the above startDevice commands would create a tree that looked like:

You would get access to data from the gripper using:

# inside a brain
self.robot.gripper[0]

or

# inside a brain
self.robot.gripper[0]

Adding another gripper would make the directory look like:

Defining your own Device

You can also define your own device. If you have code that you often want to run for many different kinds of brains or robots, you might consider abstracting into a device.

Command line Device Flag

You can, of course, start your services up in the setup() method of a brain. You can also start up services on the command line with the -d flag, like:

pyro -r Player1 -d ptz,blobfinder,laser

Use comma-separated with no spaces for built-in service names.

Writing your own Device

You might want to create a service that provides mapping, plotting, localization, or some other useful service. Here is a sample template for creating your own service:

# A Simple Device

from pyrobot.robot.device import Device

class Test(Device):

    def setup(self):
        self.specialvalue = 42

    def makeWindow(self):
        self.visible = 1
        print "made window!"

    def updateWindow(self):
        print "update window!"
        pass

def INIT(robot):
    return {"test": Test()}

/!\ Note that INIT takes an argument that is a robot. Each service is associated with a particular robot.

Pressing the robot's View button shows:

robot:
   .brain          = None
   .builtinDevices = []
   .devices        = ['test']
   .stall          = 0
   .supportedFeatures = []
   .test[0]:
      .active         = 0
      .groups         = {}
      .specialvalue   = 42
      .state          = 'stopped'
      .title          = 'test[0]'
      .type           = 'test'
      .visible        = 0
      .window         = 0
   .th             = 0
   .thr            = 0
   .timestamp      = 1122767067.13
   .x              = 0
   .y              = 0

You can then issue the following get/sets from the brain or robot:

>>>  self.robot.test[0].name
test[0]
>>>  self.robot.test[0].name = "Laura"
Ok
>>>  self.robot.test[0].name
Laura
>>>  self.robot.test[0].specialvalue
42

Processes that takes a while

Currently, the brain expects to fire about 10 times a second, and if it doesn't, things don't respond well. The GUI will not be responsive, and the robot will act erratic.

/!\ There were some design decisions made early on that cause this. Basically, we just wanted to avoid having to use locks and threads. Pyro assumes that devices will update about 10 times a second, and so will the brain, and so we made everything run in the same thread, including the GUI (basically).

What to do? Imagine that you have some kind of rational logic engine that Pyro will talk to through some process-to-process communication. Now, imagine that this communication will take a while.

One way to work around this is to create a device that will handle the communication. Here is a device skeleton that allows you to talk to it 10/second, and it will in turn talk to some other process which might take seconds, or even minutes:

from pyrobot.robot.device import Device
import time, threading, random
class LogicDevice(Device, threading.Thread):
    def setup(self):
        threading.Thread.__init__(self)
        self.lock = threading.Lock()
        self.data = 0
        self.start()
    def updateDevice(self):
        print "Updated started..."
        self.lock.acquire()
        # look at the shared data
        print "data:", self.data
        self.lock.release()
        print "Updated done!"
    def run(self):
        while self.isAlive():
            print "   Async update started..."
            time.sleep(2) # do something that takes some time
            self.lock.acquire() # acquire the lock
            self.data = random.random() # update the data
            self.lock.release() # release the lock
            print "   Async update done!"
def INIT(robot):
    return {"logic": LogicDevice()}

This has all of the code necessary for starting a thread, locking/unlocking to protect data, and ending the thread. You can load this device like any other device and it will show up as robot.logic[0]. You can even load multiple copies, the next being accessed at robot.logic[1]. Simply loading this causes the output:

   Async update started...
Updated started...
data: 0.493140023963
Updated done!
Updated started...
data: 0.493140023963
Updated done!
... (repeats 17 times total)
Updated started...
data: 0.493140023963
Updated done!
   Async update done!
   Async update started...
Updated started...
data: 0.346892748144
Updated done!
Updated started...
data: 0.346892748144
Updated done!
...

Further Reading

You can find the services interface in pyrobot/robot/device.py.

Up: Pyro Devices