orca-robotics INTRODUCTION Overview Download and Install Quick Start Documentation Publications REPOSITORY Interfaces Components Libraries Utilities Software Map DEVELOPER Tutorials Examples Dev Guide Dashboard Wiki login/pass: orca/orca PEOPLE Contributors Users Project Download Mailing lists
|
Writing Your First Orca Component
IntroThis document will step you through the process of making your first Orca component. In particular, we will build an Orca component called "drunkrobot" that simulates a robot that takes a random walk. We'll use the existing Orca component "position2dmon" to watch the robot as it moves around.Getting startedBefore reading this document, you should familiarise yourself with:
Once you've read that, things here should make a bit more sense. BasicsThe Suggestions on the Internal Component Structure guide contains the following diagram that will become the basis of our implementation.
Our design will be based on this sketch; beginning with the implementation of the interface, then the main loop, then finallly the component. We'll then actually get our component running, talking to the Orca services and also to other components. InterfaceOur drunken robot must implement a particular interface. Let us assume that our robot bumbles around on a flat surface. The appropriate interface in this is case orca::Position2d. The orca::Position2d data file is declared in:src/interfaces/slice/orca/position2d .iceFrom that declaration, which is written in Slice, a language for describing object-oriented interfaces, we can see that our drunken robot must implement the following methods:
interface Position2d { nonmutating Position2dData getData(); nonmutating Position2dGeometry getGeometry(); }; The "nonmutating" bit means that the Ice layer can make some optimisations because getting data or geometry doesn't change our drunken robot at all. Note also that our robot must know about the orca::Position2dData and orca::Position2dGeometry objects. These are also described in the Ice file. Orca uses BROS (Basic Robotic Standard) for representations of geometry and the like. These are also defined in the above file; for instance, orca::Position2dData definition looks like this:
class Position2dData extends OrcaObject { // Robot pose in global CS according to odometry. Frame2d pose; // Translational and angular velocities in the robot CS. Twist2d motion; // Are the motors stalled bool stalled; }; Setting upFor the moment, we'll be setting up in a subdirectory of the src/examples, just to make building easier. But this doesn't have to be this way. So first we go to src/examples, and make a new directory calledmydrunkrobot (a copy of all of the source code for this walkthrough can be found in drunkrobot ).
% mkdir mydrunkrobot Creating the interface classesAs discussed in the design pattern, our implementation of the interface simply puts data into a buffer and our program then accesses it in its main loop. Since we're implementing a orca::Position2d object, we need to create its simple implementation. We'll put this in a class calledPosition2dI . The header file position2dI.h looks like this:
After we include the header files, we then declare the constructor. The first is a pointer to the 2d data, but set to use a template of a Position2dData object, and the second, since it's not going to change is the static 2d geometry. The getData() and getGeometry() functions are also implemented. You can ignore the Ice::Current parameter or read about it in the Ice documentation. There are also members for the buffered results.
No we implement the above interface. There's nothing unexpected here; to get the latest position, we check the pointer buffer, to return the configuration, we pass the value that was initialised when the interface was created. The file
Creating the main loop.Now we've implemented the interface, we have to write the main loop that actually does the processing. Main loops inherit from the orcaice::Thread class and only need to implement one method: run(). When run finishes, the thread will terminate.The other thing which is unusual about the main loop is that is also responsible for "push" data. In Orca 2, data is pushed through IceStorm, Ice's publish-subscribe system. This is done by using a proxy for a consumer of orca::Position2dData. We use the proxy (hence the Prx at the end) to represent remote IceStorm objects.
Hence the file
The implementation in this example is trivial. The constructor sets things up. Then our main loop does the following:
The file
Putting the component togetherThe component glues the interface class and the main loop together. Mostly, it is boiler-plate code; code that connects one object to the other. The other important characteristic of the the component is that it inherits from the orcaice::Component class. This will allow us later to choose how we wish to run the drunkrobot: as a stand-alone process or within an application servr (called IceBox in Ice) - an environment where lots of different components can live and share resources more effectively than if they were separate processes. The header for the component is as follows:
The implementation first appears to be intimidating, but if you do find it intimidating, it might be worth reading through some examples of how Ice sets up connections in the Ice manual. Our start() method basically does the following:
Setting up the build system.To build the software usingCMake , we need to write a CMakeLists.txt file. As it turns out, we can use a generic one, for example, the one that can be found in [ORCA-SRC]/src/components/position2dmon. Make a copy of the one in position2dmon and make some minor modifications as shown:
The next thing we have to do is go one level up to Finally, we run cmake in the top src directory to recreate all Makefele's.
% cd [ORCA-SRC] % cmake . % make
We should now have a executable file called ConfigurationWe are almost there. Now we set up the config files for our robot.
We need to specify the name of the platform, the component name and finally that we plan to use the tcp protocol. Since we implement the orca::Position2d interface, we specify the platform and component, and then the actual interface we implement. These all go in
And finally we are ready! First, however, we have to launch the support services, namely IceStorm and IcePack. In order to do this, follow the instructions in Quick-Start Guide. Then all we have to type is:
% ./drunkrobot And we now have a robot that is generating random positions every second or so. Connecting to another component.Let's now try to connect another component. Orca distribution includesposition2dmon , a command line tool for displaying output from Position2d interfaces, just like the one our drunk robot provides. To get these two things to talk to one another shouldn't be hard at all, in fact all it requires is to define a new position2dmon config file.
If we go into the position2dmon directory, we can create a file called The contents of the file might be something like:
# component Position2dMon.PlatformName=local Position2dMon.ComponentName=position2dmon Position2dMon.Endpoints=default # requires Position2dMon.Requires.Position2d.Proxy=position2d@local/drunkrobot Assuming that the drunkrobot process above is still running, we can watch the position information using:
% ./position2dmon --Ice.Config=position2dmon-drunkrobot.cfg We can hit "g" to get the data once, or hit "s" to get a constant stream of data. Woohoo! We now have two components talking to one another. Further thingsThere is still a little that you should learn about when using Orca. These include
But ... that's the subject of another walkthrough. |
Webmaster: Tobias Kaupp (tobasco at users.sourceforge.net)