Skip to content

Talking to NAO (IPC)

Paul Heinen edited this page Jul 25, 2017 · 4 revisions

Overview

UML Diagram of communication process


Due to Aldabaran's API not being open source, we unfortunately cannot directly communicate with the NAO's hardware. Aldabaran's API includes everything you would need to technically make the NAO play soccer, with the caveat that we cannot modify that code and it isn't the most effecient system. To overcome these obstacles, we have employed a similar strategy as other teams such as BHuman and rUNSWift. The solution is to have a seperate custom module running on NAOqi which limits the use of Aldabaran's API to only making direct hardware change requests and memory access requests (official documentation). To be specific we create:

  • A DCM Proxy (AL::DCMProxy::...): Direct requests to actuators/motors a.k.a the writer.
  • A Memory Proxy (AL::ALMemoryProxy). Direct access to NAOqi's memory. Reads Sensor values and current hardware values.
  • Aliases (AL::ALValue): A multi-demensional jagged array which allows faster reads and writes to either one or many values in NAOqi.

Overview of NAOqi hardware communication (source): Overview of NAOqi hardware communication

Method of Interprocess Communication:

There are various ways of going about doing this. Linux/UNIX have native support for Shared Memory Objects (checkout /proc/ipc) using POSIX commands. Since we are already using boost, a design decision was made to use Boost.Interprocess, due to how easy and safe it makes doing anything with Shared Memory. There is quite a lot of flexibility as to how you use Boost.Interprocess, so I will outline how we decided to use it and give an example of how you may (but shouldn't) directly read from and write to our shared memory object.

Our Boost.Interprocess Implementation:

We create a boost managed memory object called "PineappleJuice". This can be thought of a block of contiguous memory of fixed size (~64KB but you can increase or decrease it as needed..). Next, we insert an object into this block of managed memory, which is simply a struct called hal_data.h into a chunk named "juicyData" (we strictly follow our pineapple analogies). To avoid race conditions between the two processes, we use a boost::interprocess::anonymous_semaphore object, which is also stored in interprocess memory.

Example of Directly Accessing Shared Memory:

Note: This shouldn't be done by any module in the NAO-Engine aside from NAOInterface. All modules should communicate NAOqi read/write requests with NAOInterface instead.

boost::interprocess::managed_shared_memory shared_mem {open_only, "PineappleJuice"};
std::pair<hal_data *, int val> find_res = shared_mem.find("juicyData");
hal_data *shared_data_accessor = find_res.first;
//Beware of nullptr undefined behavior...this is just a lazy safety check.
if (!shared_data_accessor.first) { std::cout << "Failed to find" << std::endl; return; } 
while(!(sem_trywait(semaphore)) LOG_DEBUG << "Still waiting...";
//...some code
sem_post(semaphore);

Final Note on Boost.Interprocess: Objects which are going to be stored in inteprocess memory require some caution on creation. Data types such as std::vector<> and std::string which require heap allocation will not work with boost.interprocess. Boost Interprocess has specialized data types for this purpose. Please refer to the Boost Documentation if you make any changes to this.


NAOInterface Module

The NAOInterface Module provides an easier and safer way of reading from and writing to NAOqi. Currently the API is unstable, and subject to quite a few changes. Instead of dealing with shared memory objects and semaphores, modules can simply send an intent to NAOInterface, which will effeciently relay that intent to NAOqi.

Clone this wiki locally