Using Parameters of Skills via ARON

Objective: Learn how to pass parameters to your skill (using the Skills.Manager in ArmarX GUI).

Previous Tutorials: Create a Skill

Next Tutorials: Using Subskills

Reference Code: skill_tutorials

Create the parameters

First create a skill as described in the previous tutorial Create a Skill. If you followed along you may also just use the one created earlier. We will use a new skill use_parameters inside the same package.

>**Note** : When creating multiple skills in one packgage with the armarx-package-tool you may have to change the order of the add-subdirectories in the first CMakeList.txt file in your source folder based on the dependencies of your code (e.g. here we need to put add_subdirectory(use_parameters) before add_subdirectory(components))

CMakeList.txt:

add_subdirectory(say_hello)
add_subdirectory(use_parameters)
add_subdirectory(components)

Ok, then let's take a closer look at the aron file that has already been created by armarx-package in use_parameters/aron. You can see it has the typical xml layout, with an element AronTypeDefinition containing an element GenerateTypes containing an element Object with the name ::skill_tutorials::use_parameters::arondto::UseParametersParams.

This [...YourSkillName...]Params aron data type is used to store and transmit the parameters that your skill is to get when being invoked. We use an aron type instead of a plain C++ struct to allow sending it over the network (important when using the skill manager GUI) and to enable calling the skill from other languages as well, for example, from python. To learn more about ARON and the data types available in it, have a look at ArmarX Object Notation (ARON) / Interpretable Data Format (IDF).

Inside the mentioned Object element, there should already be one predefined ObjectChild element. You can add as many ObjectChildren as you want. For all of them, the corresponding (C++-)code will be autogenerated, so that you can access the UseParameterParams's attributes in your code, as well as set their values in the Skills.Manager of an ArmarX GUI.

Let's change the pre-generated parameter and add a second one. Afterwards, this could look as follows:

aron parameters

UseParametersParams.xml :

<?xml version="1.0" encoding="UTF-8" ?>
<AronTypeDefinition>
<GenerateTypes>
<Object name="::skill_tutorials::use_parameters::arondto::UseParametersParams">
<ObjectChild key="stringToPrint">
<string />
</ObjectChild>
<ObjectChild key="numPrints">
<int />
</ObjectChild>
</Object>
</GenerateTypes>
</AronTypeDefinition>

And let's also set the default parameters, which are defined in skills/UseParameters.cpp.

skills/UseParameters.cpp :

#include "UseParameters.h"
#include <skill_tutorials/use_parameters/core/UseParameters.h>
namespace skill_tutorials::use_parameters::skills
{
UseParameters::GetSkillDescription()
{
ParamType defaultParams;
defaultParams.stringToPrint = "world!";
defaultParams.numPrints = 1;
auto skillId = armarx::skills::SkillID{.skillName =skillName};
return ::armarx::skills::SkillDescription{
.skillId = skillId,
.description = "TODO: Description of skill UseParameters.",
.rootProfileDefaults = defaultParams.toAron(),
.parametersType = ParamType::ToAronType()};
}
...
}

Remember to tell Cmake and the skill provider about your library (hint: see last tutorial) and build the project once.

Let's take a look at what this already changes in the GUI. Start ArmarX and the GUI if you haven't already and open the ScenarioManager. Create a new scenario named UseParameters and add the skill provider of your new skill. If you still have your SayHello scenario available, you don't need to include the other applications a second time, just make sure they are running. Afterwards, open the Skills.Manager as in the previous tutorial Create a Skill.

In the Skills.Manager, you can now find the parameters that you have defined and their default values. You can change a parameter's value by double-clicking on it.

First look at parameters

Passing the parameters to the core

Ok, then let's do something with these parameters now! Go back to your editor and open the skills/UseParameters.cpp. If you look at the main function you can see it has an argument named in of type SpecializedMainInput. SpecializedMainInput has an attribute parameters, which corresponds to the aron object of the type you defined in the UseParametersParams.xml. So now we need to take these parameters and get them to our core. A good way of doing this, while ensuring that the core can still be used without unnecessarily many dependencies, is to have all these parameters as properties of the core and then overwrite these with the parameters.

The most readable and re-usable approach is to define a custom Properties struct, of which the individual parameters are plain C++ members.

>**Just as a Note**: An alternative version, which is slightly faster to implement, is to use the arondto as the (only) member of the Properties. To do so, you have to perform the following replacements in the code listings below: In core/UseParameters.h, replace struct Properties { ... }; by struct Properties { arondto::UseParametersParams params; };. In skills/UseParameters.cpp, replace core::UseParameters::Properties p{ ... }; by core::UseParameters::Properties p{ .params = in.parameters };. Nevertheless, we will continue with the plain C++ members.

Creating the properties in the core can look like this:

core/UseParameters.h :

...
class UseParameters
{
public:
struct Remote
{
};
/// Fixed properties of this skill implementation
struct Properties
{
std::string someStringToPrint;
int someNumPrints;
};
struct Subskills
{
};
/// Constructor of this skill implementation
UseParameters(const Remote& r, const Properties& p, const Subskills& s);
bool execute();
protected:
...
private:
...
}

And you can set the properties of the core like this (make sure you are actually inside the correct directory), so that they correspond to the aron data type you just defined:

skills/UseParameters.cpp :

...
UseParameters::MainResult
UseParameters::main(const SpecializedMainInput& in)
{
// Enter main code of the skill here.
core::UseParameters::Remote r;
core::UseParameters::Properties p{
.someStringToPrint = in.parameters.stringToPrint,
.someNumPrints = in.parameters.numPrints
};
core::UseParameters::Subskills s;
core::UseParameters impl(r, p, s);
if (impl.execute())
{
return MakeSucceededResult();
}
return MakeFailedResult();
}
...

To summarize: In skills/UseParameters.cpp we get the parameter values from our gui input (that's what the in.parameters-part is used for) and give the values to the parameters we defined under struct Properties in core/UseParameters.h.

Use the parameters

Ok, now that we have successfully passed through the parameters to our core we can use them to do things. So let's go back to the core and print something.

In this tutorial we want to use the gui input of the parameter numPrints to set the number of iteration of a loop that prints something to the logger.

The code to do so could look like this:

core/UseParameters.cpp :

#include "UseParameters.h"
namespace skill_tutorials::use_parameters::core
{
UseParameters::UseParameters(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 UseParameters::_execute()
{
for (int i = 0; i < properties.someNumPrints; ++i)
ARMARX_INFO << i << " Hello " << properties.someStringToPrint;
return true;
}
}

As mentioned in the previous tutorial, the functionality of the skill is implemented in _execute().

You can now build your project and restart it in the ScenarioManager. If you now click on Request Execution in the Skills.Manager, you should see your message, changing according to how you parameterize the skill execution. Go ahead and change the parameters and see how the messages change.

>**Note**: If you just try printing the same message muliple times, the built-in spam protection might block all but the first message.

Great, you did it!

armarx::control::retrieve_hand::constants::SKILL_NAME
constexpr auto SKILL_NAME
Definition: constants.h:6
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
armarx::skills::SkillID::skillName
std::string skillName
Definition: SkillID.h:41
armarx::skills::SkillDescription
Definition: SkillDescription.h:17
add_subdirectory
add_subdirectory(core) add_subdirectory(provider) add_subdirectory(manager) set(LIB_NAME RobotAPISkills) armarx_component_set_name("$
Definition: CMakeLists.txt:1
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:181
Logging.h
armarx::skills::SkillID
Definition: SkillID.h:14
main
int main(int argc, char *argv[])
Definition: Admin.cpp:45
armarx::ctrlutil::s
double s(double t, double s0, double v0, double a0, double j)
Definition: CtrlUtil.h:33
armarx::core::time::Duration::MilliSeconds
static Duration MilliSeconds(std::int64_t milliSeconds)
Constructs a duration in milliseconds.
Definition: Duration.cpp:48