Writing a problem description
YAML basic structure
The basic structure of a problem description file is comprised by
a stack field specifying a list of tasks for each priority layer. Tasks are declared as labels which are later defined at the bottom of the file.
a constraints field specifying bounds for the underlying optimization. Again, these are declared as textual labels to be defined after.
a task/constraint definition part where each textual label declared before is actually defined
For example, a simple stack for a bimanual, redundant manipulation platform could be:
## stack of tasks definition ##
stack:
- ["LeftArm", "RightArm"]
- ["Postural"]
constraints: ["JointLimits"]
## task and constraint definition ##
JointLimits:
type: "JointLimits"
LeftArm:
type: "Cartesian"
distal_link: "arm1_8"
RightArm:
type: "Cartesian"
distal_link: "arm2_8"
Postural:
type: "Postural"
Task common parameters
All tasks share a list of common parameters, described below.
type
is the literal name of the task type; a list of natively-supported types is available here, whereas this section contains details on how to create custom types
name
is the name of the task, which will be used to interact with the task itself,
both from the programmatic API (C++/Python), and from the ROS api (topics, services, …).
The name
field is optional, the default depending on the task type.
active
is a boolean specifying whether the task should be active right from creation,
or not. It defaults to true
.
lambda
is the value of the feedback gain for position tracking, as a floating point number ranging from 0 to 1. Defaults to 1.0.
lambda2
is the value of the feedback gain for velocity tracking (generally used by second-order controllerd),
as a floating point number ranging from 0 to 1. Defaults to -1, which means that the provided value
shound not be used. A reasonable default is therefore computed based on the value of łambda
.
weight
is used to give different soft priority to the task degrees of freedom, also w.r.t.
other tasks at the same priority level. It must be either a scalar (e.g. weight: 10.0
) or a vector
with as many elements as the task size (e.g. weight: [1., 1., 1., 4., 4., 4.]
for a 6D task). Defaults to
the identity.
indices
extract a subtask from the given vector of indices. For instance, indices: [0, 1, 2]
extracts
the position task from a Cartesian task. Defaults to indices: [0, 1, ..., size-1]
)
disabled_joints
is a vector of joints that should not be used to perform the task
enabled_joints
provides a way to specify the disabled_joints in terms of which joints will be enabled
lib_name
specifies the library with the definition of the custom task type (see this section)
Task definition example
LeftArm:
type: TaskTypeName
name: left_arm
active: true
lambda: 0.1
weight: 10.0
indices: [0, 1, 2]
lib_name: libCartesioTaskTypeName.so
disabled_joints: [j_arm1_7, torso_yaw]
Supported task types
Splitting tasks into subtasks
The indices
field is used to select a subtask from a given task. Sometimes,
the user wants to split a unique “base” class into multiple subtasks, usually
to assign different priorities to them. In this case, it is necessary that, for instance,
sending references to the “base” task will affect all subtasks.
CartesIO offers the Subtask
type in order to address this need, as in the following example:
ComTask:
type: "Com"
weight: 10
lambda: 0.05
ComXY:
type: "Subtask"
task: "ComTask"
indices: [0, 1]
ComZ:
type: "Subtask"
task: "ComTask"
indices: [2]
It is then possible to use the task API to (for instance) enable/disable single subtasks, or change their weight. The same can of course be done on the “base” task. An example using the Python API would be:
com = cli.getTask('ComTask')
com_xy = cli.getTask('ComXY')
com_z = cli.getTask('ComZ')
com_z.setActivationState(pyci.ActivationState.Disabled) # disable only z dof
com_xy.setWeight(np.eye(2) * 100.0) # set weight only for xy
Specifying tasks as constraints
It is possible to turn tasks into constraints by simply listing them inside the
constraints
field.