Adding a new task/constraint type

(1) Define an abstract interface

First, we will just define the programmatic API of our task. This is done by defining a pure abstract class with no constructor or implementation, just pure virtual methods. This class will inherit from TaskDescription in order to be recognised as a task, or from ConstraintDescription if it’s a constraint.

#ifndef ANGULARMOMENTUM_H
#define ANGULARMOMENTUM_H

#include <cartesian_interface/sdk/problem/Task.h>

namespace XBot { namespace Cartesian {

/**
 * @brief The AngularMomentum class models the abstract
 * interface for the task.
 *
 * NOTE: it is mandatory to inherit **virtually** from TaskDescription
 */
class AngularMomentum : public virtual TaskDescription
{

public:

    CARTESIO_DECLARE_SMART_PTR(AngularMomentum)

    /* Define the task API (pure virtual methods) */
    virtual Eigen::Vector3d getReference() const = 0;
    virtual void setReference(const Eigen::Vector3d& lref) = 0;

};

(2) Declare an implementation class

/**
 * @brief The AngularMomentumImpl class implements the abstract
 * interface
 */
class AngularMomentumImpl : public TaskDescriptionImpl,
                            public virtual AngularMomentum
{

public:

    CARTESIO_DECLARE_SMART_PTR(AngularMomentumImpl)

    /* The task implementation constructor signature must be
     * as follows */
    AngularMomentumImpl(YAML::Node task_node,
                        Context::ConstPtr context);

    /* Implement the task API */
    Eigen::Vector3d getReference() const override;
    void setReference(const Eigen::Vector3d& lref) override;

    /* Customize update, reset and log */
    void update(double time, double period) override;
    void reset() override;
    void log(MatLogger2::Ptr logger, bool init_logger, int buf_size) override;

private:

    static constexpr double REF_TTL = 1.0;

    Eigen::Vector3d _lref;
    double _ref_timeout;
};

} }

#endif // ANGULARMOMENTUM_H

(3) Implement all methods

The constructor signature must match the following one: every task is constructed by CartesIO in a uniform way, with the relevant YAML node from the problem description file, and a const share pointer to a context object. You must call the TaskDescriptionImpl constructor additionally specifying

  • the task name (a specific name, different for all instances, e.g. “visual_servo_left_hand”)

  • the task size (this will set the weight and indices size)

The constructor is also the place where you parse custom parameters from the given YAML. The base class will do the same jobs for all common Task parameters.

#include "AngularMomentum.h"

#include "fmt/format.h"

using namespace XBot::Cartesian;


AngularMomentumImpl::AngularMomentumImpl(YAML::Node task_node,
                                         Context::ConstPtr context):
    TaskDescriptionImpl(task_node,
                        context,
                        "AngularMomentum",
                        3),
    _lref(0,0,0),
    _ref_timeout(-1)
{
    /* Here you can parse custom YAML fields from task_node */

}

In this example, we implement a reference timeout as reference management logic. This way, if the user stops providing references, the desired value drops to zero automatically.

Eigen::Vector3d AngularMomentumImpl::getReference() const
{
    return _lref;
}

void AngularMomentumImpl::setReference(const Eigen::Vector3d& lref)
{
    _lref = lref;
    _ref_timeout = getTime() + REF_TTL;
}

Note

All methods which are overridden from the base class TaskDescriptionImpl must call the base class method!

void XBot::Cartesian::AngularMomentumImpl::update(double time, double period)
{
    // call base class
    TaskDescriptionImpl::update(time, period);

    // if the last reference has expired, the set it to zero
    if(time > _ref_timeout) _lref.setZero();

}

void XBot::Cartesian::AngularMomentumImpl::reset()
{
    // call base class
    TaskDescriptionImpl::reset();

    _lref.setZero();
}

void XBot::Cartesian::AngularMomentumImpl::log(MatLogger2::Ptr logger,
                                               bool init_logger,
                                               int buf_size)
{
    // call base class
    TaskDescriptionImpl::log(logger, init_logger, buf_size);

    if(init_logger)
    {
        logger->create(getName() + "_ref", 3, 1, buf_size);
        return;
    }

    logger->add(getName() + "_ref", _lref);
}

(4) Register the plugin

CARTESIO_REGISTER_TASK_PLUGIN(AngularMomentumImpl, AngularMomentum)

Note

All *REGISTER* macros take as second argument the task name that will be specified by users inside the type yaml field. In this case, the OpenSot adapter will correspond to a task with type: AngularMomentum