Create a Skill

Objective: Learn how to create an ArmarX skill.

Previous Tutorials: Create a "Hello World!" Component in ArmarX (C++)

Next Tutorials: Using Parameters of Skills via ARON

Reference Code: skill_tutorials

Create a new ArmarX Package

Open a terminal and create a new package:

# Create a new ArmarX package:
armarx-package init skill_tutorials

The output should look something like this:

> Creating directory ...... /code/ skill_tutorials ...
> Creating directory ...... /code/ skill_tutorials/source ...
> Creating directory ...... /code/ skill_tutorials/source/ skill_tutorials ...
> Generating .............. /code/ skill_tutorials/source/ skill_tutorials/CMakeLists.txt ...
> Creating directory ...... /code/ skill_tutorials/scenarios ...
> Generating .............. /code/ skill_tutorials/scenarios/CMakeLists.txt ...
> Creating directory ...... /code/ skill_tutorials/data ...
> Creating directory ...... /code/ skill_tutorials/data/ skill_tutorials ...
> Generating .............. /code/ skill_tutorials/data/ skill_tutorials/VariantInfo- skill_tutorials.xml ...
> Creating directory ...... /code/ skill_tutorials/etc ...
> Creating directory ...... /code/ skill_tutorials/etc/cmake ...
> Generating .............. /code/ skill_tutorials/etc/cmake/ArmarXPackageVersion.cmake ...
> Creating directory ...... /code/ skill_tutorials/etc/doxygen ...
> Creating directory ...... /code/ skill_tutorials/etc/doxygen/pages ...
> Generating .............. /code/ skill_tutorials/etc/doxygen/pages/Overview.dox ...
> Generating .............. /code/ skill_tutorials/etc/doxygen/pages/Tutorials.dox ...
> Generating .............. /code/ skill_tutorials/etc/doxygen/pages/HowTos.dox ...
> Generating .............. /code/ skill_tutorials/etc/doxygen/pages/FAQ.dox ...
> Generating .............. /code/ skill_tutorials/etc/doxygen/pages/GuiPlugins.dox ...
> Generating .............. /code/ skill_tutorials/etc/doxygen/pages/Components.dox ...
> Generating .............. /code/ skill_tutorials/etc/doxygen/mainpage.dox ...
> Generating .............. /code/ skill_tutorials/etc/CMakeLists.txt ...
> Generating .............. /code/ skill_tutorials/README.md ...
> Generating .............. /code/ skill_tutorials/LICENSE.md ...
> Generating .............. /code/ skill_tutorials/CMakeLists.txt ...
> Generating .............. /code/ skill_tutorials/.gitignore ...
> skill_tutorials package created.

Create a new ArmarX Skill

A skill in ArmarX involves a skill library as well as a skill provider.

In case you wonder what the purpose of all these different things is, and why it is not included all-in-one:

  • On a structural level:
    • The skill framework in ArmarX is a way of implementing functionalities, so that they are easy to use in other code and via a GUI. It is the abstract overall name for skill-related things in ArmarX, and can be seen as an interface and a common way to structure code.
    • A specific skill is a certain functionality, e.g., making the robot say hello. In the "skill manager GUI", available skills are listed. A "skill" is also a term on the implementation level, see below.
  • On an implementation level:
    • It might be that we want several instances of a skill to run in parallel. In some cases, they may re-use the other instances' dependencies, or in contrary, may need to be configured in a way that they do not interfer. Thus, we do not instantiate skills directly by calling their constructor, but use a skill provider for creating skill instances (like a factory). This skill provider is an ArmarX component. It should do as few things as possible, i.e., be limited to creating the skills.
    • The term skill library refers to organizing C++ code in libraries - it is the C++ library that belongs to the skill implementation. You might think of it as a subfolder or built target, defined to improve the code structure.
    • Finally, a skill is a class that contains the code to provide your functionality in the manner of the skill framework. It is usually
      • located within a skill library and
      • registered in a skill provider and
      • mentioned (e.g., regarding its name) in certain code files
    • The actual logic of your functionality is preferably not implemented in the skill itself, but in its core. The core should be independent of the skill framework, and rather be regular objects and functions that use plain C++ (and ArmarX) types. This makes it easier to re-use your functionality also outside the skill framework, and to adapt it when there are changes of the skill framework itself.

Let's first create a new skill librarywith the name say_hello, to have a location to implement your skill:

# Change directory:
cd skill_tutorials
# Create a new skill library:
armarx-package add skill_library say_hello

The output should look something like this:

> Updating................. /code/ skill_tutorials/source/ skill_tutorials/CMakeLists.txt ...
> Creating directory ...... /code/ skill_tutorials/source/ skill_tutorials/say_hello ...
> Creating directory ...... /code/ skill_tutorials/source/ skill_tutorials/say_hello/test ...
> Generating .............. /code/ skill_tutorials/source/ skill_tutorials/say_hello/test/say_hello_test.cpp ...
> Creating directory ...... /code/ skill_tutorials/source/ skill_tutorials/say_hello/aron ...
> Generating .............. /code/ skill_tutorials/source/ skill_tutorials/say_hello/aron/SayHelloParams.xml ...
> Creating directory ...... /code/ skill_tutorials/source/ skill_tutorials/say_hello/constants ...
> Generating .............. /code/ skill_tutorials/source/ skill_tutorials/say_hello/constants/CMakeLists.txt ...
> Generating .............. /code/ skill_tutorials/source/ skill_tutorials/say_hello/constants/constants.h ...
> Generating .............. /code/ skill_tutorials/source/ skill_tutorials/say_hello/constants/constants.cpp ...
> Creating directory ...... /code/ skill_tutorials/source/ skill_tutorials/say_hello/core ...
> Generating .............. /code/ skill_tutorials/source/ skill_tutorials/say_hello/core/CMakeLists.txt ...
> Generating .............. /code/ skill_tutorials/source/ skill_tutorials/say_hello/core/SayHello.h ...
> Generating .............. /code/ skill_tutorials/source/ skill_tutorials/say_hello/core/SayHello.cpp ...
> Creating directory ...... /code/ skill_tutorials/source/ skill_tutorials/say_hello/skills ...
> Generating .............. /code/ skill_tutorials/source/ skill_tutorials/say_hello/skills/CMakeLists.txt ...
> Generating .............. /code/ skill_tutorials/source/ skill_tutorials/say_hello/skills/SayHello.h ...
> Generating .............. /code/ skill_tutorials/source/ skill_tutorials/say_hello/skills/SayHello.cpp ...
> Generating ..........

Take a look at everything that has been created in the source folder.

# Take a look at what has been created
cd ./source/skill_tutorials/say_hello
ls

There is a new folder named say_hello. If you open it, you can see 5 subfolders: aron, constants, core, skills, test.

  • In the aron folder you find a file SayHelloParams.xml. In this file, the set of parameters used by your skill is defined as an aron types. Representing these data by an aron type allows to easily transmit them over the network, e.g., from a GUI to the skill provider.
  • In the folder constants, all the constants used in the skill are defined.
  • In core, most of the functionality should be implemented. Implementing your code in a plain C++ library, rather than inside the skill itself, makes it easier to re-use it in other parts of ArmarX, or potentially even outside of it.
  • In the skills folder you can write all the code that is needed to connect the skill with the skill_provider.
  • In the test folder, code tests can be added.

Next we create the skill_provider, which is implemented as a component, and that can be used to call the skill from the ArmarX GUI.

cd .. # Go to the root directory of skill_tutorials.
# Create a new skill provider:
armarx-package add skill_provider say_hello

The output should look something like this:

> Creating directory ...... /code/ skill_tutorials/source/ skill_tutorials/components ...
> Generating .............. /code/ skill_tutorials/source/ skill_tutorials/components/CMakeLists.txt ...
> Creating directory ...... /code/ skill_tutorials/source/ skill_tutorials/components/say_hello_skill_provider ...
> Generating .............. /code/ skill_tutorials/source/ skill_tutorials/components/say_hello_skill_provider/./Component.cpp ...
> Generating .............. /code/ skill_tutorials/source/ skill_tutorials/components/say_hello_skill_provider/./ComponentInterface.ice ...
> Generating .............. /code/ skill_tutorials/source/ skill_tutorials/components/say_hello_skill_provider/./Component.h ...
> Updating................. /code/ skill_tutorials/source/ skill_tutorials/components/say_hello_skill_provider/./Component.cpp ...
> Generating .............. /code/ skill_tutorials/source/ skill_tutorials/components/say_hello_skill_provider/CMakeLists.txt ...
> Updating cmake .......... /code/ skill_tutorials/source/ skill_tutorials/components/CMakeLists.txt ...
> Updating cmake .......... /code/ skill_tutorials/source/ skill_tutorials/CMakeLists.txt ...
> say_hello skill_provider element created.

Let's also take a look at what has been created here:

# Take a look at what has been created
cd ./source/skill_tutorials/components
ls

In the components folder there is a single folder say_hello_skill_provider. Inside of that you find all your normal component files. If you dont remember what these are all for you can take a look at the previous tutorial Create a "Hello World!" Component in ArmarX (C++) . But basically here you just add the skill and later maybe define a remote GUI.

Hello World

Now you should open the package in your IDE. We will be using QtCreator, but you can use any IDE you want.

Go back to your package's root directory and start QTCreator from the terminal:

# Go back to the package's root directory
cd ~/code/skill_tut/tutorial_package
# Start QTCreator
qtcreator

When QTCreator is ready, select 'Open Project' and choose Cmakelist.txt to open the project:

open_project_in_qtcreator

Afterwards, choose the following configurations as the project's configuration and click Configure Project to continue:

>**Note**: At the lap pc's the configuration kit you need to use is called Axii's Kit

project_configuration

You should now see the following:

qtcreator_interface

>**Note:** In case the project tree doesn't show the files, go to Projects and select re-configure with initial parameters:

reconfiger_with_init_params

Firstly, open the top level CMakeList.txt file and uncomment the following line.

CMakeLists.txt :

armarx_find_package(PUBLIC RobotAPI REQUIRED)

This is because the skill framework depends on code which is part of the RobotAPI built target.

Next, we want to build the project once so all the autogenerated aron files will be created. But before we can actually built the project though, we need to do two things: tell Cmake that we've created a new skill library and make this library known to our skill provider.

Therfore go to the CMakeList.txt file of the say_hello_skill_provider and add skill_tutorials::say_hello_skills to the dependencies:

Cmakelist.txt:

armarx_add_component(say_hello_skill_provider
ICE_FILES
ComponentInterface.ice
ICE_DEPENDENCIES
ArmarXCoreInterfaces
RobotAPIInterfaces
SOURCES
Component.cpp
HEADERS
Component.h
DEPENDENCIES
# ArmarXCore
ArmarXCore
# skill_tutorials
skill_tutorials::say_hello_skills
)

To make the skill library known to the skill provider we need to include it in the source file Component.ccp of the say_hello_skill_provider. You'll find it here:

Where to find Component.cpp

Now add the following to Component.cpp:

#include <skill_tutorials/say_hello/skills/SayHello.h>

Component.cpp:

#include "Component.h"
// Include headers you only need in function definitions in the .cpp.
// #include <Eigen/Core>
// #include <SimoxUtility/color/Color.h>
// Skill libraries
// CHANGE THIS TO YOUR SKILL LIBRARY
#include <skill_tutorials/say_hello/skills/SayHello.h>
namespace skill_tutorials::components::say_hello::skill_provider
{
const std::string
Component::defaultName = "say_hello_skill_provider";
Component::createPropertyDefinitions()
{
.
.
.

You can build the project now by clicking on the hammer symbol in the left bottom corner or simply press CTRL + B

Make the skill do something

Now we want to have our skill do something. As mentioned before, the functionality itself goes into the core. So let's open the cpp file there and add a print "Hello World!", using the built-in ArmarX logger.

>**Note**: In ArmarX, when we want to print something, we use the build in ArmarX logger, not the c++ cout function of the iostream library. This is because the ArmarX GUI provides a log viewer, printing the information directly to the GUI instead of an additional terminal.

In case you wonder why there is both an execute() and an _execute() method: Externally, we want to call an execute() method; i.e., the name should be written as expected. However, before we actually start the execution, we need to do some auxiliary things (here: locking the execution mutex to prevent threads running parallel from changeing an object in the memory at the same time, causing confusing behavior). As we do not want these auxiliary actions to clutter our actual implementation, we move the latter into a second method. This can be private, and we differentiate it from the first one by prefixing it with an underscore - i.e., we call it _execute(). The execute() method is now basically a wrapper around _execute() and cares about all auxiliary things that need to be done.

Where to find SayHello.cpp

core/SayHello.cpp :

#include "SayHello.h"
namespace skill_tutorials::say_hello::core
{
SayHello::SayHello(const Remote& r, const Properties& p, const Subskills& s) :
remote(r), properties(p), subskills(s)
{
}
{
std::scoped_lock l(this->executionMutex);
return this->_execute();
}
bool SayHello::_execute()
{
ARMARX_INFO << "Hello World";
return true;
}
}

After adding everything to your code, build your project again (press CTRL+B). Okay, great! Then let's see if we can start the skill in the ArmarXGUI.

Create a new Scenario

Firstly, you need to make the path to the build directory of your package known to ArmarX. For that, append the following lines to the armarx-workspace.rc file, which you can find in the top level directory of your current workspace. Open your files and navigate to your workspace directory and open armarx-workspace.rc and simply add:

armarx-workspace.rc:

# Additional
_axii_env_set skill_tutorials_DIR "$HOME/path/to/package/build" # (e.g. "$HOME/local_projects/my_repository/build", should be outside any Axii workspace)

Now start ArmarX and open a GUI, both from the terminal.

armarx start
armarx gui

What you are now seeing on the GUI is the LogViewr, where we can see all the different messages logged by the system. Next, open the ScenarioManager by clicking on the ScenarioManager icon or simply search for it in the Widget Search.

Open the ScenarioManager

Open your package (skill_tutorials) by clicking on Open Scenario followed by Open Package and entering the package name.

Open the package

Then, create a new scenario, give it a name, and select the correct package.

Create the Scenario

Any skill needs the SkillMemory, MemoryNameSystem and the RemoteGUIProvider to be able to run. So add all these apps to your Scenario by clicking on Edit mode and drag the needed apps into the scenario.

Adding the apps

Start the scenario by clicking on the blue play button of your SayHello scenario.

Scenario with all Dependencies

Now open the Skills.Manager. To do so, you can search it in the Widget Search. As the proxy, SkillMemory should already be selected.

Open Skills.Manager

You also may want to open the Meta.LogViewer next to it, so that you can see both. Simply go to the Meta.LogViewer tap, grab the window and drag it to the left side of the Gui.

If you click update on the top left of the Skills.Manager, you should see your skill_provider appear. If you expand it, you also see the skill say_hello. In case you implement multiple skills in the library they will all appear there.

Update Skills.Manager

Now is the time to say hello! You can click on the skill and request the execution. Take a look at the LogViewer; you should see the message you have sent. Congratulations!

Hello World!
execute
Use of this software is granted under one of the following two to be chosen freely by the user Boost Software License Version Marcin Kalicinski Permission is hereby free of to any person or organization obtaining a copy of the software and accompanying documentation covered by this execute
Definition: license.txt:12
RobotAPI
This file is part of ArmarX.
armarx_add_component
armarx_add_component(COMPONENT_LIBS ArmarXCoreInterfaces ArmarXCore ${Boost_LIBRARIES} SOURCES ExternalApplicationManager.cpp ExternalApplicationManagerStarter.cpp ExternalApplicationManagerDependency.cpp HEADERS ExternalApplicationManager.h ExternalApplicationManagerStarter.h ExternalApplicationManagerDependency.h) target_include_directories(ExternalApplicationManager PUBLIC $
Definition: CMakeLists.txt:11
Decoupled.h
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:181
IceUtil::Handle< class PropertyDefinitionContainer >
Logging.h
armarx::ctrlutil::s
double s(double t, double s0, double v0, double a0, double j)
Definition: CtrlUtil.h:33