Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • sw/armarx/robot-api
  • uwkce_singer/robot-api
  • untcg_hofmann/robot-api
  • ulqba_korosakov/RobotAPI
4 results
Show changes
Commits on Source (21)
Showing
with 461 additions and 548 deletions
# RobotAPI
## Documentation
[Wiki](docs)
[Online Documentation](https://armarx.humanoids.kit.edu/RobotAPI-Overview.html)
Stub to make the package tool work.
[Aron](aron)
[ArMem](armem)
* [Introduction](introduction)
* [Existing Memories (Memory Servers)](existing_memory_servers_and_segments)
* [How to create a new Core Segment or Memory Server](how_to_create_a_new_core_segment_or_memory_server)
This is a list of existing memory servers, their core segments and their ARON data types.
# `RobotState`
* Memory Server: [`RobotStateMemory` (RobotAPI)](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/tree/master/source/RobotAPI/components/armem/server/RobotStateMemory)
* Core Segments:
| Core Segment Name | Type | Description | Example Entities |
|-------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|-------------------------|
| `Description` | [RobotDescription](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem_robot/aron/RobotDescription.xml) | Robot description (e.g. link to XML or URDF file) | |
| `Proprioception` | ToDo | Robot configuration and internal sensor values. | tbd |
| [`Localization`](RobotState/Localization) | [Transform](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem_robot_localization/aron/Transform.xml) | Transformations between frames {world,map,robot} to retrieve global robot pose. | armarx::aron::Transform |
Missing:
- Robot calibration: should be part of `Description` core segment
# `Object`
* Memory Server: [`ObjectMemory` (RobotAPI)](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/components/armem/server/ObjectMemory)
* Core Segments:
| Core Segment Name | Type | Description | Example Entities |
|-----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|-----------------------------------------------------|
| `Class` | [ObjectClass](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem_objects/aron/ObjectClass.xml) | Static description of a known object class. Entity names are (ArmarXObjects) object IDs. | `KIT/CoffeeFilters`, `YCB/001_chips_can` |
| `Instance` | [ObjectInstance](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem_objects/aron/ObjectInstance.xml) | Localized object instance. Entity names are (ArmarXObjects) object IDs. | `KIT/CoffeeFilters`, `YCB/001_chips_can/instance_1` |
| `ArticulatedObjectClass` | [RobotDescription](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem_robot/aron/RobotDescription.xml) | Static description of a known object class. Entity names are (ArmarXObjects) object IDs. | |
| `ArticulatedObjectInstance` | [Robot](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem_robot/aron/Robot.xml) | Localized object instance. Entity names are (ArmarXObjects) object IDs. | |
| `Attachments` | [ObjectAttachment and ArticulatedObjectAttachment](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem_objects/aron/Attachment.xml) | | |
# `Vision`
* Memory Server: [`ArMemVisionMemory` (VisionX)](https://git.h2t.iar.kit.edu/sw/armarx/visionx/-/tree/master/source/VisionX/components/armem/ArMemVisionMemory)
* Core Segments:
| Core Segment Name | Type | Description | Example Entities |
|-------------------|--------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------|------------------|
| `ImageRGB` | [`ImageRGB`](https://git.h2t.iar.kit.edu/sw/armarx/visionx/-/blob/master/source/VisionX/libraries/ArMem/aron/ImageRGB.xml) | RGB images (mono and stereo) of a camera | image |
| `ImageDepth` | [`ImageDepth`](https://git.h2t.iar.kit.edu/sw/armarx/visionx/-/blob/master/source/VisionX/libraries/ArMem/aron/ImageDepth.xml) | Depth images of an RGB-D or stereo camera | image |
# `Speech`
* Memory Server: [`SpeechMemory` (SpeechX)](https://git.h2t.iar.kit.edu/sw/armarx/speechx/-/tree/master/source/SpeechX/components/SpeechMemory)
* Core Segments:
| Core Segment Name | Type | Description | Example Entities |
|-------------------------|-----------------|---------------------------------------------------------------------------|------------------|
| `Command` | `SpeechCommand` | Language commands from speech (topic `Speech_Commands`). | `command` |
| `TextListenerInterface` | `Text` | Data from `TextListenerInterface` topics. (`TextToSpeech`, `RobotAction`) | `text` |
...
# `Skills`
* Memory Server: [`SkillsMemory` (RobotAPI)](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/tree/master/source/RobotAPI/components/armem/server/SkillsMemory)
* Core Segments:
| Core Segment Name | Type | Description | Example Entities |
|-------------------|--------------|-------------------------------------|------------------|
| `Statechart` | `Transition` | Transitions in ArmarX state charts. | tbd |
# `GeneralPurpose`
This memory is meant to allow for a quick-and-dirty integration of your data type in the memory framework. When your development finishes a stable state, consider creating moving the core segment(s) to an existing memory server or creating a new dedicated memory server.
* Memory Server: [`GeneralPurposeMemory` (RobotAPI)](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/tree/master/source/RobotAPI/components/armem/server/GeneralPurposeMemory)
* Core Segments:
| Core Segment Name | Type | Description | Example Entities |
|----------------------------------------|------|-------------|------------------|
| None. You can add segments on the fly. | | | |
# `Mapping`
* Memory Server: `ToDo`
* Core Segments:
| Core Segment Name | <br/>Type | Description | Example Entities |
|---------------------------------|-----------|--------------------------------------------------|------------------|
| [`Mapping`](RobotState/Mapping) | `ToDo` | Mapping related sensor data (e.g. point clouds). | tbd |
# `Example`
An example memory server (alongside a matching client) showing how to implement a standard memory server and test the framework during development.
* Memory Server: [`ExampleMemory` (RobotAPI)](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/tree/master/source/RobotAPI/components/armem/server/ExampleMemory)
* Core Segments:
| Core Segment Name | Type | Description | Example Entities |
|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|------------------|
| `ExampleData` | [ExampleData](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/components/armem/server/ExampleMemory/aron/ExampleData.xml) | Some example data. | example |
\ No newline at end of file
# The localization segment within the RobotState memory
## Concept
There exist various frames that are needed to obtain the robot's global pose (pose within in the global frame):
* [global] the "world" frame.
* [map] During mapping, the robot does not have knowledge about the world but creates a local map (with "map" as root frame). All map features (e.g. point clouds, occupancy grids, ...) are linked to this frame.
* [odom] The odometry as an integration of the robot's velocity.
* [robot] The robot's root frame
![frames](res/frames.png)
For each transformation between frames, there exist individual components:
* [global->map] **Map registration:** given a scene (objects with known poses, e.g. walls, ...), this component obtains a static transformation between a the scene and the map.
* [map->odom] **Localization** The localization component computes the transformation [map->robot] and performs map corrections by updating [map->odom].
* [odom->robot] **Odometry**
### Memory design
Within the core segment, each robot has its own provider segment (here: Armar6). Within the provider segment, entities describe the transformations between the frames ("Global,Map" -> [global->map]).
![Localization](res/localization-armem.png)
### Using the localization memory
See **RobotAPI/libraries/armem_robot_localization**
There exist two classes:
* TransformWriter to send transformations to memory
* TransformReader to obtain transformations between any of the frames; can also be used to obtain the "robot's global pose"
For an example, see RobotComponents/components/carographer_mapping_and_localization (currently branch feature/cartographer_integration)
# The mapping segment within the RobotState memory
## Concept
Within the memory segment, sensor data is stored that is relevant for
* mapping
* map registration
![transformations_frames-Page-2](res/transformations_frames-Page-2.png)
# Distributed Memory
The ArMem memory system is distributed: It is comprised of several **memory servers** storing data in a hierarchical structure.
A memory server can be defined in any ArmarX package and defines which kind of data it stores (e.g. robot state, object classes and instances, visual data, speech, ...).
As a memory server knows which data it stores, it can provide special behaviour, such as custom visualization, aggregation, interpolation or prediction.
As each server is a separate component describing its own structure, it is simple to add new memory servers containing special data types, which are defined in ArmarX packages further down in the dependency chain.
All memory servers follow the same hierarchical data structure and self-describing data format.
The data structure consists of several levels, storing histories/timelines of different entities stored in different segments (see "Memory Levels" below).
The common data format is ARON (ArmarX Object Notation), which is a self-describing, hierarchical data format, allowing extensive introspection as well as general storage.
Each item in the memory (i.e. entries in all levels) can be identified with a **Memory ID**, which contains the keys of each (specified) level. For example, a core segment ID specifies the memory name and core segment name, while an entity instance ID specifies the whole path to the leaf of the data structure.
Technically, each memory server is one ArmarX component implementing the `armem::server::MemoryInterface` (comprising a `ReadingMemoryInterface` and a `WritingMemoryInterface`).
Memory servers register themselves in the **Memory Name System (MNS)**, where they can be found by their (semantic) memory name (which is usually a short, human-readable name such as "Robot", "Object" or "Vision").
Memory servers receive data via **commits** (via the `WritingMemoryInterface`), and return data according to **queries** (via the `ReadingMemoryInterface`).
# Memory Levels
A memory server stores data in a hierarchical structure consisting of several levels:
```
- Grasping (Memory)
- Grasps (Core Segment)
- KnownObjectGraspPlanner (Provider Segment)
- my_object (Entity)
- t = 2021-03-09 14:24:20.064524 (Entity Snapshot)
- 00 (Entity Instance)
- 01 (Entity Instance)
- 02 (Entity Instance)
```
| Level | Key | Description | Examples | Implementation |
| ----- | --- | ----------- | -------- | -------------- |
| Memory | Name (String) | Semantic grouping of one or more core segments. Corresponds to one memory server. | Robot, Object, Vision | [Memory](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem/core/Memory.h) |
| Core Segment | Name (String) | Building block of a memory containing homogeneous data of a specific (ARON) type. Specifies a _Core Segment Type_ which all provided data must comply to. | (Robot) Configuration, (Object) Classes, Known (Object) Instances, ImageRGB | [CoreSegment](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem/core/CoreSegment.h) |
| Provider Segment | Name (String) | Sub-segment of a core segment which contains the data from a single source (e.g. a camera, a method, a component). Can define a _Provider Segment Type_ which extends the _Core Segment Type_. | Primsense, SimTrack, MyGraspPlanner | [ProviderSegment](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem/core/ProviderSegment.h) |
| Entity | Name (String) | A single thing or concept whose properties change over time. An entity has a history / timeline, i.e. a sequence of snapshots storing the entity's properties at a specific point in time. | `image`, `some_object`, `some_object_grasps` | [Entity](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem/core/Entity.h) |
| Entity Snapshot | Timestamp (`armem::Time` aka `IceUtil::Time`) | An entry of an entity's history, i.e. the state of an entity at a specific point in time. Can contain multiple (entitiy) instances. | Image, object instance, grasp hypotheses at a time *t* | [EntitySnapshot](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem/core/EntitySnapshot.h) |
| Entity Instance | Index (int) | One instance of the segment's ARON type. | left/right stereo image (at time t), object (at time t), grasp hypothesis (one of many at time t) | [EntityInstance](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem/core/EntityInstance.h) |
| Entity Instance Metadata | -- | Metadata stored alongside the instance's data. | Further timesteps, confidence | [EntityInstanceMetadata](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem/core/EntityInstance.h) |
* [Introduction](introduction)
* [Code Generation](code_generation)
* [Visitors](visitors)
* [Readers, Writers and Conversion](conversion)
# Aron conversion
Aron offers ways to simply convert any aron object into another representation (e.g. from variant to nlohmann::json or vice versa). To do so, it makes use of specific readers and writers. In the following we will only describe the readers, writers and converters for Aron data, not for types (but the principle is the same).
## Readers
An Aron reader is used to get information of an Aron object in a specific representation. Assume you have an nlohmann::json aron object which contains some information (E.g. it is a dict, containing some members, ...). We need this information to create another object in another representation with the same content.
To do so, you only have to implement the armarx::aron::data::ReaderInterface class. It needs one template parameter for your InputType (e.g. here const nlohmann::json).
The interface provides the following pure virtual methods:
```cpp
virtual void readList(InputType& input, std::vector<InputTypeNonConst>& elements) = 0;
virtual void readDict(InputType& input, std::map<std::string, InputTypeNonConst>& elements) = 0;
virtual void readNDArray(InputType& input, std::vector<int>& shape, std::string& typeAsString, std::vector<unsigned char>& data) = 0;
virtual void readInt(InputType& input, int& i) = 0;
virtual void readLong(InputType& input, long& i) = 0;
virtual void readFloat(InputType& input, float& i) = 0;
virtual void readDouble(InputType& input, double& i) = 0;
virtual void readString(InputType& input, std::string& s) = 0;
virtual void readBool(InputType& input, bool& i) = 0;
```
You have to implement the function so that the non-const arguments of the method will be filled with the values of the input. E.g. the implementation of the readString method for the nlohmann::json reader would be:
```cpp
void NlohmannJSONReader::readString(const nlohmann::json& input, std::string& i)
{
if (input[rw::json::constantes::TYPE_SLUG] != expectedType)
{
throw error::ValueNotValidException(__PRETTY_FUNCTION__, "Wrong type in json encountered.", input[rw::json::constantes::TYPE_SLUG], expectedType);
}
i = input[rw::json::constantes::VALUE_SLUG];
}
```
Of course, the way to get the member depend on the way how to construct (writer) the nlohmann::json.
\ No newline at end of file
[[_TOC_]]
# Aron structure
Aron (ArmarX Object Notation) is the ArmarX implementation of variants which can be transferred over the network via ZeroC Ice. Further, Aron is the data representation used in the ArmarX Memory System [ArMem](ArMem/ArMem). We distinguish between:
- type specification vs. data information
- data transfer object (in the following called Aron DTO) vs. its corresponding c++ wrapper (in the following called Aron object). The DTO specification is done in ice so that every Aron object can be transferred via ice.
## Aron Type specification
An Aron type specification defines the static type for an Aron data DTO or an Aron data object. It does not contain any data (e.g. an aron type list only knows the accepted type, not the list members). Since Aron supports maybe types (raw ptr, smart ptr, optional), every Aron type DTO contains a special member. It can only consist of the following types and information (AronVariant means any Type):
```cpp
Object { // dto called armarx::aron::type::dto::AronObject
string name;
AronVariant extends;
map<string, AronVariant> memberTypes;
}
Dict { // dto called armarx::aron::type::dto::Dict
AronVariant acceptedType;
}
List { // dto called armarx::aron::type::dto::List
AronVariant acceptedType;
}
Pair { // ...
AronVariant acceptedType1;
AronVariant acceptedType2;
}
Tuple {
vector<AronVariant> acceptedTypes;
}
IntEnum {
string name;
map<string, int> acceptedValues;
}
NDArray {
int ndim;
type type; // in [uint8, int8, uint16, int16, uint32, int32, float32, float64]
}
Matrix {
int rows, cols;
type type; // in [int16, int32, int64, float32, float64]
}
Quaternion {
type type; // in [float32, float64]
}
Position {
}
Orientation {
}
Pose {
}
Image {
pixelType type; // in [rgb24, depth32]
}
PointCloud {
voxelType type; // in [PointXYZ, PointXYZI, PointXYZL, PointXYZRGB, PointXYZRGBA, PointXYZRGBL, PointXYZHSV]
}
Int { // dto called armarx::aron::type::dto::AronInt
}
Long { // dto called armarx::aron::type::dto::AronLong
}
Float { // dto called armarx::aron::type::dto::AronFloat
}
Double { // dto called armarx::aron::type::dto::AronDouble
}
String { // dto called armarx::aron::type::dto::AronString
}
Bool { // dto called armarx::aron::type::dto::AronBool
}
Time { // dto called armarx::aron::type::dto::AronTime
}
```
## Aron Data specification
Aron data objects and Aron data DTOs are similar structured to type objects and type DTOs. However, there are less classes for data objects and data DTOs. Aron data is completely decoupled from the static types and contains the data members (e.g. an aron data list only contains the list members (as AronVariants) and not the accepted type):
```cpp
Dict { // dto called armarx::aron::data::dto::Dict
map<string, AronVariant> members;
}
List { // dto called armarx::aron::data::dto::List
vector<AronVariant> elements;
}
NDArray { // ...
vector<int> shape;
string type;
vector<byte> data;
}
Int { // dto called armarx::aron::data::dto::AronInt
int value;
}
Long { // dto called armarx::aron::data::dto::AronLong
long value;
}
Float { // dto called armarx::aron::data::dto::AronFloat
float value;
}
Double { // dto called armarx::aron::data::dto::AronDouble
double value;
}
String { // dto called armarx::aron::data::dto::AronString
string value;
}
Bool { // dto called armarx::aron::data::dto::AronBool
bool value;
}
```
Please note that every Aron data object or DTO can be NULL!. The reason is, that if the type supports maybe types and a member is e.g. optional the data must support maybetype as well. If a generated Aron class (We come to the code generation later) has an optional member, this member will be translated into a NULL Aron object and DTO.
## Connection of Aron data and Aron type
Aron data contains less classes than Aron type. Because Aron data objects and DTOs do not check the type of the members (they can be any aron data (AronVariant)), we can only validate an aron data object or DTO if we have the type information. The following mapping describes, how data and types correspond.
```cpp
Aron Type | Aron Data
-------------------------------
Object -> Dict
Dict -> Dict
List -> List
Pair -> List
Tuple -> List
NDArray -> NDArray
Matrix -> NDArray
Quaternion -> NDArray
Position -> NDArray
Orientation -> NDArray
Pose -> NDArray
Image -> NDArray
PointCloud -> NDArray
IntEnum -> Int
Int -> Int
Long -> Long
Float -> Float
Double -> Double
String -> String
Bool -> Bool
Time -> Long
```
If no type object or DTO is available, we can at least derive some information from the data object (e.g. "it is a data dict, so the type cant be list").
To differ between data and type makes it easier to work with these Variants. In most cases, the type information is not relevant. Then you only have to check for the data type.
# Aron data and type objects
As already mentioned, we implemented wrapper classes around the DTO. These classes offer convenience methods and conversions and make lots things easier when working directly with Aron. These classes can be found at aron/core/[data,type]/variant/Variant.h.
The Aron objects have more or less the same structure as the DTOs (see above). Everything is stored as a shared_ptr (e.g. members, elements, acceptedTypes, ...). If you want to implement a method which takes any Aron data object as input, you can use the base class of every Aron data object:
```cpp
void myFancyMethod(const armarx::aron::data::VariantPtr& variant);
```
If you want to check, what a variant really is, you can use the descriptor of the object:
```cpp
armarx::aron::data::VariantPtr variant; // <-- the variant
auto desc = variant->getDescriptor();
switch(desc)
{
case armarx::aron::data::Descriptor::eDict: ...
case armarx::aron::data::Descriptor::eList: ...
...
}
```
If you have a DTO, you dont know the exact type but you want to have a Aron object you can make use of the static construction methods:
```cpp
armarx::aron::data::dto::GenericData dto; // <-- the DTO variant
auto aron = armarx::aron::data::Variant::FromAronDTO(dto);
```
If you know the type, you can use the constructor the specific Aron object.
## Example to create an Aron data dict from scratch
Goal: Have an Aron dict with a list as member "the_list" and some values in it. Then echo the members and their descriptor as a string.
```cpp
using namespace armarx;
// setup aron object
auto dict = std::make_shared<aron::data::Dict>();
{
auto list = std::make_shared<aron::data::List>();
for (unsigned int i = 0; i < 10; ++i)
{
list->addElement(std::make_shared<aron::data::Int>(i));
}
dict->addElement("the_list", list);
}
// echo
auto listVariant = dict->getElement("the_list"); // will return a aron::data::VariantPtr
auto list = aron::data::List::DynamicCastAndCheck(listVariant); // cast and check whether the cast was successful
for (const auto& intVar : list->getElements())
{
auto i = aron::data::Int::DynamicCastAndCheck(intVar);
std::cout << "The value is: " << i->getValue() << std::endl;
std::cout << "The descriptor is: " << aron::data::defaultconversion::string::Descriptor2String.at(i->getDescriptor()) << std::endl;
}
```
Please note that we might add more methods or make the current ones more flexible (e.g. such as nlohmann::json).
# Lessons learned
- There is a difference between Aron data and Aron type
- There is a difference between Aron objects and Aron DTOs
- How are Aron DTOs structured
- How do Aron data and Aron types correspond
- How to use the Aron objects
\ No newline at end of file
[[_TOC_]]
# Visitors
Visitors are a useful tool to define specific methods for specific types without specifying the `switch(): case: ...` again and again. Further, recursive visitors offer a convenient way to iterate through the tree-like structure of any Aron object. (Please note that right now the implementation does not check for cycles!). Again, we will only look at data visitors since the implementation of type visitors is more or less similar.
## Non-recursive Visitors
First, there are "normal" visitors. They are used to get rid of the big switch-case statement when getting an Aron object with unknown type. Further, these visitors can be used to convert an Aron object from one representation into another (see [Aron/Conversion](Aron/Conversion)).
The Visitor Base-class offers the following pure virtual methods which you have to implement:
```cpp
virtual data::Descriptor getDescriptor(Input&) = 0;
virtual void visitDict(Input&) {};
virtual void visitList(Input&) {};
virtual void visitNDArray(Input&) {};
virtual void visitInt(Input&) {};
virtual void visitLong(Input&) {};
virtual void visitFloat(Input&) {};
virtual void visitDouble(Input&) {};
virtual void visitBool(Input&) {};
virtual void visitString(Input&) {};
virtual void visitUnknown(Input&) { throw error::AronException(__PRETTY_FUNCTION__, "Unknown type in visitor."); }
```
`Input` is a template parameter, defining the Input-type (e.g. `const armarx::aron::data::VariantPtr` for a `VariantVisitor` Implementation).
The method `getDescriptor(Input&)` is used for the switch case interally.
As you can see the `VariantVisitor` always gets a generic variant as input in each function. This means that, although the Visitor guarantees that the input is correct, you have to cast the class to the correct type. For widely used Visitors, the Aron library offers convenience implementations (e.g. for Variants and for nlohmann::json inputs). The convenience class for Variants additionally adds overloads where the input is already casted correctly (you can decide which method you want to override).
To use a Visitor, you have to use the function
```cpp
void visit(VisitorImplementation& v, typename VisitorImplementation::Input& o);
```
which takes the visitor and the input as arguments and does the switch-case.
## Recursive Vistors
Recursive visitors are similar to normal vistors but they continue "visiting" until the object is fully visited. For containers (e.g. dicts, lists) they offer two methods each:
```cpp
virtual void visitDictOnEnter(Input& element) {};
virtual void visitDictOnExit(Input& element) {};
virtual void visitListOnEnter(Input& element) {};
virtual void visitListOnExit(Input& element) {};
```
The `*OnEnter` method is called before iterating over all children of the container. The `*OnExit` is called after the iteration.
Further, in order to get the elements of a container, you must implement the methods:
```cpp
virtual MapElements getDictElements(Input&) = 0;
virtual ListElements getListElements(Input&) = 0;
```
Again, Input can be any aron object (e.g. Variant or nlohmann::json).
As for normal visitors, the Aron library offers convenience implementations for Variants and nlohmann::json inputs.
To use the RecursiveVisitor, you have to use the function
```cpp
void visitRecursive(RecursiveVisitorImplementation& v, typename RecursiveVisitorImplementation::Input& o);
```
### Example
Assume you want to count how many objects of each kind you have in a Variant.
```cpp
#include <RobotAPI/libraries/aron/core/data/visitor/variant/VariantVisitor.h>
class CountingRecursiveVisitor : public RecursiveConstVariantVisitor
{
public:
int dicts = 0;
int lists = 0;
int ndarrays = 0;
int ints = 0;
int floats = 0;
int longs = 0;
int doubles = 0;
int strings = 0;
int bools = 0;
public:
void visitAronVariantOnEnter(const data::DictPtr&) override { dicts++; }
void visitAronVariantOnEnter(const data::ListPtr&) override { lists++; }
void visitAronVariant(const data::NDArrayPtr&) override { ndarrays++; }
void visitAronVariant(const data::IntPtr&) override { ints++; }
void visitAronVariant(const data::LongPtr&) override { longs++; }
void visitAronVariant(const data::FloatPtr&) override { floats++; }
void visitAronVariant(const data::DoublePtr&) override { doubles++; }
void visitAronVariant(const data::BoolPtr&) override { strings++; }
void visitAronVariant(const data::StringPtr&) override { bools++; }
};
void countHowMany(const aron::data::VariantPtr& aron)
{
CountingRecursiveVisitor visitor;
aron::data::visitRecursive(visitor, aron);
std::cout << "The aron has " << visitor.dicts << " dicts." << std::endl;
}
```
## Typed Visitors
# Lessons Learned
\ No newline at end of file
......@@ -63,6 +63,13 @@ RobotAPI also provides a GUI-plugin for robot visualization.
\par
\link RobotAPI-GuiPlugins Read more \endlink
\par RobotUnit
\subpage RobotUnit
\par GamepadUnit
\subpage GamepadUnit
\defgroup RobotAPI RobotAPI
\copydoc RobotAPI-Overview
......
# Localization Segment (RobotState Memory) {#memory_system-localization}
## Concept
There exist various frames that are needed to obtain the robot's global pose (pose within in the global frame):
| Name | Description |
|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `global` | The "world" frame |
| `map` | During mapping, the robot does not have knowledge about the world but creates a local map (with `map` as root frame). All map features (e.g. point clouds, occupancy grids, ...) are linked to this frame. |
| `odom` | The odometry as an integration of the robot's velocity. |
| `robot` | The robot's root frame |
![frames](localization/frames.png)
For each transformation between frames, there exist individual components:
| Involved Frames | Description |
|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `global->map` | **Map registration:** given a scene (objects with known poses, e.g. walls, ...), this component obtains a static transformation between a the scene and the map. |
| `map->odom` | **Localization** The localization component computes the transformation `map->robot` and performs map corrections by updating `map->odom`. |
| `odom->robot` | **Odometry** |
### Memory Design
Within the core segment, each robot has its own provider segment (here: `Armar6`).
Within the provider segment, entities describe the transformations between the frames (`Global,Map` -> `global->map`).
![Localization](localization/localization-armem.png)
### Using the Localization Memory
See `armarx::armem::client::robot_state::localization` (include `RobotAPI/libraries/armem_robot_localization/`).
There exist two classes:
* `armarx::armem::client::robot_state::localization::TransformWriter` to send transformations to memory
* `armarx::armem::client::robot_state::localization::TransformReader` to obtain transformations between any of the
frames; can also be used to obtain the "robot's global pose"
For an example, see `RobotComponents/components/carographer_mapping_and_localization` (currently branch `feature/cartographer_integration`)
# How to Create a New Core segment or Memory Server {#memory_system-how_to_create_a_new_core_segment_or_memory_server}
@see
\ref tutorials_120_memory_server_and_client_cpp
\todo
Merge the information from here into \ref tutorials_120_memory_server_and_client_cpp
In order to make a new type of data available in the memory system, you first need to create an ARON XML file. Please refer to [Aron/CodeGeneration](Aron/CodeGeneration) if you want to know how to do so.
[[_TOC_]]
<!--
# Define your data type in ARON
......@@ -390,4 +397,4 @@ When you now start your new memory server and the MemoryNameSystem (MNS) (e.g. u
That's it! You now have a memory server providing a place where your nice and interesting data can feel welcome, and where interested listeners would look to find it.
[Next, we learn how to commit data to the memory, and query data from the memory.](ArMem/How-to-read-from-and-write-to-a-Memory-Server)
\ No newline at end of file
[Next, we learn how to commit data to the memory, and query data from the memory.](ArMem/How-to-read-from-and-write-to-a-Memory-Server)
# ArmarX Memory System {#memory_system}
\tableofcontents
@see
- \ref tutorials_120_memory_server_and_client_cpp
- \subpage memory_system-how_to_create_a_new_core_segment_or_memory_server
## Introduction to the Memory System {#memory_system-introduction}
### Distributed Memory
The ArMem memory system is distributed: It consists of several **memory servers** storing data in a hierarchical
structure.
A memory server can be defined in any ArmarX package and defines which kind of data it stores
(e.g. robot state, object classes and instances, visual data, speech, ...).
As a memory server knows which data it stores, it can provide special behaviour, such as custom visualization,
aggregation, interpolation or prediction.
As each server is a separate component describing its own structure, it is simple to add new memory servers containing
special data types, which are defined in ArmarX packages further down in the dependency chain.
All memory servers follow the same hierarchical data structure and self-describing data format.
The data structure consists of several levels, storing histories/timelines of different entities stored in different
segments (see "Memory Levels" below).
The common data format is \ref aron, which is a self-describing, hierarchical data format,
allowing extensive introspection as well as general storage.
Each item in the memory (i.e. entries in all levels) can be identified with a **Memory ID**, which contains the keys
of each (specified) level. For example, a core segment ID specifies the memory name and core segment name, while an
entity instance ID specifies the whole path to the leaf of the data structure.
Technically, each memory server is one ArmarX component implementing the `armarx::armem::server::MemoryInterface`
(comprising a `armarx::armem::server::ReadingMemoryInterface` and a `armarx::armem::server::WritingMemoryInterface`).
Memory servers register themselves in the **Memory Name System (MNS)**, where they can be found by their (semantic)
memory name (which is usually a short, human-readable name such as "Robot", "Object" or "Vision").
Memory servers receive data via **commits** (via the `armarx::armem::server::WritingMemoryInterface`),
and return data according to **queries** (via the `armarx::armem::server::ReadingMemoryInterface`).
### Memory Levels
A memory server stores data in a hierarchical structure consisting of several levels:
```
- Grasping (Memory)
- Grasps (Core Segment)
- KnownObjectGraspPlanner (Provider Segment)
- my_object (Entity)
- t = 2021-03-09 14:24:20.064524 (Entity Snapshot)
- 00 (Entity Instance)
- 01 (Entity Instance)
- 02 (Entity Instance)
```
| Level | Key | Description | Examples | Implementation |
|--------------------------|----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|---------------------------------------------|
| Memory | Name (String) | Semantic grouping of one or more core segments. Corresponds to one memory server. | Robot, Object, Vision | `armarx::armem::wm::Memory` |
| Core Segment | Name (String) | Building block of a memory containing homogeneous data of a specific (ARON) type. Specifies a _Core Segment Type_ which all provided data must comply to. | (Robot) Proprioception, (Object) Classes, Known (Object) Instances, ImageRGB | `armarx::armem::wm::CoreSegment` |
| Provider Segment | Name (String) | Sub-segment of a core segment which contains the data from a single source (e.g. a camera, a method, a component). Can define a _Provider Segment Type_ which extends the _Core Segment Type_. | Primesense, MyObjectLocalizer, MyGraspPlanner | `armarx::armem::wm::ProviderSegment` |
| Entity | Name (String) | A single thing or concept whose properties change over time. An entity has a history / timeline, i.e. a sequence of snapshots storing the entity's properties at a specific point in time. | `image`, `some_object`, `some_object_grasps` | `armarx::armem::wm::Entity` |
| Entity Snapshot | Timestamp (`armarx::armem::Time` aka `armarx::DateTime`) | An entry of an entity's history, i.e. the state of an entity at a specific point in time. Can contain multiple (entitiy) instances. | Image, object instance, grasp hypotheses at a time *t* | `armarx::armem::wm::EntitySnapshot` |
| Entity Instance | Index (int) | One instance of the segment's ARON type. | left/right stereo image (at time t), object (at time t), grasp hypothesis (one of many at time t) | `armarx::armem::wm::EntityInstance` |
| Entity Instance Metadata | -- | Metadata stored alongside the instance's data. | Further timesteps, confidence | `armarx::armem::wm::EntityInstanceMetadata` |
## Existing Memory Servers and Segments {#memory_system-existing_servers_segments}
This is a list of existing memory servers, their core segments and their \ref ARON "ARON" data types.
### RobotState {#memory_system-robot_state}
* Memory Server: [`RobotStateMemory` (RobotAPI)](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/tree/master/source/RobotAPI/components/armem/server/RobotStateMemory)
* Core Segments:
| Core Segment Name | Type | Description | Example Entities |
|----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|----------------------------------------------|
| Description | [RobotDescription](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem_robot/aron/RobotDescription.xml) | Robot description (e.g. link to XML or URDF file) | Description of ARMAR-6 |
| Proprioception | [Proprioception](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem_robot_state/aron/Proprioception.xml) | Robot configuration and internal sensor values. | Proprioception of ARMAR-6 |
| \subpage memory_system-localization "Localization" | [Transform](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem_robot_localization/aron/Transform.xml) | Transformations between frames {world,map,robot} to retrieve global robot pose. | Transfrom from robot root frame to map frame |
Missing:
- Robot calibration: should be part of `Description` core segment
### Object
* Memory Server: [`ObjectMemory` (RobotAPI)](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/components/armem/server/ObjectMemory)
* Core Segments:
| Core Segment Name | Type | Description | Example Entities |
|--------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|-----------------------------------------------------|
| Class | [ObjectClass](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem_objects/aron/ObjectClass.xml) | Static description of a known object class. Entity names are (ArmarXObjects) object IDs. | `KIT/CoffeeFilters`, `YCB/001_chips_can` |
| Instance | [ObjectInstance](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem_objects/aron/ObjectInstance.xml) | Localized object instance. Entity names are (ArmarXObjects) object IDs. | `KIT/CoffeeFilters`, `YCB/001_chips_can/instance_1` |
| Attachments | [ObjectAttachment and ArticulatedObjectAttachment](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/libraries/armem_objects/aron/Attachment.xml) | | |
### Vision
* Memory Server: [`ArMemVisionMemory` (VisionX)](https://git.h2t.iar.kit.edu/sw/armarx/visionx/-/tree/master/source/VisionX/components/armem/ArMemVisionMemory)
* Core Segments:
| Core Segment Name | Type | Description | Example Entities |
|-------------------|--------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------|------------------|
| ImageRGB | [`ImageRGB`](https://git.h2t.iar.kit.edu/sw/armarx/visionx/-/blob/master/source/VisionX/libraries/ArMem/aron/ImageRGB.xml) | RGB images (mono and stereo) of a camera | image |
| ImageDepth | [`ImageDepth`](https://git.h2t.iar.kit.edu/sw/armarx/visionx/-/blob/master/source/VisionX/libraries/ArMem/aron/ImageDepth.xml) | Depth images of an RGB-D or stereo camera | image |
### Speech
* Memory Server: [`SpeechMemory` (SpeechX)](https://git.h2t.iar.kit.edu/sw/armarx/speechx/-/tree/master/source/SpeechX/components/SpeechMemory)
* Core Segments:
| Core Segment Name | Type | Description | Example Entities |
|-----------------------|-----------------|---------------------------------------------------------------------------|------------------|
| Command | `SpeechCommand` | Language commands from speech (topic `Speech_Commands`). | `command` |
| TextListenerInterface | `Text` | Data from `TextListenerInterface` topics. (`TextToSpeech`, `RobotAction`) | `text` |
...
### Skills
* Memory Server: [`SkillsMemory` (RobotAPI)](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/tree/master/source/RobotAPI/components/armem/server/SkillsMemory)
* Core Segments:
| Core Segment Name | Type | Description | Example Entities |
|-------------------|--------------|-------------------------------------|------------------|
| Statechart | `Transition` | Transitions in ArmarX state charts. | tbd |
### Example
An example memory server (alongside a matching client) showing how to implement a standard memory server and test the framework during development.
* Memory Server: [`ExampleMemory` (RobotAPI)](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/tree/master/source/RobotAPI/components/armem/server/ExampleMemory)
* Core Segments:
| Core Segment Name | Type | Description | Example Entities |
|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|------------------|
| ExampleData | [ExampleData](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/blob/master/source/RobotAPI/components/armem/server/ExampleMemory/aron/ExampleData.xml) | Some example data. | example |
### GeneralPurpose
This memory is meant to allow for a quick-and-dirty integration of your data type in the memory framework. When your development finishes a stable state, consider creating moving the core segment(s) to an existing memory server or creating a new dedicated memory server.
* Memory Server: [`GeneralPurposeMemory` (RobotAPI)](https://git.h2t.iar.kit.edu/sw/armarx/robot-api/-/tree/master/source/RobotAPI/components/armem/server/GeneralPurposeMemory)
* Core Segments:
| Core Segment Name | Type | Description | Example Entities |
|----------------------------------------|------|-------------|------------------|
| None. You can add segments on the fly. | | | |
# ArmarX Object Notation (ARON) / Interpretable Data Format (IDF) {#aron}
\tableofcontents
\see
- \subpage aron-code_generation
- \subpage aron-visitors
- \subpage aron-conversion
ARON (ArmarX Object Notation) is the ArmarX implementation of variants which can be transferred over the network via
ZeroC Ice.
Further, ARON is the data representation used in the \ref memory_system.
We distinguish between:
- type specification vs. data information
- data transfer object (in the following called "ARON DTO") vs. its corresponding C++ wrapper
(in the following called "ARON object").
The DTO specification is done in ice so that every ARON object can be transferred via ice.
## ARON Type specification
An ARON type specification defines the static type for an ARON data DTO or an ARON data object. It does not contain any
data (e.g. an ARON type list only knows the accepted type, not the list members).
Since ARON supports maybe types (raw ptr, smart ptr, optional), every ARON type DTO contains a special member.
It can only consist of the following types and information (ARONVariant means any Type):
```cpp
Object { // dto called armarx::aron::type::dto::AronObject
string name;
AronVariant extends;
map<string, AronVariant> memberTypes;
}
Dict { // dto called armarx::aron::type::dto::Dict
AronVariant acceptedType;
}
List { // dto called armarx::aron::type::dto::List
AronVariant acceptedType;
}
Pair { // ...
AronVariant acceptedType1;
AronVariant acceptedType2;
}
Tuple {
vector<AronVariant> acceptedTypes;
}
IntEnum {
string name;
map<string, int> acceptedValues;
}
NDArray {
int ndim;
type type; // in [uint8, int8, uint16, int16, uint32, int32, float32, float64]
}
Matrix {
int rows, cols;
type type; // in [int16, int32, int64, float32, float64]
}
Quaternion {
type type; // in [float32, float64]
}
Position {
}
Orientation {
}
Pose {
}
Image {
pixelType type; // in [rgb24, depth32]
}
PointCloud {
voxelType type; // in [PointXYZ, PointXYZI, PointXYZL, PointXYZRGB, PointXYZRGBA, PointXYZRGBL, PointXYZHSV]
}
Int { // dto called armarx::aron::type::dto::AronInt
}
Long { // dto called armarx::aron::type::dto::AronLong
}
Float { // dto called armarx::aron::type::dto::AronFloat
}
Double { // dto called armarx::aron::type::dto::AronDouble
}
String { // dto called armarx::aron::type::dto::AronString
}
Bool { // dto called armarx::aron::type::dto::AronBool
}
Time { // dto called armarx::aron::type::dto::AronTime
}
```
## ARON Data specification
ARON data objects and ARON data DTOs are similar structured to type objects and type DTOs.
However, there are fewer classes for data objects and data DTOs.
ARON data is completely decoupled from the static types and contains the data members
(e.g. an aron data list only contains the list members (as ARONVariants) and not the accepted type):
```cpp
Dict { // dto called armarx::aron::data::dto::Dict
map<string, AronVariant> members;
}
List { // dto called armarx::aron::data::dto::List
vector<AronVariant> elements;
}
NDArray { // ...
vector<int> shape;
string type;
vector<byte> data;
}
Int { // dto called armarx::aron::data::dto::AronInt
int value;
}
Long { // dto called armarx::aron::data::dto::AronLong
long value;
}
Float { // dto called armarx::aron::data::dto::AronFloat
float value;
}
Double { // dto called armarx::aron::data::dto::AronDouble
double value;
}
String { // dto called armarx::aron::data::dto::AronString
string value;
}
Bool { // dto called armarx::aron::data::dto::AronBool
bool value;
}
```
\note
Note that every ARON data object or DTO can be `nullptr`!
The reason is that if the type supports maybe types and a member is, for example, optional,
the data must support maybetype as well.
If a generated ARON class (We come to the code generation later) has an optional member,
this member will be translated into a NULL ARON object and DTO.
## Connection of ARON data and ARON type
ARON data contains fewer classes than ARON type.
Because ARON data objects and DTOs do not check the type of the members (they can be any aron data (ARONVariant)),
we can only validate an aron data object or DTO if we have the type information. The following mapping describes,
how data and types correspond.
| ARON Type | ARON Data |
|-------------|-----------|
| Object | Dict |
| Dict | Dict |
| List | List |
| Pair | List |
| Tuple | List |
| NDArray | NDArray |
| Matrix | NDArray |
| Quaternion | NDArray |
| Position | NDArray |
| Orientation | NDArray |
| Pose | NDArray |
| Image | NDArray |
| PointCloud | NDArray |
| IntEnum | Int |
| Int | Int |
| Long | Long |
| Float | Float |
| Double | Double |
| String | String |
| Bool | Bool |
| Time | Long |
If no type object or DTO is available, we can at least derive some information from the data object
(e.g. "it is a data dict, so the type cant be list").
To differ between data and type makes it easier to work with these Variants.
In most cases, the type information is not relevant.
Then you only have to check for the data type.
## ARON Data and Type Objects
As already mentioned, we implemented wrapper classes around the DTO.
These classes offer convenience methods and conversions and make lots things easier when working directly with ARON.
These classes can be found at `aron/core/{data,type}/variant/Variant.h`.
The ARON objects have more or less the same structure as the DTOs (see above).
Everything is stored as a `std::shared_ptr` (e.g. members, elements, acceptedTypes, ...).
If you want to implement a method which takes any ARON data object as input,
you can use the base class of every ARON data object:
```cpp
void myFancyMethod(const armarx::aron::data::VariantPtr& variant);
```
If you want to check, what a variant really is, you can use the descriptor of the object:
```cpp
armarx::aron::data::VariantPtr variant; // <-- the variant
auto desc = variant->getDescriptor();
switch(desc)
{
case armarx::aron::data::Descriptor::eDict: ...
case armarx::aron::data::Descriptor::eList: ...
...
}
```
If you have a DTO, you do not know the exact type,
but you want to have a ARON object you can make use of the static construction methods:
```cpp
armarx::aron::data::dto::GenericData dto; // <-- the DTO variant
auto aron = armarx::aron::data::Variant::FromAronDTO(dto);
```
If you know the type, you can use the constructor the specific ARON object.
### Example to Create an ARON Data Dict from Scratch
Goal: Have an ARON dict with a list as member "the_list" and some values in it.
Then echo the members and their descriptor as a string.
```cpp
using namespace armarx;
// setup aron object
auto dict = std::make_shared<aron::data::Dict>();
{
auto list = std::make_shared<aron::data::List>();
for (unsigned int i = 0; i < 10; ++i)
{
list->addElement(std::make_shared<aron::data::Int>(i));
}
dict->addElement("the_list", list);
}
// echo
auto listVariant = dict->getElement("the_list"); // will return a aron::data::VariantPtr
auto list = aron::data::List::DynamicCastAndCheck(listVariant); // cast and check whether the cast was successful
for (const auto& intVar : list->getElements())
{
auto i = aron::data::Int::DynamicCastAndCheck(intVar);
std::cout << "The value is: " << i->getValue() << std::endl;
std::cout << "The descriptor is: " << aron::data::defaultconversion::string::Descriptor2String.at(i->getDescriptor()) << std::endl;
}
```
Please note that we might add more methods or make the current ones more flexible (e.g. such as nlohmann::json).
## Lessons Learned
- There is a difference between ARON data and ARON type
- There is a difference between ARON objects and ARON DTOs
- How are ARON DTOs structured
- How do ARON data and ARON types correspond
- How to use the ARON objects