An Introduction to Programming the Sony AIBO using the OPEN
Transcription
An Introduction to Programming the Sony AIBO using the OPEN
An Introduction to Programming the Sony AIBO using the OPEN-R environment Sony AIBO OPEN-R Tutorial This tutorial aims to introduce OPEN-R programming to a student familiar with the concepts and language of C++. OPEN-R is the C++ interface for controlling every aspect of a Sony AIBO Robot, pictured on the front page. This tutorial has been designed to accommodate both Windows (using Cygwin) and Linux. Table of Contents 1. Installation ....................................................................................................................... 1 1.1. Requirements ......................................................................................................... 1 1.2. Installing Cygwin .................................................................................................... 2 1.3. Installing the OPEN-R SDK ...................................................................................... 9 1.4. Running a sample program ........................................................................................ 9 1.4.1. Copying programs to the memory stick under Windows ......................................10 1.4.2. Copying programs to the memory stick under Linux ...........................................11 1.5. Compiling a sample program ....................................................................................14 1.5.1. Compiling under Windows/Cygwin .................................................................14 1.5.2. Compiling under Linux .................................................................................15 2. Lights .............................................................................................................................18 2.1. Structure of an OPEN-R program ..............................................................................18 2.1.1. Object structure and message passing ..............................................................18 2.1.2. Object template ...........................................................................................19 2.2. Blinking LEDs example ..........................................................................................21 2.2.1. What the code needs to do .............................................................................21 2.2.2. Writing the code ..........................................................................................22 2.2.3. Running the code .........................................................................................24 2.3. Other files in an AIBO project ..................................................................................24 2.3.1. stub.cfg ......................................................................................................24 2.3.2. <objectName>.ocf .......................................................................................25 2.3.3. makefile .....................................................................................................25 2.3.4. OBJECT.CFG .............................................................................................26 2.3.5. CONNECT.CFG .........................................................................................26 2.3.6. Directory Structure .......................................................................................27 3. Movement .......................................................................................................................28 3.1. Movement of the joints and joint addressing ................................................................28 3.2. Moving the AIBO's head .........................................................................................28 3.3. Moving the AIBO's legs ..........................................................................................34 3.4. Moving the AIBO's head and legs together ..................................................................37 4. Vision and Lights .............................................................................................................38 4.1. Adding vision to the code ........................................................................................38 4.2. An Introduction to MoNet ........................................................................................43 5. Troubleshooting ...............................................................................................................46 Glossary .............................................................................................................................47 iv Chapter 1. Installation The Sony AIBO (www.aibo.com) series of autonomous robots was created initially as entertainment robots for home users, but has also been embraced by researchers looking for a low-cost programmable robot platform. AIBO™ stands for Artificial Intelligence roBOt. An AIBO is equipped with a camera, a pair of stereo microphones and a set of touch sensors. With this equipment and sophisticated processing and coordination programs, it demonstrates three types of human-like senses: vision, sound and touch. The AIBO series can perform image processing, acoustic signal processing and posture estimation using its in-built accelerometers. AIBO robots interact with humans and fellow AIBO's using body gestures, face lights and simple musical melodies. Emotion is displayed through the face light colours, e.g. red for angry or frustrated, green for happy or purple for sad. To create programs for your AIBO, you will require a compilation environment. This tutorial will cover how to set up this compilation environment in the labs at the CSSE department of The University of Western Australia and test your environment by compiling a sample AIBO program for you to try out. By the end of this module, you should be able to • compile an AIBO program. • load an AIBO program onto a memory stick. • execute the program on the AIBO from a memory stick. 1.1. Requirements In order to create programs and run them on the AIBO you will need the following: • A Linux-like environment. This can be either: • a CSSE lab machine booted into Linux; or • a CSSE lab machine booted into Windows, with the Cygwin1 environment installed. • OPEN-R SDK2 (preinstalled in the CSSE Linux environment). • A Sony AIBO programmable memory stick (pictured below) 3 1 2 3 http://www.cygwin.com/ http://openr.aibo.com/ • A USBmemory memory stick below) This is a special-purpose stick that reader must be (pictured obtained from Sony for the AIBO. At time of writing, only 16MB programmable memory sticks are available. 1 Installation • A background in C++ programming If you are running under Windows, you will need to install both Cygwin and the OPEN-R SDK yourself. The following sections explain how to accomplish this. If you are running under Linux, you may jump straight to Section 1.4 to verify your environment is working. 1.2. Installing Cygwin Note This section is only relevant for running on Windows. If you are running under Linux, you may jump straight to Section 1.4. The OPEN-R SDK requires a Unix-like environment to compile. On a Windows machine this environment is provided by Cygwin. Cygwin is the standard Linux framework of source code, libraries and programs that Linux programs require to compile and run on Windows. This tutorial assumes the following: • You are logged on to a Windows machine. • You have access to the internet or the UWA network. • You have at least 500 Mb free hard drive space. 2 Installation 1. Download and run the Cygwin installer from http://mirrors.uwa.edu.au/cygwin/setup.exe. You may have to pass through several warnings to allow this program to run. The following screen will appear. Click the Next button. 2. You are asked to choose a download source. Select 'Install from Internet' and click Next. We will be installing from the local UWA mirror, so your proxy quota will not be used. 3 Installation 3. You are prompted to enter a root directory. This is the installation location of Cygwin. If you install under your Windows Home directory, Cygwin will be available whenever you log on to a Windows machine. The recommended path is H:\cygwin. The other options can be left as they are. Click Next. 4. You are prompted to enter a 'Local Package Directory'. This is a local cache for the downloaded installation files. Having them stored locally makes it easier to alter your installation later. By default the path will be under your 'My Documents' folder, which is not recommended. Enter something on your network drive such as H:\cygwin\temp-cache. Click Next. 4 Installation 5. You are asked to configure a proxy. If you are on the UWA network, you will need to configure a proxy to install Cygwin. Even though we will be downloading Cygwin from a local UWA mirror, the Cygwin installer must connect to the internet to download a list of possible mirrors. If you know that Internet Explorer is using the correct proxy, select 'Use IE5 Settings'. Otherwise, select 'Use HTTP/FTP Proxy' and enter 'Proxy Host:' proxy.csse.uwa.edu.au and 'Port': 8888. Click Next. 5 Installation 6. If you are using a proxy, you will be prompted for a username and password. Enter your CSSE Linux username and password. 7. You are asked to select a Cygwin mirror. In the 'User URL' box type http://mirrors.uwa.edu.au/cygwin/ and click Add. The URL will be added to the list and selected. Click Next. 6 Installation 8. Cygwin will download a list of packages. A package is some documents, source code, library or program that you or other programs will use. You can click the plus signs to expand the various categories, and you can click the recycle arrows to change the installation option for a specific package. Clicking many times cycles between skipping the package (e.g. not installing it) or installing different versions. You should always install the latest version. The following packages are required for compiling programs for the OPEN-R SDK: • Under Devel, select gcc-g++ and make. • Under Interpreters, select perl. Many other packages are selected by default, and some of these are required, such as most of the Base category (e.g. gzip, tar). You can select any other packages that you will find useful for authoring programs in C++, or you can use Windows programs. Once you have selected your packages, click Next. 9. Cygwin will now install the packages you selected. Depending on your internet connection and packages selected, this may take some time. For a minimal install on the UWA network we experienced installation times of approximately 10 minutes. 7 Installation 10. You will be prompted to create icons on the desktop and start menu. At least one of the two is recommended, so you can start Cygwin easily. Click Finish. Now that Cygwin is installed, you may proceed to the next section to install OPEN-R. 8 Installation 1.3. Installing the OPEN-R SDK Note This section is only relevant for running on Windows. If you are running under Linux, the OPEN-R SDK is already installed for you, so you may jump straight to Section 1.4. The OPEN-R SDK is a framework of C++ source code, libraries and utilities for writing programs for the Sony AIBO. You will need to download the following files into the directory H:\cygwin\usr\local, where H:\cygwin is the folder you installed Cygwin to. The OPEN-R SDK assumes it is installed in the Cygwin folder /usr/local, hence it is easiest to install OPEN-R here. Sony does not allow the OPEN-R SDK to be hosted publicly on the Internet. Consequently, if you are not on the UWA Network you will have to download the OPEN-R SDK yourself from the OPEN-R website4. Note that you will have to register to download the SDK. • mipsel-devtools-3.3.2-bin-r1.tar.gz2 • OPEN_R_SDK-1.1.5-r3.tar.gz3 • OPEN_R_SDK-docE-1.1.5-r1.tar.gz4 • OPEN_R_SDK-sample-1.1.5-r2.tar.gz5 Once these files have finished downloading, run Cygwin using the desktop or start menu shortcut. A terminal window appears, running the bash shell. To change to the /usr/local directory, type: > cd /usr/local Then to extract the OPEN-R SDK and the MIPS compiler, type: > > > > tar tar tar tar -zxf -zxf -zxf -zxf OPEN_R_SDK-1.1.5-r3.tar.gz mipsel-devtools-3.3.2-bin-r1.tar.gz OPEN_R_SDK-sample-1.1.5-r2.tar.gz OPEN_R_SDK-docE-1.1.5-r1.tar.gz Proceed to the next section to verify your OPEN-R installation is working correctly. 1.4. Running a sample program In this section you will download a sample AIBO program to test your environment and the AIBO. The sample program will get the AIBO to stand up and play a sound. 4 2 3 4 5 http://openr.aibo.com/openr/eng/index.php4 http://pc307a.csse.uwa.edu.au/tutorials/files/tute1/mipsel-devtools-3.3.2-bin-r1.tar.gz http://pc307a.csse.uwa.edu.au/tutorials/files/tute1/OPEN_R_SDK-1.1.5-r3.tar.gz http://pc307a.csse.uwa.edu.au/tutorials/files/tute1/OPEN_R_SDK-docE-1.1.5-r1.tar.gz http://pc307a.csse.uwa.edu.au/tutorials/files/tute1/OPEN_R_SDK-sample-1.1.5-r2.tar.gz 9 Installation You will need to download this zip file6 and unzip the contents somewhere within your home directory. Under Windows, unzipping is supported natively. Under Linux, use the unzip command. Click here if you are using a Windows system. Click here if you are using a Linux system. 1.4.1. Copying programs to the memory stick under Windows Note This section is only relevant for running on Windows. If you are running under Linux, you should see Section 1.4.2. 1. Insert an AIBO programmable memory stick into your PC (via a memory stick reader). After a few seconds, it should appear under My Computer. Tip You must put the memory stick into the memory reader before inserting the memory reader into the PC. Otherwise, Windows will not detect the memory stick. 6 2. Delete all files from the memory stick. 3. Copy the contents of the zipfile (available here) onto the root directory of the memory stick (ie, the only folder in the root directory of the memory stick should be called OPEN-R). 4. Use the "Safely Remove Hardware" tray icon (shown below) to stop the memory stick reader, then disconnect the reader and remove the memory stick. 5. Insert the memory stick into the slot on the bottom of the AIBO (under the cover), as pictured below. http://pc307a.csse.uwa.edu.au/tutorials/files/tute1/Example.zip 10 Installation 6. Turn the AIBO on using the power button below the back of its neck. 7. Make sure that the volume on the AIBO's speakers is turned up. The volume control is located under the AIBO near the memory stick slot. Since we are not using the wireless features of the AIBO this switch should also be off. 8. If the AIBO plays the melody from 'Phantom of the Opera' and shuts down, then the AIBO's batteries are depleted. Replace or recharge them. If the AIBO makes a sequence of tones getting lower and lower, the memory stick is not set up correctly. See the troubleshooting section for more information. 9. If the AIBO stands up and then plays a sound, everything is working correctly. 10. Turn off the AIBO by pressing the power button again. It will start beeping with a sequence of descending tones. Once a sequence of ascending tones are heard, the AIBO is switched off, and you may remove the memory stick or battery for the next step. Next, we will download the source for the above example program, then compile and install it. Click here to continue. 1.4.2. Copying programs to the memory stick under Linux Note This section is only relevant for running on Linux. If you are running under Windows, you should see Section 1.4.1. 1. You will need to create a device on the desktop for your memory stick. Do to this, right click on the Desktop, select Create New -> Device -> MO Device. 11 Installation 2. Change the name of the device from "MO Device" to "Memory Stick". On the Device tab, select /media/usb as the device. Click Ok. You should find a new icon on your desktop labeled "Memory Stick". 3. Insert an AIBO programmable memory stick into your PC (via a memory stick reader). Wait a few seconds, then double click on the newly created Memory Stick icon. Tip You must put the memory stick into the memory reader before inserting the memory reader into the PC. Otherwise, Linux will not detect the memory stick. If you are greeted with an error message instead of the contents of the memory stick, wait a few more seconds before trying again. 12 Installation 4. Delete all files from the memory stick. 5. Copy the contents of the zipfile (available here) onto the root directory of the memory stick (ie, the only folder in the root directory of the memory stick should be called OPEN-R). 6. Unmount the memory stick by right clicking on the Memory Stick icon on the desktop, and selecting "Unmount". Once it has finished writing to the memory stick, disconnect the memory stick reader and remove the memory stick. 7. Insert the memory stick into the slot on the bottom of the AIBO (under the cover), as pictured below. 13 Installation 8. Turn the AIBO on using the power button below the back of its neck. 9. Make sure that the volume on the AIBO's speakers is turned up. The volume control is located under the AIBO near the memory stick slot. Since we are not using the wireless features of the AIBO this switch should also be off. 10. If the AIBO plays the melody from 'Phantom of the Opera' and shuts down, then the AIBO's batteries are depleted. Replace or recharge them. If the AIBO makes a sequence of tones getting lower and lower, the memory stick is not set up correctly. See the troubleshooting section for more information. 11. If the AIBO stands up and then plays a sound, everything is working correctly. 12. Turn off the AIBO by pressing the power button again. It will start beeping with a sequence of descending tones. Once a sequence of ascending tones are heard, the AIBO is switched off, and you may remove the memory stick or battery for the next step. Next, we will download the source for the above example program, then compile and install it. Click here to continue. 1.5. Compiling a sample program In this section, you will compile an example AIBO program to verify your compilation environment is set up correctly. The sample program will get the AIBO to stand up and play a sound. You will need to download this zip file7. Click here if you are using a Windows system. Click here if you are using a Linux system. 1.5.1. Compiling under Windows/Cygwin Note This section is only relevant for running on Windows. If you are running under Linux, you should see Section 1.5.2. 1. Extract the zip file (available here) somewhere under the /usr/local/ directory of your Cygwin installation. For example, into H:\Cygwin\usr\local\tutorials\tute1\ 2. With Cygwin, enter the Example directory that was unzipped. For example, type > cd /usr/local/tutorials/tute1/Example 3. From Cygwin, run the make command by typing > make 4. The console will display the compilation process. If the process succeeds, the last line displayed will be /usr/local/OPEN_R_SDK/bin/mipsel-linux-strip PlaySound.bin 7 http://pc307a.csse.uwa.edu.au/tutorials/files/tute1/ExampleSrc.zip 14 Installation 5. From Cygwin, run make install by typing > make install 6. The compiled binary has now been compressed and placed in the MS directory one level up. 7. Insert the AIBO memory stick again. Delete all the files on it. 8. Open the MS directory that H:\cygwin\usr\local\tutorials\tute1\MS\. 9. Copy the OPEN-R directory from the MS directory to the memory stick, as demonstrated in Section 1.4.1. was unzipped, for example open 10. Insert the memory stick into the AIBO and start it up. 11. If the AIBO plays the melody from 'Phantom of the Opera' and shuts down, then the AIBO's batteries are depleted. Replace or recharge them. 12. If the AIBO stands up and then plays a sound, everything is working correctly. Congratulations, you now have a working compilation environment and AIBO! Next, you will learn how to write a simple OPEN-R program yourself. Continue to Chapter 2. 1.5.2. Compiling under Linux Note This section is only relevant for running on Linux. If you are running under Windows, you should see Section 1.5.1. 1. Extract the zip file (available here) somewhere under your home directory. For example, into 15 Installation aibo/tutorials/tute1/ student@csse:~> cd aibo/tutorials/tute1/ student@csse:~/aibo/tutorials/tute1> unzip ExampleSrc.zip ... (Alternately you may use the graphical interface to extract these files). 2. From a terminal, enter the Example directory that was unzipped. For example, type student@csse:~> cd ~/aibo/tutorials/tute1/Example 3. Run the make command by typing student@csse:~/aibo/tutorials/tute1/Example> make 4. The console will display the compilation process. If the process succeeds, the last line displayed will be /usr/local/OPEN_R_SDK/bin/mipsel-linux-strip PlaySound.bin 5. Run make install by typing student@csse:~/aibo/tutorials/tute1/Example> make install 6. The compiled binary has now been compressed and placed in the MS directory one level up. 7. Insert the AIBO memory stick again. Delete all the files on it. 8. Open the MS directory that was unzipped, (eg, ~/aibo/tutorials/tute1/MS/). 9. Copy the OPEN-R directory from the MS directory to the memory stick, as demonstrated in Section 1.4.2. 16 Installation 10. Insert the memory stick into the AIBO and start it up. 11. If the AIBO plays the melody from 'Phantom of the Opera' and shuts down, then the AIBO's batteries are depleted. Replace or recharge them. 12. If the AIBO stands up and then plays a sound, everything is working correctly. Congratulations, you now have a working compilation environment and AIBO! Next, you will learn how to write a simple OPEN-R program yourself. Continue to Chapter 2. 17 Chapter 2. Lights In this tutorial you will learn how to begin programming on the AIBO using the OPEN-R SDK. Each OPEN-R object must have a few important parts to run on the AIBO properly. We will first introduce the structure of an OPEN-R object and then you will write a simple program in OPEN-R which makes the AIBO's face lights flash. 2.1. Structure of an OPEN-R program An OPEN-R program works by creating multiple OPEN-R objects to perform different operations on the AIBO. These objects are then executed concurrently, and the use of message passing enables the AIBO to carry out multiple tasks simultaneously. 2.1.1. Object structure and message passing The general structure of an OPEN-R object must contain a few important functions and variables, in order for the AIBO to operate. The standard functions required in every object are DoInit(), DoStart(), DoStop() and DoDestroy(). The standard variables are the arrays of subjects and observers, which are lists of other objects that we will be sending messages to and receiving messages from. The notion of subjects and observers is best illustrated by the following diagram: In order for an OPEN-R object to communicate with other OPEN-R objects it needs to facilitate message sending, which requires there to be a subject (sender of the message), and an observer (receiver of the message). When the observer is ready to receive a message it sends a special message, ASSERT_READY, to the sender to inform it of this. Once the subject has received said special message, it is then able to send a message to the observer. Another way to initiate communication is for the subject to ask the observer for confirmation that it is ready to receive and process a message. In the OPEN-R SDK each object runs in its own memory space, which means objects cannot access each others memory. Hence to pass data between objects special shared memory objects must be created. These objects, called RCRegion by OPEN-R, hold data to be passed from one object to another, such as from the camera to your object. TO ensure that these regions cannot be accessed by more than one object at a time they are locked when in use, hence an object usually creates several 18 Lights regions and must find a free one before sending data with it. 2.1.2. Object template The following code is a skeleton template of the header file section needed to implement an OPENR object in C++, which is the programming language used by OPEN-R: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #ifndef Template_h_DEFINED #define Template_h_DEFINED #include #include #include #include #include <OPENR/OObject.h> <OPENR/OSubject.h> <OPENR/OObserver.h> <OPENR/ODataFormats.h> "def.h" class Template : public OObject { public: Template(); virtual ~Template() {} virtual virtual virtual virtual OStatus OStatus OStatus OStatus DoInit (const DoStart (const DoStop (const DoDestroy(const OSystemEvent& OSystemEvent& OSystemEvent& OSystemEvent& event); event); event); event); OSubject* subject[numOfSubject]; OObserver* observer[numOfObserver]; private: }; #endif // Template_h_DEFINED The included files in lines 4 to 8 are standard to all OPEN-R objects since they include important macros and definitions commonly used in programming on the AIBO. Lines 15 to 18 declare the four standard functions common to all OPEN-R objects. They are used by the AIBO during start-up and shut down procedures and need to be present in order for the object to be initialized. Subject and observer are lists of all the other objects which this object will be communicating with. This header file is then implemented in a C++ file, Template.cc: 1 2 3 4 #include #include #include #include <OPENR/OPENRAPI.h> <OPENR/OSyslog.h> <OPENR/core_macro.h> "Template.h" Again the above statements are required to include macros and definitions important to programming with the AIBO. 1 2 3 Template::Template() { } The constructor for your object is used to initialise your local primitive variables, but cannot at this stage perform any actions which involve communication with other objects since they may or may not have been initialised yet. 19 Lights 1 2 3 4 5 6 7 8 9 10 11 12 13 14 OStatus Template::DoInit(const OSystemEvent& event) { NEW_ALL_SUBJECT_AND_OBSERVER; REGISTER_ALL_ENTRY; SET_ALL_READY_AND_NOTIFY_ENTRY; /* own code here */ OStatus st = OPENR::SetMotorPower(opowerON); return oSUCCESS; } DoInit() is the first method called by the AIBO system. The macros from lines 5-7 set up the communication channels and ensure that all subjects and observers are initialised. Line 11 only needs to be included in one object in a multiple object program, as they turn the power on to all the AIBO's joints. When expanding the template it may be necessary to include further code to create shared memory objects and to connect to primitives, such as address references for the LEDs. 1 2 3 4 5 6 7 8 OStatus Template::DoStart(const OSystemEvent& event) { ENABLE_ALL_SUBJECT; ASSERT_READY_TO_ALL_OBSERVER; /* own code here */ return oSUCCESS; } DoStart() is called after all objects have been initialized by DoInit(). This means it is now possible to send messages to observers, to inform them that the object is ready for communication (achieved in lines 5-6). At this stage an initial state can also be achieved, such as standing the AIBO up, turning on the LEDs or anything else that is required before the AIBO can begin to process data from other sources. 1 2 3 4 5 6 7 8 9 OStatus Template::DoStop(const OSystemEvent& event) { DISABLE_ALL_SUBJECT; DEASSERT_READY_TO_ALL_OBSERVER; return oSUCCESS; } When the system shuts down the AIBO calls DoStop() for each object. The objects should then move themselves to an idle state, and cease communication with all subjects and observers. This is specified in lines 6-7. 1 OStatus 20 Lights 2 3 4 5 6 Template::DoDestroy(const OSystemEvent& event) { DELETE_ALL_SUBJECT_AND_OBSERVER; return oSUCCESS; } DoDestroy() is called after DoStop() has been called on all objects. Any memory that needs to be freed can be done during this method. The files described above can be downloaded below. There are some additional files that a program needs to compile and run, these will be described later in this tutorial. Template.zip1 • 2.2. Blinking LEDs example The previous template can then be extended to a simple program that can make the LEDs on the AIBO's face and back flash. 2.2.1. What the code needs to do In order to extend the template code a few extra methods need to be written. The following additions need to be made to the header file, BlinkingLeds.h. Here we define some states that our object will be in, so we don't try to talk to the lights while the AIBO is shutting down. 1 2 3 4 5 6 enum LEDState { LEDState_IDLE, LEDState_RUN, LEDState_STOP, }; This constant array is a list of addresses to each of the AIBO's face and back LEDs which we will be referencing during this tutorial. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 static const char* const LED3_LOCATOR[] = { "PRM:/r1/c1/c2/c3/la-LED3:la", // Face light1 "PRM:/r1/c1/c2/c3/lb-LED3:lb", // Face light2 "PRM:/r1/c1/c2/c3/lc-LED3:lc", // Face light3 "PRM:/r1/c1/c2/c3/ld-LED3:ld", // Face light4 "PRM:/r1/c1/c2/c3/le-LED3:le", // Face light5 "PRM:/r1/c1/c2/c3/lf-LED3:lf", // Face light6 "PRM:/r1/c1/c2/c3/lg-LED3:lg", // Face light7 "PRM:/r1/c1/c2/c3/lh-LED3:lh", // Face light8 "PRM:/r1/c1/c2/c3/li-LED3:li", // Face light9 "PRM:/r1/c1/c2/c3/lj-LED3:lj", // Face light10 "PRM:/r1/c1/c2/c3/lk-LED3:lk", // Face light11 "PRM:/r1/c1/c2/c3/ll-LED3:ll", // Face light12 "PRM:/r1/c1/c2/c3/lm-LED3:lm", // Face light13 "PRM:/r1/c1/c2/c3/ln-LED3:ln" // Face light14 "PRM:/lu-LED3:lu", // Back light (front, color) "PRM:/lv-LED3:lv", // Back light (front, white) "PRM:/lw-LED3:lw", // Back light (middle, color) "PRM:/lx-LED3:lx", // Back light (middle, white) "PRM:/ly-LED3:ly", // Back light (rear, color) "PRM:/lz-LED3:lz" // Back light (rear, white) }; We also need to provide the definition of any new functions which we will use. In this case we need 1 http://pc307a.csse.uwa.edu.au/tutorials/files/tute2/Template.zip 21 Lights one more public function and a number of private ones. The public method Ready() will be the main function called when the AIBO is ready for more data. This needs to be defined in stub.cfg: void Ready(const OReadyEvent& event); 1 Additionally, these private methods and variables need to be added to facilitate communication to the LEDs: //Connects to our primitives - the leds void OpenPrimitives(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 //Create some CommandVectors - shared memory objects //used for inter-object communication void NewCommandVectorData(); //Helper method to set the led intensity void SetLED3Value(RCRegion* rgn, sword intensity, OLED3Mode mode); static const size_t NUM_LEDS = 14; LEDState OPrimitiveID RCRegion* ledState; ledID[NUM_LEDS]; region; 2.2.2. Writing the code The constructor for the class can be used to initialize the LED reference array and the region array to null. We then need to implement the DoInit(), DoStart(), DoStop() and DoDestroy() methods. In addition to the code supplied in the template, DoInit() needs to include calls to OpenPrimitives(), to connect to the primitives, and NewCommandVectorData(), to create shared memory objects for inter-object communication. DoStart() changes the LED state to RUNNING, and sends an initial message to the LEDs. Once the LEDs have finished processing this message they will ASSERT_READY, which will trigger our Ready() message and allow us to pass more data to the LEDs. DoStop() sets the LED state to STOP, which will stop the cycle of communication the next time Ready() is called. DoDestroy() is the same as provided in the template. OpenPrimitives() simply needs to obtain a reference to each of the AIBO's LEDs by calling OPENR::OpenPrimitive(LED3_LOCATOR[i], &ledID[i]) for each LED address in the LED3_LOCATOR array. 1 2 3 4 5 6 7 8 9 10 void BlinkingLeds::OpenPrimitives() { OStatus result; for (int i = 0; i < NUM_LEDS; i++) { result = OPENR::OpenPrimitive(LED3_LOCATOR[i], &ledID[i]); } } Code for NewCommandVectorData() is supplied and it is used for creating shared memory objects used in communication. This also needs to be encapsulated in a reference counted memory region (RCRegion) so that it can be freed when it is no longer required. 22 Lights 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 BlinkingLeds::NewCommandVectorData() { MemoryRegionID cmdVecDataID; OCommandVectorData* cmdVecData; OPENR::NewCommandVectorData(NUM_LEDS, &cmdVecDataID, &cmdVecData); region = new RCRegion(cmdVecData->vectorInfo.memRegionID, cmdVecData->vectorInfo.offset, (void*)cmdVecData, cmdVecData->vectorInfo.totalSize); cmdVecData->SetNumData(NUM_LEDS); for (int j = 0; j < NUM_LEDS; j++) { OCommandInfo* info = cmdVecData->GetInfo(j); info->Set(odataLED_COMMAND3, ledID[j], ocommandMAX_FRAMES); } } To change the intensity of the LEDs we use the method SetLED3Value(). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void BlinkingLeds::SetLED3Value(RCRegion* rgn, sword intensity, OLED3Mode mode) { OCommandVectorData* cmdVecData = (OCommandVectorData*)rgn->Base(); for (int i = 0; i < NUM_LEDS; i++) { OCommandData* data = cmdVecData->GetData(i); OLEDCommandValue3* val = (OLEDCommandValue3*)data->value; for (int j = 0; j < ocommandMAX_FRAMES; j++) { val[j].intensity = 16 * ((intensity + j) % 15); val[j].mode = mode; val[j].period = 1; } } } Ready() is the main method, specified in stub.cfg, to be called when the LEDs are ready to receive more data. Firstly we need to check if the LEDs state is set to RUN mode, otherwise we need to wait before we can send data to the LEDs. Then we need to find and store a free region, if one exists. If a region does exist then we can change the intensity of the LEDs by calling SetLED3Value(region, intensity, oled3_MODE_A). After that we then set the data held in the subject[sbjBlink] to reflect the changes in the region by calling SetData(RCRegion* rgn). Finally we notify the observer that there is new data ready by calling NotifyObservers(). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void BlinkingLeds::Ready(const OReadyEvent& event) { if(ledState != LEDState_RUN) return; static int intensity = 10; intensity = (intensity + 1) % 15; RCRegion* rgn = FindFreeRegion(); if(rgn != 0) { SetLED3Value(rgn, intensity, oled3_MODE_A); subject[sbjBlink]->SetData(rgn); subject[sbjBlink]->NotifyObservers(); } 23 Lights 17 } 2.2.3. Running the code You can download the source code created above here, or you can use your own. However you will need some additional files for compiling and running your code on the AIBO. These files are provided here and explained in the next section of the tutorial. 1. Download this zip file2 and extract it to a folder in your Cygwin installation. For example H:\cygwin\usr\local\tutorials\tute2\ 2. Download BlinkingLeds.h3 and BlinkingLeds.cc4 or write your own and place them in the extracted BlinkingLeds directory. 3. Open Cygwin and navigate to the extracted BlinkingLeds directory. Run make. 4. Run make install. 5. If you have a previous tutorial installed and working on the AIBO memory stick, you can simply copy the OPEN-R directory from the extracted MS directory onto the root of the memory stick. Alternatively you can copy the AIBO operating system from / usr/local/OPEN_R_SDK/OPEN_R/MS_ERS7/BASIC/memprot onto the root of the memory stick, Then copy the OPEN-R directory from the extracted MS directory onto the memory stick. Overwrite any files that conflict. 6. Insert the memory stick into the AIBO and start it up. 2.3. Other files in an AIBO project Compiling, installing and running an AIBO program requires some additional files after the actual code. This document will describe the purpose and contents of each of these files. 2.3.1. stub.cfg The stub.cfg file specifies the connection points (subjects and observers) of an OPEN-R object. A typical stub.cfg file looks like this: 1 2 3 4 5 6 7 ObjectName : BlinkingLeds NumOfOSubject : 1 NumOfOObserver : 1 Service : "BlinkingLeds.Blink.OCommandVectorData.S", null, Ready() Service : "BlinkingLeds.DummyObserver.DoNotConnect.O", null, null The object name must match the name of the C++ class used to define the object. The following two lines define the number of subjects and observers that follow. The rest of the file names the connection points and specifies the methods in the C++ class that will handle the communication. The name has four parts: 2 3 4 1. The first should be the class name of the object. 2. The second should be an arbitrary service name that is unique to the object. http://pc307a.csse.uwa.edu.au/tutorials/files/tute2/BlinkingLeds.zip http://pc307a.csse.uwa.edu.au/tutorials/files/tute2/BlinkingLeds.h http://pc307a.csse.uwa.edu.au/tutorials/files/tute2/BlinkingLeds.cc 24 Lights 3. The third specifies the data-type that will be passed between objects. 4. The last is either an S (for subject) or an O (for observer). Following the object name are two member names, which can be replaced with null if they are not needed. The first method handles setting up the communication channel - it tells the other object if the current object is ready or busy. If the default behaviour for message passing is acceptable (and it usually is) then null is used here. The second method handles the message. When another object is ready for more data, your subject method will be called to provide it. When another object has more data ready, your observer will be called to consume it. You must specify at least one subject and one observer. If you do not need any of one or the other, a dummy entry must be made, with data type DoNotConnect and null method handlers. During compilation the stub.cfg file is fed to an OPEN-R utility called stubgen2. This program creates several C++ source and header files that define the subjects and observers in code, which are then included into your object at link time. 2.3.2. <objectName>.ocf The .ocf file is used by the mips compiler while compiling your object. A typical file, named blinkingLeds.ocf, looks like this: 1 object blinkingLeds 3072 16384 128 cache tlb user These parameters specify such things as the size of the stack and whether your object should run in kernel or user mode. The parameters given here will be fine for almost all purposes. Note that the second parameter should be replaced with the name of your object. 2.3.3. makefile The makefile file is a set of instructions for the make utility which is used to build your object. You do not strictly need to use the makefile, but copying and modifying existing makefiles is one of the easiest ways to compile your objects. A typical makefile may look like this: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 OPENRSDK_ROOT?=/usr/local/OPEN_R_SDK INSTALLDIR=../MS CXX=$(OPENRSDK_ROOT)/bin/mipsel-linux-g++ STRIP=$(OPENRSDK_ROOT)/bin/mipsel-linux-strip MKBIN=$(OPENRSDK_ROOT)/OPEN_R/bin/mkbin STUBGEN=$(OPENRSDK_ROOT)/OPEN_R/bin/stubgen2 MKBINFLAGS=-p $(OPENRSDK_ROOT) LIBS=-L$(OPENRSDK_ROOT)/OPEN_R/lib -lObjectComm -lOPENR CXXFLAGS= \ -O2 \ -g \ -I. \ -I$(OPENRSDK_ROOT)/OPEN_R/include/R4000 \ -I$(OPENRSDK_ROOT)/OPEN_R/include # # When OPENR_DEBUG is defined, OSYSDEBUG() is available. # #CXXFLAGS+= -DOPENR_DEBUG .PHONY: all install clean 25 Lights 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 all: blinkingLeds.bin %.o: %.cc $(CXX) $(CXXFLAGS) -o $@ -c $^ BlinkingLedsStub.cc: stub.cfg $(STUBGEN) stub.cfg blinkingLeds.bin: BlinkingLedsStub.o BlinkingLeds.o blinkingLeds.ocf $(MKBIN) $(MKBINFLAGS) -o $@ $^ $(LIBS) $(STRIP) $@ install: blinkingLeds.bin gzip -c blinkingLeds.bin > $(INSTALLDIR)/OPEN-R/MW/ OBJS/BLINK.BIN clean: rm -f *.o *.bin *.elf *.snap.cc rm -f BlinkingLedsStub.h BlinkingLedsStub.cc def.h entry.h rm -f $(INSTALLDIR)/OPEN-R/MW/OBJS/BLINK.BIN To modify this file for your own projects, replace all instances of the string BlinkingLeds (note the upper-case B) with <YourObject> (note the upper-case Y), and all instances of blinkingLeds with <yourObject> (again noting the lower-case first letters). Then replace the two BLINK.BIN with the desired name for your object. Note that this makefile also assumes the existence of a directory ../MS/OPEN-R/MW/OBJS relative to the current directory. The compiled and compressed binary file will be placed here when you run make install. 2.3.4. OBJECT.CFG The OBJECT.CFG file specifies which of the objects currently on the AIBO memory stick should be started on boot. A typical file looks like: 1 2 /MS/OPEN-R/MW/OBJS/BLINK.BIN /MS/OPEN-R/MW/OBJS/POWERMON.BIN /MS is the root of the memory stick, so the highest-level directory visible on a Windows machine would be OPEN-R. The OBJECT.CFG file itself should be placed in the /MS/OPEN-R/MW/CONF folder. Each line specifies a compiled object that will be started as a process on the AIBO. The order of objects in this file should not matter. 2.3.5. CONNECT.CFG The CONNECT.CFG file specifies how the named connection points specified in each objects stub.cfg file should be interconnected once the AIBO has started up. A typical CONNECT.CFG looks like this: 1 2 3 # BlinkingLEDs --> OVirtualRobotComm BlinkingLeds.Blink.OCommandVectorData.S OVirtualRobotComm.Effector.OCommandVectorData.O 26 Lights Each line beginning with a # is a comment and will be ignored. The other non-empty lines should specify two named connection points separated by a space, a subject followed by an observer. In the above example we see that the BlinkingLeds module send commands to OVirtualRobotComm which represents the virtual object that handles communication between objects and the AIBO hardware. In later tutorials we will encounter more complicated interconnections. This file should also be placed in /MS/OPEN-R/MW/CONF. The AIBO will use this file to wire up the objects at run-time. 2.3.6. Directory Structure An AIBO memory stick must be configured correctly to run. Starting with a programmable memory stick, delete everything in the root of the memory stick. Then copy the OPEN-R directory from / usr/local/OPEN_R_SDK/OPEN_R/MS_ERS7/BASIC/memprot to the root of the memory stick (hereafter referred to as /MS). Go to /MS/OPEN-R/MW/CONF. In this directory you should place your CONNECT.CFG and OBJECT.CFG files. Go to /MS/OPEN-R/MW/OBJS. In this directory you should place your compiled objects. Note that the final file is a gzip compressed version of your compiled and linked .bin file. 27 Chapter 3. Movement In this tutorial you will learn how to move joints in the AIBO's head and legs and then write a group of classes that will cause both to move simultaneously. 3.1. Movement of the joints and joint addressing Each joint in the AIBO has a specific address which can be used to send movement data directly to that joint. There are 3 joints in the AIBO's head, one for each degree of freedom. The primitive addresses for each joint we will be using in the next few examples are all provided in the supplied code. During initialization we ask the AIBO system for a memory address of each of the joints we will be using by calling OPENR::OpenPrimitive(), which takes a string address of the joint and a local address to store it. Henceforth, we can refer to the joint by using the address held in local memory. 3.2. Moving the AIBO's head The following is the header file for a program to move the AIBO’s head from side to side. It works by creating an object MovingHead, which sends messages to the joints in the AIBO’s head and neck telling them in which direction to move and by how much: 1 2 3 4 5 6 7 #ifndef MovingHead_h_DEFINED #define MovingHead_h_DEFINED #include #include #include #include <OPENR/OObject.h> <OPENR/OSubject.h> <OPENR/OObserver.h> "def.h" This section of code describes the possible states the object will be in. 10 11 12 13 14 15 16 17 18 19 20 21 enum MovingHeadState { MHS_IDLE, MHS_START, MHS_ADJUSTING_DIFF_JOINT_VALUE, MHS_MOVING_TO_ZERO_POS, MHS_SWING_HEAD }; enum MovingResult { MOVING_CONT, MOVING_FINISH }; These are the addresses of the joints we will be using for moving the AIBO’s head. 1 2 3 4 5 static const char* const JOINT_LOCATOR[] = { "PRM:/r1/c1-Joint2:11", // TILT1 "PRM:/r1/c1/c2-Joint2:12", // PAN "PRM:/r1/c1/c2/c3-Joint2:13" // TILT2 }; We need a method, Ready(), to be called when subjects are ready to receive data. 1 2 class MovingHead : public OObject { public: 28 Movement 3 4 5 6 7 8 9 10 11 12 13 14 MovingHead(); virtual ~MovingHead() {} OSubject* subject[numOfSubject]; OObserver* observer[numOfObserver]; virtual virtual virtual virtual OStatus OStatus OStatus OStatus DoInit (const DoStart (const DoStop (const DoDestroy(const OSystemEvent& OSystemEvent& OSystemEvent& OSystemEvent& event); event); event); event); void Ready(const OReadyEvent& event); This function is used to set the gains and shifts of the PIDs that control the joints in the AIBO. 1 2 3 4 private: void OpenPrimitives(); void NewCommandVectorData(); void SetJointGain(); This method is needed to calibrate the AIBO's joints. 1 MovingResult AdjustDiffJointValue(); This function is used to move between the current position and the 'ZERO' position (0,0,0) in ZERO_POS_MAX_COUNTER * ocommandMAX_FRAMES frames. 1 MovingResult MoveToZeroPos(); SwingHead() swings the AIBO's head from side to side. 1 MovingResult SwingHead(); This function creates ocommandMAX_FRAMES of motion data between the specified start and end vals. 1 2 RCRegion* FindFreeRegion(); void SetJointValue(RCRegion* rgn, int idx, double start, double end) This function gives the AIBO ocommandMAX_FRAMES of data to move a joint. 1 void SetJointValue(RCRegion* rgn, int idx, int phase); The following variables are useful for referring to constants that are used throughout the program. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 static const size_t NUM_COMMAND_VECTOR = 2; static const size_t NUM_JOINTS = 3; static const int TILT1_INDEX = 0; static const int PAN_INDEX = 1; static const int TILT2_INDEX = 2; static static static static const const const const double double double double TILT1_ZERO_POS PAN_ZERO_POS TILT2_ZERO_POS SWING_AMPLITUDE = = = = static const word static const word static const word TILT1_PGAIN TILT1_IGAIN TILT1_DGAIN = 0x000a; = 0x0004; = 0x0002; static const word static const word PAN_PGAIN PAN_IGAIN = 0x0008; = 0x0002; 29 0.0; 0.0; 0.0; 80.0; Movement 18 19 20 21 22 23 24 25 26 27 28 29 30 31 static const word PAN_DGAIN = 0x0004; static const word static const word static const word TILT2_PGAIN TILT2_IGAIN TILT2_DGAIN = 0x0008; = 0x0004; = 0x0002; static const word static const word static const word PSHIFT ISHIFT DSHIFT = 0x000e; = 0x0002; = 0x000f; // 128ms * 16 = 2048ms static const int ZERO_POS_MAX_COUNTER // phase [0:127] static const int MAX_PHASE = 16; = 128; These are private variables which hold references to the joints, memory regions, and the state of the AIBO’s head. MovingHeadState movingHeadState; OPrimitiveID jointID[NUM_JOINTS]; RCRegion* region[NUM_COMMAND_VECTOR]; 1 2 3 4 }; 1 #endif // MovingHead_h_DEFINED This header file is then implemented in a C++ file, MovingHead.cc: In addition to the normal includes, we also need to include the math.h library. 1 2 3 4 5 6 #include #include #include #include #include #include <math.h> <OPENR/OPENRAPI.h> <OPENR/OUnits.h> <OPENR/OSyslog.h> <OPENR/core_macro.h> "MovingHead.h" We need to initialise the object state variables to null or equivalent, noting that we can't talk to the AIBO at this point. 1 2 3 4 5 6 7 MovingHead::MovingHead() : movingHeadState(MHS_IDLE) { for (int i = 0; i < NUM_JOINTS; i++) jointID[i] = oprimitiveID_UNDEF; for (int i = 0; i < NUM_COMMAND_VECTOR; i++) region[i] = 0; } The DoInit() method is the same as in the Blinking LEDs example, but we need to add the above code to the DoStart() function, to ensure that our Ready() method is called. If subject[sbjMove] is ready before we call ENABLE_ALL_SUBJECT, Ready() won't get called so we start the ball rolling here. 1 2 3 4 5 6 if (subject[sbjMove]->IsReady() == true) { AdjustDiffJointValue(); movingHeadState = MHS_ADJUSTING_DIFF_JOINT_VALUE; } else { movingHeadState = MHS_START; } The only addition to DoStop() is setting the state of the head to idle, to ensure that if a call to Ready() is made the AIBO’s head will remain still. 30 Movement movingHeadState = MHS_IDLE; 1 OpenPrimitives() and NewCommandVectorData() are the same as the Blinking LEDs example. However, in OpenPrimitives() remember that you need to use the array of head joint addresses instead of the LED addresses. SetJointGain() sets the gain and shifts in each of the AIBO’s head and neck joints. The above code shows the standard calls to methods from the OPENR object to do this. This must be done for each of the joints we will be using ie. TILT1, PAN and TILT2. OPENR::EnableJointGain(jointID[TILT1_INDEX]); OPENR::SetJointGain(jointID[TILT1_INDEX], TILT1_PGAIN, TILT1_IGAIN, TILT1_DGAIN, PSHIFT, ISHIFT, DSHIFT); 1 2 3 4 5 6 We need to write two versions of SetJointValue() for the two different cases of movement, they are provided above. One version of this method creates a movement between a specific start and end value, and the other moves between the phase given and phase+1. Phase is an integer between 0 and 127 and is equivalent to 1/128th of 2 pi. Hence if you linearly increase phase from 0 to 127, the AIBO joint will swing between +-SWING_AMPLITUDE. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 void MovingHead::SetJointValue(RCRegion* rgn, int idx, double start, double end) { OCommandVectorData* cmdVecData = (OCommandVectorData*)rgn->Base(); OCommandInfo* info = cmdVecData->GetInfo(idx); info->Set(odataJOINT_COMMAND2, jointID[idx], ocommandMAX_FRAMES); OCommandData* data = cmdVecData->GetData(idx); OJointCommandValue2* jval = (OJointCommandValue2*)data->value; double delta = end - start; for (int i = 0; i < ocommandMAX_FRAMES; i++) { double dval = start + (delta * i) / (double)ocommandMAX_FRAMES; jval[i].value = oradians(dval); } } void MovingHead::SetJointValue(RCRegion* rgn, int idx, int phase) { OCommandVectorData* cmdVecData = (OCommandVectorData*)rgn->Base(); OCommandInfo* info = cmdVecData->GetInfo(idx); info->Set(odataJOINT_COMMAND2, jointID[idx], ocommandMAX_FRAMES); OCommandData* data = cmdVecData->GetData(idx); OJointCommandValue2* jval = (OJointCommandValue2*)data->value; double d1 = 2.0 * M_PI / (double)MAX_PHASE; double d2 = d1 / (double)ocommandMAX_FRAMES; for (int i = 0; i < ocommandMAX_FRAMES; i++) { double dval = SWING_AMPLITUDE * sin(d1 * phase + d2 * i); 31 Movement 45 46 47 jval[i].value = oradians(dval); } } This function calibrates the AIBO by requesting the joint value and then setting the joint value to the value obtained. Note that values obtained from GetJointValue() are in micro-radians and are converted to degrees when passed to SetJointValue(). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 MovingResult MovingHead::AdjustDiffJointValue() { OJointValue current[NUM_JOINTS]; for (int i = 0; i < NUM_JOINTS; i++) { OPENR::GetJointValue(jointID[i], ¤t[i]); SetJointValue(region[0], i, degrees(current[i].value/1000000.0), degrees(current[i].value/1000000.0)); } subject[sbjMove]->SetData(region[0]); subject[sbjMove]->NotifyObservers(); return MOVING_FINISH; } MoveToZeroPos() detects the head’s current position in all three joints and moves them all to their base positions. It does so by calling this method many times from Ready(), each time moving closer to the Zero position until it is reached by all joints. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 MovingResult MovingHead::MoveToZeroPos() { //This function moves between the current position //and the 'ZERO' position (0,0,0) in //ZERO_POS_MAX_COUNTER * ocommandMAX_FRAMES frames static int counter = -1; static double s_tilt1, d_tilt1; static double s_pan, d_pan; static double s_tilt2, d_tilt2; if (counter == -1) { OJointValue current; OPENR::GetJointValue(jointID[TILT1_INDEX], ¤t); s_tilt1 = degrees(current.value/1000000.0); d_tilt1 = (TILT1_ZERO_POS - s_tilt1) / (double)ZERO_POS_MAX_COUNTER; OPENR::GetJointValue(jointID[PAN_INDEX], ¤t); s_pan = degrees(current.value/1000000.0); d_pan = (PAN_ZERO_POS - s_pan) / (double)ZERO_POS_MAX_COUNTE OPENR::GetJointValue(jointID[TILT2_INDEX], ¤t); s_tilt2 = degrees(current.value/1000000.0); d_tilt2 = (TILT2_ZERO_POS - s_tilt2) / (double)ZERO_POS_MAX_ counter = 0; } RCRegion* rgn = FindFreeRegion(); SetJointValue(rgn, TILT1_INDEX, s_tilt1, s_tilt1 + d_tilt1); SetJointValue(rgn, PAN_INDEX, s_pan, 32 Movement 38 39 40 41 42 43 44 45 46 47 48 49 50 51 s_pan + d_pan); SetJointValue(rgn, TILT2_INDEX, s_tilt2, s_tilt2 + d_tilt2); subject[sbjMove]->SetData(rgn); subject[sbjMove]->NotifyObservers(); s_tilt1 += d_tilt1; s_pan += d_pan; s_tilt2 += d_tilt2; counter++; return (counter == ZERO_POS_MAX_COUNTER) ? MOVING_FINISH : MOVING_CONT; } This function just swings the head, incrementing phase every time it is called and wrapping around when it reaches 128. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 MovingResult MovingHead::SwingHead() { static int phase = 0; RCRegion* rgn = FindFreeRegion(); if (rgn == 0) { return MOVING_FINISH; } SetJointValue(rgn, TILT1_INDEX, 0.0, 0.0); SetJointValue(rgn, PAN_INDEX, phase); SetJointValue(rgn, TILT2_INDEX, 0.0, 0.0); subject[sbjMove]->SetData(rgn); subject[sbjMove]->NotifyObservers(); phase = (phase + 1) % MAX_PHASE; return MOVING_CONT; } Ready() method is called when the joints are ready for more data. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void MovingHead::Ready(const OReadyEvent& event) { if (movingHeadState == MHS_IDLE) { } else if (movingHeadState == MHS_START) { AdjustDiffJointValue(); movingHeadState = MHS_ADJUSTING_DIFF_JOINT_VALUE; } else if (movingHeadState == MHS_ADJUSTING_DIFF_JOINT_VALUE) { SetJointGain(); MovingResult r = MoveToZeroPos(); movingHeadState = MHS_MOVING_TO_ZERO_POS; } else if (movingHeadState == MHS_MOVING_TO_ZERO_POS) { MovingResult r = MoveToZeroPos(); if (r == MOVING_FINISH) { movingHeadState = MHS_SWING_HEAD; } } else { MovingResult r = SwingHead(); 33 Movement 29 30 31 32 33 if (r == MOVING_FINISH) { movingHeadState = MHS_IDLE; } } } The complete code and additional files for the MovingHead object can be downloaded here1. 3.3. Moving the AIBO's legs The following is the header file required for the moving legs example. It is similar to the moving head example in many respects, therefore we leave most of the implementation up to you: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 1 #ifndef MovingLegs_h_DEFINED #define MovingLegs_h_DEFINED #include #include #include #include <OPENR/OObject.h> <OPENR/OSubject.h> <OPENR/OObserver.h> "def.h" enum MovingLegsState { MLS_IDLE, MLS_START, MLS_ADJUSTING_DIFF_JOINT_VALUE, MLS_MOVING_TO_BROADBASE, MLS_MOVING_TO_SLEEPING }; enum MovingResult { MOVING_CONT, MOVING_FINISH }; static const char* const JOINT_LOCATOR[] = //(Right Front Leg) "PRM:/r4/c1-Joint2:41", // RFLEG "PRM:/r4/c1/c2-Joint2:42", // RFLEG "PRM:/r4/c1/c2/c3-Joint2:43", // RFLEG //(Left Front Leg) "PRM:/r2/c1-Joint2:21", // LFLEG "PRM:/r2/c1/c2-Joint2:22", // LFLEG "PRM:/r2/c1/c2/c3-Joint2:23", // LFLEG //(Right Rear Leg) "PRM:/r5/c1-Joint2:51", // RRLEG "PRM:/r5/c1/c2-Joint2:52", // RRLEG "PRM:/r5/c1/c2/c3-Joint2:53", // RRLEG //(Left Rear Leg) "PRM:/r3/c1-Joint2:31", // LRLEG "PRM:/r3/c1/c2-Joint2:32", // LRLEG "PRM:/r3/c1/c2/c3-Joint2:33" // LRLEG }; const double 120, // 90, // 30, // 120, 90, 30, BROADBASE_ANGLE[] = { RFLEG J1 RFLEG J2 RFLEG J3 // LFLEG J1 // LFLEG J2 // LFLEG J3 http://pc307a.csse.uwa.edu.au/tutorials/files/tute3/MovingHead.zip 34 { J1 J2 J3 J1 J2 J3 J1 J2 J3 J1 J2 J3 Movement 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 -120, // RRLEG J1 70, // RRLEG J2 30, // RRLEG J3 -120, // LRLEG J1 70, // LRLEG J2 30 // LRLEG J3 }; const double 59, // 0, // 30, // 59, 0, 30, SLEEPING_ANGLE[] = { RFLEG J1 RFLEG J2 RFLEG J3 // LFLEG J1 // LFLEG J2 // LFLEG J3 -119, // RRLEG J1 4, // RRLEG J2 122, // RRLEG J3 -119, // LRLEG J1 4, // LRLEG J2 122 // LRLEG J3 }; class MovingLegs : public OObject { public: MovingLegs(); virtual ~MovingLegs() {} OSubject* subject[numOfSubject]; OObserver* observer[numOfObserver]; virtual virtual virtual virtual OStatus OStatus OStatus OStatus DoInit (const DoStart (const DoStop (const DoDestroy(const OSystemEvent& OSystemEvent& OSystemEvent& OSystemEvent& event); event); event); event); void Ready(const OReadyEvent& event); private: void void void MovingResult MovingResult MovingResult OpenPrimitives(); NewCommandVectorData(); SetJointGain(); AdjustDiffJointValue(); MoveToBroadBase(); MoveToSleeping(); RCRegion* FindFreeRegion(); void SetJointValue(RCRegion* rgn, int idx, double start, double end); static const size_t NUM_JOINTS = 12; static const size_t NUM_COMMAND_VECTOR = 2; static const word static const word static const word J1_PGAIN = 0x0010; J1_IGAIN = 0x0004; J1_DGAIN = 0x0001; static const word static const word static const word J2_PGAIN = 0x000a; J2_IGAIN = 0x0004; J2_DGAIN = 0x0001; static const word static const word static const word J3_PGAIN = 0x0010; J3_IGAIN = 0x0004; J3_DGAIN = 0x0001; static const word PSHIFT 35 = 0x000e; Movement 120 121 122 123 124 125 126 127 128 129 130 131 static const word static const word ISHIFT DSHIFT = 0x0002; = 0x000f; static const int BROADBASE_MAX_COUNTER = 24; static const int SLEEPING_MAX_COUNTER = 24; MovingLegsState OPrimitiveID RCRegion* movingLegsState; jointID[NUM_JOINTS]; region[NUM_COMMAND_VECTOR]; }; #endif MoveToBroadBase() and MoveToSleeping() are the only functions not defined in the previous example, however they are very similar to MoveToZeroPos(). The major difference between them is that this time we are sending data to 12 joints instead of 3 so an array of start and delta values would be more appropriate. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 MovingResult MovingLegs::MoveToBroadBase() { static int counter = -1; static double start[NUM_JOINTS]; static double delta[NUM_JOINTS]; double ndiv = (double)BROADBASE_MAX_COUNTER; if (counter == -1) { for (int i = 0; i < NUM_JOINTS; i++) { OJointValue current; OPENR::GetJointValue(jointID[i], ¤t); start[i] = degrees(current.value/1000000.0); delta[i] = (BROADBASE_ANGLE[i] - start[i]) / ndiv; } counter = 0; RCRegion* rgn = FindFreeRegion(); for (int i = 0; i < NUM_JOINTS; i++) { SetJointValue(rgn, i, start[i], start[i] + delta[i]); start[i] += delta[i]; } subject[sbjMove]->SetData(rgn); counter ++; } RCRegion* rgn = FindFreeRegion(); for (int i = 0; i < NUM_JOINTS; i++) { SetJointValue(rgn, i, start[i], start[i] + delta[i]); start[i] += delta[i]; } subject[sbjMove]->SetData(rgn); subject[sbjMove]->NotifyObservers(); counter++; return (counter == BROADBASE_MAX_COUNTER) ? MOVING_FINISH : MOVING_CONT; } MovingResult MovingLegs::MoveToSleeping() { static int counter = -1; static double start[NUM_JOINTS]; static double delta[NUM_JOINTS]; double ndiv = (double)SLEEPING_MAX_COUNTER; if (counter == -1) { 36 Movement 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 for (int i = 0; i < NUM_JOINTS; i++) { start[i] = BROADBASE_ANGLE[i]; delta[i] = (SLEEPING_ANGLE[i] - start[i]) / ndiv; } counter = 0; } RCRegion* rgn = FindFreeRegion(); for (int i = 0; i < NUM_JOINTS; i++) { SetJointValue(rgn, i, start[i], start[i] + delta[i]); start[i] += delta[i]; } subject[sbjMove]->SetData(rgn); subject[sbjMove]->NotifyObservers(); counter++; return (counter == SLEEPING_MAX_COUNTER) ? MOVING_FINISH : MOVING_CONT; } The complete code and additional files for the MovingLegs object can be downloaded here2. 3.4. Moving the AIBO's head and legs together Ensure that both examples will compile and run on the AIBO separately and then see if you can place both on at the same time. Multiple objects on the AIBO will have no effect on each other. If they do not communicate between one another, and do not attempt to send conflicting commands to parts of the AIBO, then they will both run correctly. However, all of the objects share a common CONNECT.CFG and OBJECT.CFG. So the contents of these files must be merged to run more than one example at a time. Note that this is as simple as using >cat dir1/OBJECT.CFG dir2/OBJECT.CFG > OBJECT.CFG You can download the two samples with their shared configuration files here3. If the sample works correctly, the AIBO's head and legs should both move. 2 3 http://pc307a.csse.uwa.edu.au/tutorials/files/tute3/MovingLegs.zip http://pc307a.csse.uwa.edu.au/tutorials/files/tute3/Moving.zip 37 Chapter 4. Vision and Lights In this tutorial we will be extending the ideas developed in tutorial two. This will include the use of the camera object and MoNet. The final goal of the tutorial is to make the AIBO stand using MoNet and then use the digital camera to detect the AIBO’s ball and respond with the LED face lights. We start with the same code as the BlinkingLeds tutorial, with our object renamed to VisionAndLights. This code will set the face LED intensity to a constant glow at medium intensity. Otherwise the AIBO will not do anything. The code we will be using as a starting point is supplied below. • VisionAndLights_1.h1 • VisionAndLights_1.cc2 • stub_1.cfg3 • CONNECT_1.CFG4 4.1. Adding vision to the code We will now update the code to respond to the camera image. To process camera data we must do the following: 1. Connect to the camera primitive 2. Receive image data 3. Analyze image data for the AIBO's ball 4. Alter the face LED intensity The AIBO supplies a fast colour detection algorithm to aid image processing. A programmer can initialize a colour channel to a specified colour range, and then simply ask the AIBO for the amount of the specified colour in the camera image. Hence we will be setting up a colour channel with a colour range for the AIBO's pink ball in SetCdtVectorData(). We must update stub.cfg with a new connection point for the AIBO camera, called NotifyImage(). ObjectName : VisionAndLights NumOfOSubject : 1 NumOfOObserver : 1 Service : "VisionAndLights.Blink.OCommandVectorData.S", null, Ready() Service : "VisionAndLights.Image.OFbkImageVectorData.O", null, NotifyImage() In VisionAndLights.h, we must include the header file for the FbkImage class, which enables us to process AIBO camera images. 1 2 3 4 http://pc307a.csse.uwa.edu.au/tutorials/files/tute4/VisionAndLights_1.h http://pc307a.csse.uwa.edu.au/tutorials/files/tute4/VisionAndLights_1.cc http://pc307a.csse.uwa.edu.au/tutorials/files/tute4/stub_1.cfg http://pc307a.csse.uwa.edu.au/tutorials/files/tute4/CONNECT_1.CFG 38 Vision and Lights 1 2 3 4 5 6 7 8 9 10 #ifndef VisionAndLights_h_DEFINED #define VisionAndLights_h_DEFINED #include #include #include #include #include #include ... <OPENR/OObject.h> <OPENR/OSubject.h> <OPENR/OObserver.h> <OPENR/ODataFormats.h> <OPENR/OFbkImage.h> "def.h" We add the address of the camera, so that we can connect to it. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ... enum LEDState { LEDState_IDLE, LEDState_RUN, LEDState_STOP, }; // The address of the camera static const char* const FBK_LOCATOR = "PRM:/r1/c1/c2/c3/i1-FbkImageSensor:F1"; // The addresses of the fourteen face leds static const char* const LED3_LOCATOR[] = { "PRM:/r1/c1/c2/c3/la-LED3:la", // Face light1 ... We define a method NotifyImage() that will be called whenever the camera has data ready for us to process. 1 2 3 4 5 6 7 ... OSubject* subject[numOfSubject]; OObserver* observer[numOfObserver]; void NotifyImage(const ONotifyEvent& event); void Ready(const OReadyEvent& event); ... We define a method SetCdtVectorData() that initializes a colour channel with the colour range for the AIBO's pink ball. 1 2 3 4 5 6 7 8 ... private: void OpenPrimitives(); void NewCommandVectorData(); void SetCdtVectorData(); void SetIntensity(); ... We define a compile time variable for which colour channel we shall use - in this case channel 0. And we define a member variable to store the ID of the camera primitive. 1 2 3 ... static const size_t NUM_LEDS = 14; static const size_t NUM_COMMAND_VECTOR = 4; 39 Vision and Lights 4 5 6 7 8 9 10 11 12 13 static const OCdtChannel CDT_CHAN LEDState sword OPrimitiveID OPrimitiveID RCRegion* = ocdtCHANNEL0; ledState; ledIntensity; fbkID; ledID[NUM_LEDS]; region[NUM_COMMAND_VECTOR]; }; #endif // VisionAndLights_h_DEFINED In VisionAndLights.cc, we add an initializer to our constructor for our new member variable. 1 2 3 4 5 6 7 8 9 10 11 #include #include #include #include <OPENR/OPENRAPI.h> <OPENR/OSyslog.h> <OPENR/core_macro.h> "VisionAndLights.h" VisionAndLights::VisionAndLights() : ledIntensity(10), fbkID(oprimitiveID_UNDEF), ledState(LEDState_IDLE) { ... During DoInit() we initialize our colour channel by calling SetCdtVectorData(). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ... OStatus VisionAndLights::DoInit(const OSystemEvent& event) { ... OpenPrimitives(); NewCommandVectorData(); SetCdtVectorData(); OStatus st = OPENR::SetMotorPower(opowerON); return oSUCCESS; } ... The definition of NotifyImage() must be added. If our object is in the RUN state, we obtain the image data from the event object and construct an OFbkImage object with the data. We then request the colour frequency of the colour defined in our colour channel in SetCdtVectorData(). We then set our member variable ledIntensity to this value, with some bounds checking. The next time the LED's request more data, they will be supplied with this new intensity value. Finally, regardless of whether we are in the RUN state or not, we assert to the camera that we are ready for more data. If we fail to do this, NotifyImage() will not be called again. 1 2 3 4 5 6 7 8 9 10 11 12 13 void VisionAndLights::NotifyImage(const ONotifyEvent& event) { if(ledState == LEDState_RUN) { OFbkImageVectorData* imageVec = (OFbkImageVectorData*)event.Data(0); OFbkImageInfo* info = imageVec->GetInfo(ofbkimageLAYER_C); byte* data = imageVec->GetData(ofbkimageLAYER_C); OFbkImage cdtImage(info, data, ofbkimageBAND_CDT); 40 Vision and Lights 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 //get a measure of colour frequency for our //specified colour range byte freq = cdtImage.ColorFrequency(CDT_CHAN); ledIntensity = freq; if(ledIntensity < 0) ledIntensity = 0; if(ledIntensity > 15) ledIntensity = 15; } //ready for more camera data //note that this must be called even //if we don't want camera data right now, //else we'll never get data again. observer[event.ObsIndex()]->AssertReady(); } We alter the OpenPrimitives() method to also open the camera primitive, storing the primitive ID in our new member variable fbkID. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ... void VisionAndLights::OpenPrimitives() { OStatus result; for (int i = 0; i < NUM_LEDS; i++) { result = OPENR::OpenPrimitive(LED3_LOCATOR[i], &ledID[i]); } result = OPENR::OpenPrimitive(FBK_LOCATOR, &fbkID); } ... The SetCdtVectorData() method must be defined. We start by creating a colour detection vector data (OCdtVectorData) which we initialize with one channel of colour data. Into this channel (OCdtInfo) we define 32 rectangles in the YCrCb colour-space. The union of these rectangles defines a 3D object in the colour-space - in this case the object is two cubes. We can then test camera image data for colour lying inside this object. After the vector data is complete, we pass it to the OPEN-R system by calling OPENR::SetCdtVectorData(). Then the object can be deleted. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void VisionAndLights::SetCdtVectorData() { OStatus result; MemoryRegionID cdtVecID; OCdtVectorData* cdtVec; OCdtInfo* cdt; result = OPENR::NewCdtVectorData(&cdtVecID, &cdtVec); if (result != oSUCCESS) { return; } cdtVec->SetNumData(1); cdt = cdtVec->GetInfo(0); cdt->Init(fbkID, CDT_CHAN); // // // // // This sets up a colour range to test for each pixel against. colour are defined in the YCrCb format. A region in this space is specified by defining a number of rectangles in the Cr-Cb plane for a given Y value. The union 41 Vision and Lights 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 // of these rectangles defines one or more 3D shapes // in the colourspace. // // cdt->Set(Y_segment, // cdt->Set( 0, 230, 150, cdt->Set( 1, 230, 150, cdt->Set( 2, 230, 150, cdt->Set( 3, 230, 150, cdt->Set( 4, 230, 150, cdt->Set( 5, 230, 150, cdt->Set( 6, 230, 150, cdt->Set( 7, 230, 150, cdt->Set( 8, 230, 150, cdt->Set( 9, 230, 150, cdt->Set(10, 230, 150, cdt->Set(11, 230, 150, cdt->Set(12, 230, 150, cdt->Set(13, 230, 150, cdt->Set(14, 230, 150, cdt->Set(15, 230, 150, cdt->Set(16, 230, 150, cdt->Set(17, 230, 150, cdt->Set(18, 230, 150, cdt->Set(19, 230, 150, cdt->Set(20, 230, 160, cdt->Set(21, 230, 160, cdt->Set(22, 230, 160, cdt->Set(23, 230, 160, cdt->Set(24, 230, 160, cdt->Set(25, 230, 160, cdt->Set(26, 230, 160, cdt->Set(27, 230, 160, cdt->Set(28, 230, 160, cdt->Set(29, 230, 160, cdt->Set(30, 230, 160, cdt->Set(31, 230, 160, Cr_max, Cr_min, Cb_max, Cb_min) 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); 120); result = OPENR::SetCdtVectorData(cdtVecID); result = OPENR::DeleteCdtVectorData(cdtVecID); } We must also modify CONNECT.CFG to connect the new connection point to the camera. 1 2 3 4 5 # VisionAndLights <--> OVirtualRobotComm VisionAndLights.Blink.OCommandVectorData.S OVirtualRobotComm.Effector.OCommandVectorData.O OVirtualRobotComm.FbkImageSensor.OFbkImageVectorData.S VisionAndLights.Image.OFbkImageVectorData.O The modified files are available here: 5 6 7 8 • VisionAndLights_2.h5 • VisionAndLights_2.cc6 • stub_2.cfg7 • CONNECT_2.CFG8 http://pc307a.csse.uwa.edu.au/tutorials/files/tute4/VisionAndLights_2.h http://pc307a.csse.uwa.edu.au/tutorials/files/tute4/VisionAndLights_2.cc http://pc307a.csse.uwa.edu.au/tutorials/files/tute4/stub_2.cfg http://pc307a.csse.uwa.edu.au/tutorials/files/tute4/CONNECT_2.CFG 42 Vision and Lights 4.2. An Introduction to MoNet In the next extension, we will use the MoNet sample to stand the AIBO up before we start processing image data. MoNet is a helper object that makes performing complex movements and playing sounds on the AIBO much simpler. We need two connections to MoNet - one to send commands and one to receive feedback on the commands we sent. Remember that communication in the AIBO is asynchronous. We do not define a subject method for sending commands to MoNet, as we do not need to do this continuously. The stub.cfg file becomes: ObjectName : VisionAndLights NumOfOSubject : 2 NumOfOObserver : 2 Service : "VisionAndLights.Command.MoNetCommand.S", null, null Service : "VisionAndLights.Result.MoNetResult.O", null, NotifyResult() Service : "VisionAndLights.Blink.OCommandVectorData.S", null, Ready() Service : "VisionAndLights.Image.OFbkImageVectorData.O", null, NotifyImage() In VisionAndLights.h, we start by including the MoNet header file in our header file. 1 2 3 4 5 6 7 8 9 10 11 #ifndef VisionAndLights_h_DEFINED #define VisionAndLights_h_DEFINED #include #include #include #include #include #include #include ... <OPENR/OObject.h> <OPENR/OSubject.h> <OPENR/OObserver.h> <OPENR/ODataFormats.h> <OPENR/OFbkImage.h> <MoNetData.h> "def.h" Since our object will not be processing data while it is in the process of standing up, we need a new state to keep track of what we are doing. We also define a constant MoNetCommandID which MoNet knows to interpret as a command to stand up. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ... enum LEDState { LEDState_IDLE, LEDState_STANDING, LEDState_RUN, LEDState_STOP, }; const MoNetCommandID STAND2STAND_NULL = // see MONETCMD.CFG 2; //the address of the camera static const char* const FBK_LOCATOR = "PRM:/r1/c1/c2/c3/i1-FbkImageSensor:F1"; ... Once MoNet has finished performing a command it will send a message back to us. Hence we define a NotifyResult() method to receive this message. 43 Vision and Lights 1 2 3 4 5 6 7 8 ... OSubject* subject[numOfSubject]; OObserver* observer[numOfObserver]; void NotifyImage(const ONotifyEvent& event); void Ready(const OReadyEvent& event); void NotifyResult(const ONotifyEvent& event); ... We also define a helper method to execute a MoNet command. 1 2 3 4 5 6 7 8 9 ... RCRegion* FindFreeRegion(); void Execute(MoNetCommandID cmdID); static const size_t NUM_LEDS = 14; static const size_t NUM_COMMAND_VECTOR = 4; static const OCdtChannel CDT_CHAN = ocdtCHANNEL0; ... In DoStart(), we ask MoNet to begin standing AIBO up for us. We also set our state to STANDING rather than RUN. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ... OStatus VisionAndLights::DoStart(const OSystemEvent& event) { //start standing up Execute(STAND2STAND_NULL); //set an initial intensity, so we will //get asked for more data next frame. if (subject[sbjBlink]->IsReady() == true) { SetIntensity(); } ledState = LEDState_STANDING; ENABLE_ALL_SUBJECT; ASSERT_READY_TO_ALL_OBSERVER; return oSUCCESS; } ... We define our NotifyResult() method to receive messages from MoNet. If we are in the STANDING state and MoNet has finished executing a command, we change to the RUN state and we send the LED's an intensity. We must do this so that the LED's will ask for more data and our Ready() method will be called again. 1 2 3 4 5 6 7 8 9 10 void VisionAndLights::NotifyResult(const ONotifyEvent& event) { MoNetResult* result = (MoNetResult*)event.Data(0); if (result->status == monetCOMPLETION && ledState == LEDState_STANDING) { ledState = LEDState_RUN; SetIntensity(); } 44 Vision and Lights 11 12 13 observer[event.ObsIndex()]->AssertReady(); } We define the Execute() method as a simple helper for executing MoNet commands. 1 2 3 4 5 6 7 void VisionAndLights::Execute(MoNetCommandID cmdID) { MoNetCommand cmd(cmdID); subject[sbjCommand]->SetData(&cmd, sizeof(cmd)); subject[sbjCommand]->NotifyObservers(); } We must also update CONNECT.CFG with our connections to MoNet. MoNet is actually several objects, hence there are connections between VisionAndLights and MoNet, between MoNet and its agents, between the Agents and OVirtualRobotComm, and of course we still have our own connections to OVirtualRobotComm. # VisionAndLights <--> MoNet VisionAndLights.Command.MoNetCommand.S MoNet.ClientCommand.MoNetCommand.O MoNet.ClientResult.MoNetResult.S VisionAndLights.Result.MoNetResult.O # MoNet <--> MotionAgents MoNet.MotionAgentCommand.MoNetAgentCommand.S MotionAgents.Command.MoNetAgentCommand.O MotionAgents.Result.MoNetAgentResult.S MoNet.AgentResult.MoNetAgentResult.O # MoNet <--> SoundAgent MoNet.SoundAgentCommand.MoNetAgentCommand.S SoundAgent.Command.MoNetAgentCommand.O SoundAgent.Result.MoNetAgentResult.S MoNet.AgentResult.MoNetAgentResult.O # MotionAgents --> OVirtualRobotComm MotionAgents.Effector.OCommandVectorData.S OVirtualRobotComm.Effector.OCommandVectorData.O # SoundAgent --> OVirtualRobotAudioComm SoundAgent.Speaker.OSoundVectorData.S OVirtualRobotAudioComm.Speaker.OSoundVectorData.O # VisionAndLights <--> OVirtualRobotComm VisionAndLights.Blink.OCommandVectorData.S OVirtualRobotComm.Effector.OCommandVectorData.O OVirtualRobotComm.FbkImageSensor.OFbkImageVectorData.S VisionAndLights.Image.OFbkImageVectorData.O The complete sample, with code and compiled binaries, can be downloaded here: VisionAndLights_3.zip9 9 http://pc307a.csse.uwa.edu.au/tutorials/files/tute4/VisionAndLights_3.zip 45 Chapter 5. Troubleshooting 5.1. Cygwin Environment 5.1.1. Every time I start Cygwin my computer hangs. Make sure that you don't install Cygwin into your Linux home directory. This appears to cause instability issues (but the cause is not yet known). Instead install into your Windows home directory (H:\). 5.2. Compiling programs for the AIBO 5.2.1. I typed make, but the .BIN files are not generated. Verify that OPEN-R is installed correctly. 5.3. Running programs on the AIBO 5.3.1. When I turn on the AIBO, it plays the melody from the Phantom of the Opera then turns itself off. The batteries are low. Replace the battery with a fully charged battery. 5.3.2. When I turn on the AIBO, it plays a sequence of tones getting lower and lower in pitch then turns itself off. The Memory Stick does not have the correct structure. Verify your OBJECT.CFG file is in the correct place, and the .BIN files referenced all exist and are valid. 46 Glossary accelerometer A device used to ascertain orientation and movement. AIBO Artificial Intelligence roBOt, as developed and sold by Sony. AIBO Programmable Memory Stick A memory stick that is specifically designed to run custom programs for the AIBO. Normal memory sticks will not run AIBO programs. C++ An object oriented programming language. compilation environment The set of programs used to write and compile programs written to run on the AIBO. CPU Central Processing Unit. The brain of a computer. CSSE Computer Science and Software Engineering, a department of the Faculty of Engineering and Mathematical Sciences Faculty of at the The University of Western Australia. Cygwin A set of tools, libraries and source code designed to allow compilation and execution of UNIX programs in a Microsoft Windows environment. Linux A free, open-source UNIX-like operating system. memory stick A form of portable storage that can be inserted into Sony products such as the AIBO. memory stick reader A small device that plugs into the USB port of a computer on one end, and accepts a Sony memory stick in the other. This allows you to read and modify the files on a memory stick. MIPS MIPS is the armless yellow rabbit that appears in the basement of Peach's Castle in Super Mario 64. OPEN-R The framework of C++ classes and functions that allows a programmer to interact with the hardware components of the AIBO. programming interface An interface that allows the user to write code to control the AIBO. SDK Software Development Kit UNIX An open-standard computer operating system for servers and workstation applications. 47