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], &current[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], &current);
s_tilt1 = degrees(current.value/1000000.0);
d_tilt1 = (TILT1_ZERO_POS - s_tilt1) /
(double)ZERO_POS_MAX_COUNTER;
OPENR::GetJointValue(jointID[PAN_INDEX], &current);
s_pan = degrees(current.value/1000000.0);
d_pan = (PAN_ZERO_POS - s_pan) / (double)ZERO_POS_MAX_COUNTE
OPENR::GetJointValue(jointID[TILT2_INDEX], &current);
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], &current);
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