RcsPySim
A robot control and simulation library
AMIKControllerActivation.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright (c) 2020, Fabio Muratore, Honda Research Institute Europe GmbH, and
3  Technical University of Darmstadt.
4  All rights reserved.
5 
6  Redistribution and use in source and binary forms, with or without
7  modification, are permitted provided that the following conditions are met:
8  1. Redistributions of source code must retain the above copyright
9  notice, this list of conditions and the following disclaimer.
10  2. Redistributions in binary form must reproduce the above copyright
11  notice, this list of conditions and the following disclaimer in the
12  documentation and/or other materials provided with the distribution.
13  3. Neither the name of Fabio Muratore, Honda Research Institute Europe GmbH,
14  or Technical University of Darmstadt, nor the names of its contributors may
15  be used to endorse or promote products derived from this software without
16  specific prior written permission.
17 
18  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  DISCLAIMED. IN NO EVENT SHALL FABIO MURATORE, HONDA RESEARCH INSTITUTE EUROPE GMBH,
22  OR TECHNICAL UNIVERSITY OF DARMSTADT BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
26  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  POSSIBILITY OF SUCH DAMAGE.
29 *******************************************************************************/
30 
32 #include "ActionModelIK.h"
33 
34 #include <Rcs_macros.h>
35 #include <Rcs_basicMath.h>
36 
37 #include <sstream>
38 
39 namespace Rcs
40 {
41 
42 void MatNd_fabsClipEleSelf(MatNd* self, double negUpperBound, double posLowerBound)
43 {
44  unsigned int i, mn = self->m*self->n;
45  for (i = 0; i < mn; i++) {
46  if (self->ele[i] < 0){
47  self->ele[i] = std::min(self->ele[i], negUpperBound);
48  }
49  else if (self->ele[i] > 0){
50  self->ele[i] = std::max(self->ele[i], posLowerBound);
51  }
52  else{
53  self->ele[i] = posLowerBound; // arbitrary choice
54  }
55  }
56 }
57 
58 MatNd* MatNd_signs(const MatNd* src)
59 {
60  MatNd* signs = MatNd_createLike(src);
61  for (unsigned int i = 0; i < src->m; i++) {
62  for (unsigned int j = 0; j < src->n; j++) {
63  double val = (MatNd_get(src, i, j) >= 0) ? 1.0 : -1.0;
64  MatNd_set(signs, i, j, val);
65  }
66  }
67  return signs;
68 }
69 
70 
72  AMIKGeneric(graph), taskCombinationMethod(tcm)
73 {
74  this->x_des = MatNd_clone(dx_des); // dx_des comes from AMIKGeneric
75  activation = MatNd_create((unsigned int) getController()->getNumberOfTasks(), 1);
77 }
78 
80 {
81  MatNd_destroy(x_des);
82  MatNd_destroy(activation);
83 }
84 
86 {
87  dimAlwaysActiveTasks += task->getDim();
88  this->addTask(task);
89 }
90 
92 {
93  return (unsigned int) getController()->getNumberOfTasks() - dimAlwaysActiveTasks;
94 }
95 
96 void AMIKControllerActivation::getMinMax(double* min, double* max) const
97 {
98  // All activations are between 0 and 1
99  for (unsigned int i = 0; i < getDim(); i++) {
100  min[i] = -1;
101  max[i] = 1;
102  }
103 }
104 
105 std::vector<std::string> AMIKControllerActivation::getNames() const
106 {
107  std::vector<std::string> names;
108  for (unsigned int i = 0; i < getDim(); ++i) {
109  names.push_back("a_" + std::to_string(i));
110  }
111  return names;
112 }
113 
115 {
117 
118  activation = MatNd_create((unsigned int) getController()->getNumberOfTasks(), 1);
119 }
120 
122  MatNd* q_des, MatNd* q_dot_des, MatNd* T_des, const MatNd* action, double dt)
123 {
124  RCHECK(action->n == 1); // actions are column vectors
125 
126  // Copy the ExperimentConfig graph which has been updated by the physics simulation into the desired graph
127  RcsGraph_copyRigidBodyDofs(desiredGraph->q, graph, nullptr);
128 
129  // Combine the individual activations of every controller task
130  MatNd* action_tmp; // working copy
131  MatNd* action_abs; // workaround for negative actions
132 // MatNd* action_signs; // workaround for negative actions
133  action_tmp = MatNd_clone(action);
134  action_abs = MatNd_clone(action);
135  MatNd_fabsEleSelf(action_abs);
136 // action_signs = MatNd_signs(action);
137  switch (taskCombinationMethod) {
139  break; // no weighting
140 
142  MatNd_constMulSelf(action_tmp, 1./MatNd_sumEle(action_abs));
143  break;
144  }
145 
147  MatNd_softMax(action_tmp, action, action->m); // action->m is a neat heuristic for beta
148  break;
149  }
150 
152  for (unsigned int i = 0; i < action->m; i++) {
153  // Create temp matrix
154  MatNd* otherActions; // other actions are all actions without the current
155  MatNd_clone2(otherActions, action);
156  MatNd_deleteRow(otherActions, i);
157 
158  // Treat the actions as probability of events and compute the probability that all other actions are false
159  double prod = 1; // 1 is the neutral element for multiplication
160  for (unsigned int idx = 0; idx < otherActions->m; idx++) {
161  REXEC(7) {
162  std::cout << "factor " << (1 - otherActions->ele[idx]) << std::endl;
163  }
164  // One part of the product is always 1
165  prod *= (1 - otherActions->ele[idx]);
166  }
167  MatNd_destroy(otherActions);
168 
169  REXEC(7) {
170  std::cout << "prod " << prod << std::endl;
171  }
172 
173  MatNd_set(action_tmp, i, 0, action->ele[i]*prod);
174  }
175 
176  break;
177  }
178  }
179 
180  // Stabilize actions. If an action is exactly zero, computeDX will discard that task, leading to a shape error.
181  if (MatNd_minEle(action_abs) < 1e-6) {
182  MatNd_fabsClipEleSelf(action_tmp, -1e6, 1e6);
183  REXEC(5) {
184  std::cout << "Clipped the activations to ]-inf, -1e-6] u [1e-6, inf[" << std::endl;
185  }
186  }
187 
188  // Fill the first rows with the variable activations and the remaining rows with ones for the always active tasks
189  MatNd_copyRows(activation, 0, action_tmp, 0, action_tmp->m);
190  for (unsigned int i = action_tmp->m; i < activation->m; i++) {
191  activation->ele[i] = 1.;
192  }
193  MatNd_destroy(action_tmp);
194 
195  // Compute the differences in task space and weight them
196  getController()->computeDX(dx_des, x_des, activation);
197 
198  // Compute IK from dx_des
199  ActionModelIK::ikFromDX(q_des, q_dot_des, dt);
200 
201  // Print if debug level is exceeded
202  REXEC(7) {
203  MatNd_printComment("q_des", q_des);
204  MatNd_printComment("q_dot_des", q_dot_des);
205  if (T_des) {
206  MatNd_printComment("T_des", T_des);
207  }
208  }
209 }
210 
212 {
213  // All zero activations is stable
214  MatNd_setZero(action);
215 }
216 
218 {
220  if (tcmName == "sum") {
222  }
223  else if (tcmName == "mean") {
225  }
226  else if (tcmName == "softmax") {
228  }
229  else if (tcmName == "product") {
231  }
232  else {
233  std::ostringstream os;
234  os << "Unsupported task combination method: " << tcmName << "! Supported methods: sum, mean, softmax, product.";
235  throw std::invalid_argument(os.str());
236  }
237  return tcm;
238 }
239 
241 {
243  return "sum";
244  }
246  return "mean";
247  }
249  return "softmax";
250  }
252  return "product";
253  }
254  else {
255  return nullptr;
256  }
257 }
258 
260 {
261  return activation;
262 }
263 
265 {
266  return x_des;
267 }
268 
270 {
271  // Creating a new x_des and shallow copying saves us from the unexpected size error that clone yields
272  this->x_des = MatNd_createLike(x_des);
273  MatNd_copy(this->x_des, x_des);
274 }
275 
276 void AMIKControllerActivation::setXdesFromTaskSpec(std::vector<PropertySource*>& taskSpec)
277 {
278  MatNd* x_des = MatNd_create(1, 1); // dummy row necessary for MatNd_appendRows
279 
280  if (taskSpec.size() != getController()->getNumberOfTasks()) {
281  std::ostringstream os;
282  os << "Received " << taskSpec.size() << " elements in taskSpec, but there are "
283  << getController()->getNumberOfTasks() << " Rcs controller tasks!";
284  throw std::runtime_error(os.str());
285  }
286 
287  for (PropertySource* ts : taskSpec) {
288  MatNd* x_des_temp = nullptr;
289  if (!ts->getProperty(x_des_temp, "x_des")) {
290  throw std::invalid_argument("Field x_des is missing for at least one task specification!");
291  }
292  MatNd_appendRows(x_des, x_des_temp);
293  }
294  MatNd_deleteRows(x_des, 0, 0); // delete dummy row
295  this->setXdes(x_des);
296  MatNd_destroy(x_des);
297 }
298 
299 } /* namespace Rcs */
MatNd * MatNd_signs(const MatNd *src)
MatNd * x_des
The goal in task space.
unsigned int dimAlwaysActiveTasks
Store the cumulative number of task dimensions which are always active.
MatNd * activation
The activation resulting from the action and the task combination method (used for logging) ...
void addTask(Task *task)
const char * getTaskCombinationMethodName() const
RcsGraph * desiredGraph
TaskCombinationMethod
Definition: ActionModel.h:46
virtual std::vector< std::string > getNames() const
virtual unsigned int getDim() const
Get the number of tasks multiplied by their individual dimension, owned by the action model...
virtual void getStableAction(MatNd *action) const
static TaskCombinationMethod checkTaskCombinationMethod(std::string tcmName)
virtual void computeCommand(MatNd *q_des, MatNd *q_dot_des, MatNd *T_des, const MatNd *action, double dt)
void MatNd_fabsClipEleSelf(MatNd *self, double negUpperBound, double posLowerBound)
void ikFromDX(MatNd *q_des, MatNd *q_dot_des, double dt) const
AMIKControllerActivation(RcsGraph *graph, TaskCombinationMethod tcm)
TaskCombinationMethod taskCombinationMethod
Way to combine the tasks&#39; contribution.
void addAlwaysActiveTask(Task *task)
Add Rcs controller tasks that are always active. Do this after adding the regular tasks...
virtual void getMinMax(double *min, double *max) const
virtual void reset()
const ControllerBase * getController() const
Definition: ActionModelIK.h:76
RcsGraph * graph
Definition: ActionModel.h:194
void setXdesFromTaskSpec(std::vector< PropertySource *> &taskSpec)