RcsPySim
A robot control and simulation library
DynamicalSystem.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 
31 #include "DynamicalSystem.h"
32 #include "../config/PropertySource.h"
33 #include "../util/eigen_matnd.h"
34 
35 #include <Rcs_macros.h>
36 
37 #include <memory>
38 #include <iostream>
39 
40 
41 namespace Rcs
42 {
43 
44 /*! Get an Eigen matrix value from a config entry.
45  *
46  * Converts the config value to a MatNd, checks the dimensions, and copies the contents into the output reference.
47  *
48  * @param[out] out storage for read value.
49  * @param[in] ps Property source to load from.
50  * @param[in] property property name
51  * @param[in] expectedRows Required row count.
52  * @param[in] expectedCols Required column count.
53  *
54  * @throws std::invalid_argument if the matrix dimensions do not match.
55  */
56 static bool getMatrixProperty(
57  Eigen::MatrixXd& out, PropertySource* ps, const char* property, unsigned int expectedRows,
58  unsigned int expectedCols)
59 {
60  MatNd* mat;
61  if (!ps->getProperty(mat, property)) {
62  return false;
63  }
64  if (mat->m != expectedRows || mat->n != expectedCols) {
65  if (mat->size == expectedRows*expectedCols) {
66  // we got a flattened matrix because I have no good idea how to do this in xml
67  mat->m = expectedRows;
68  mat->n = expectedCols;
69  }
70  else {
71  // dimension mismatch
72  MatNd_destroy(mat);
73  std::ostringstream os;
74  os << "matrix dimension mismatch for " << property;
75  os << ": expected " << expectedRows << "x" << expectedCols << " but got " << mat->m << "x" << mat->n;
76  throw std::invalid_argument(os.str());
77  }
78  }
79  copyMatNd2Eigen(out, mat);
80  MatNd_destroy(mat);
81  return true;
82 }
83 
84 /*! Get an Eigen vector value from a config entry.
85  *
86  * Converts the config value to a MatNd, checks the dimensions, and copies the contents into the output reference.
87  *
88  * @param[out] out storage for read value.
89  * @param[in] ps Property source to load from.
90  * @param[in] property property name
91  * @param[in] expectedSize Required element count.
92  *
93  * @throws std::invalid_argument if the matrix dimensions do not match.
94  */
95 static bool getVectorProperty(Eigen::VectorXd& out, PropertySource* ps, const char* property, unsigned int expectedSize)
96 {
97  MatNd* mat;
98  if (!ps->getProperty(mat, property)) {
99  return false;
100  }
101  if (mat->m != expectedSize || mat->n != 1) {
102  MatNd_destroy(mat);
103  std::ostringstream os;
104  os << "vector dimension mismatch for " << property;
105  os << ": expected " << expectedSize << "(x1)" << " but got " << mat->m << "x" << mat->n;
106  throw std::invalid_argument(os.str());
107  }
108  copyMatNd2Eigen(out, mat);
109  MatNd_destroy(mat);
110  return true;
111 }
112 
113 // The factory
114 DynamicalSystem* DynamicalSystem::create(PropertySource* properties, unsigned int innerTaskDim)
115 {
116  // Obtain function type and goal
117  std::string functionName;
118  if (!properties->getProperty(functionName, "function")) {
119  throw std::invalid_argument("Missing function specification for the DynamicalSystem!");
120  }
121 
122  // Create a unique pointer for the dynamical systems (will be reset later)
123  std::unique_ptr<DynamicalSystem> ds;
124 
125  if (functionName == "const") {
126  Eigen::VectorXd constVelocity;
127  if (!getVectorProperty(constVelocity, properties, "DSVelocity", innerTaskDim)) {
128  throw std::invalid_argument("Missing constVelocity argument for DSVelocity dynamical system!");
129  }
130  ds.reset(new DSConst(constVelocity));
131  }
132  else if (functionName == "lin") {
133  Eigen::MatrixXd errorDynamics;
134  if (!getMatrixProperty(errorDynamics, properties, "errorDynamics", innerTaskDim, innerTaskDim)) {
135  throw std::invalid_argument("Missing errorDynamics argument for DSLinear dynamical system!");
136  }
137  ds.reset(new DSLinear(errorDynamics, Eigen::VectorXd::Zero(innerTaskDim)));
138  }
139  else if (functionName == "msd" or functionName == "msd_nlin") {
140  double attractorStiffness = 1;
141  properties->getProperty(attractorStiffness, "attractorStiffness");
142 
143  double damping = 0;
144  properties->getProperty(damping, "damping");
145 
146  double mass = 1;
147  properties->getProperty(mass, "mass");
148 
149  // Convert repeller list
150  std::vector<DSMassSpringDamper::Spring> repellers;
151  auto& repSpec = properties->getChildList("repellers");
152  for (auto rep:repSpec) {
153  Eigen::VectorXd zp;
154  if (!getVectorProperty(zp, rep, "pos", innerTaskDim)) {
155  throw std::invalid_argument("Missing position for repeller for DSMassSpringDamper dynamical system!");
156  }
157  double s = 1;
158  rep->getProperty(s, "stiffness");
159  repellers.emplace_back(zp, s);
160  }
161 
162  if (functionName == "msd") {
163  ds.reset(new DSMassSpringDamper(
164  DSMassSpringDamper::Spring(Eigen::VectorXd::Zero(innerTaskDim), attractorStiffness), repellers, damping,
165  mass));
166  }
167  else if (functionName == "msd_nlin") {
168  ds.reset(new DSMassSpringDamperNonlinear(
169  DSMassSpringDamper::Spring(Eigen::VectorXd::Zero(innerTaskDim), attractorStiffness), repellers, damping,
170  mass));
171  }
172  }
173  else {
174  std::ostringstream os;
175  os << "Unsupported task function name: " << functionName;
176  throw std::invalid_argument(os.str());
177  }
178 
179  // Set the goal if specified (common for all DS except DSConst)
180  Eigen::VectorXd goal;
181  if (getVectorProperty(goal, properties, "goal", innerTaskDim)) {
182  ds->setGoal(goal);
183  }
184 
185  return ds.release();
186 }
187 
188 double DynamicalSystem::goalDistance(const Eigen::VectorXd& x_curr) const
189 {
190  return (getGoal() - x_curr).norm(); // L2 norm
191 }
192 
193 unsigned int DynamicalSystem::getStateDim() const
194 {
195  return x_dot_des.rows();
196 }
197 
198 
199 /**
200  * DS2ndOrder
201  */
202 void DSSecondOrder::step(Eigen::VectorXd& x_dot, const Eigen::VectorXd& x, double dt) const
203 {
204  // Initialize x_ddot as empty matrix
205  Eigen::VectorXd x_ddot;
206 
207  // Compute x_ddot from dynamics
208  step(x_ddot, x_dot, x, dt);
209 
210  // Integrate x_ddot to get x_dot
211  x_dot += x_ddot*dt;
212 }
213 
214 
215 /**
216  * DSConst
217  */
218 DSConst::DSConst(const Eigen::VectorXd& constVelocity) : constVelocity(constVelocity) {}
219 
221 {
222  return new DSConst(constVelocity);
223 }
224 
225 void DSConst::step(Eigen::VectorXd& x_dot, const Eigen::VectorXd& x, double dt) const
226 {
227  x_dot = constVelocity;
228  REXEC(6) {
229  std::cout << "x =" << std::endl << x << std::endl;
230  std::cout << "constVelocity (DSConst) =" << std::endl << constVelocity << std::endl;
231  }
232 }
233 
234 Eigen::VectorXd DSConst::getGoal() const
235 {
236  return constVelocity;
237 }
238 
239 void DSConst::setGoal(const Eigen::VectorXd& x_des)
240 {
241  constVelocity = x_des;
242 }
243 
244 
245 /**
246  * DSLinear
247  */
248 DSLinear::DSLinear(const Eigen::MatrixXd& errorDynamics, const Eigen::VectorXd& equilibriumPoint) :
249  errorDynamics(errorDynamics), equilibriumPoint(equilibriumPoint) {}
250 
252 {
254 }
255 
256 void DSLinear::step(Eigen::VectorXd& x_dot, const Eigen::VectorXd& x, double dt) const
257 {
258  x_dot = errorDynamics*(equilibriumPoint - x);
259  REXEC(6) {
260  std::cout << "x =" << std::endl << x << std::endl;
261  std::cout << "diff to goal (DSLinear) =" << std::endl << equilibriumPoint - x << std::endl;
262  }
263 }
264 
265 Eigen::VectorXd DSLinear::getGoal() const
266 {
267  return equilibriumPoint;
268 }
269 
270 void DSLinear::setGoal(const Eigen::VectorXd& x_des)
271 {
272  equilibriumPoint = x_des;
273 }
274 
275 
276 /**
277  * DSMassSpringDamper
278  */
280  const DSMassSpringDamper::Spring& attractor,
281  const std::vector<DSMassSpringDamper::Spring>& repellers,
282  const double damping,
283  const double mass)
284  : attractorSpring(attractor), repellerSprings(repellers), damping(damping), mass(mass) {}
285 
287 {
289 }
290 
292  Eigen::VectorXd& x_ddot, const Eigen::VectorXd& x_dot, const Eigen::VectorXd& x,
293  double dt) const
294 {
295  Eigen::VectorXd sumSprings = Eigen::VectorXd::Zero(x.size()); // sums attractors and repellers
296 
297  // One attractor
298  Eigen::VectorXd diff = attractorSpring.equilibriumPoint - x;
299  double dist = diff.norm() + 1e-3;
300  double stiffness = attractorSpring.stiffness; // stiffness > 0
301 
302  sumSprings += stiffness*dist*diff/dist; // force = stiffness * elongation * normalized_direction
303 
304  // Iterate over all repellers
305  for (auto& spring : repellerSprings) {
306  diff = spring.equilibriumPoint - x;
307  dist = diff.norm() + 1e-3;
308  stiffness = spring.stiffness; // stiffness > 0
309 
310  sumSprings += -stiffness/dist*diff/dist; // force = stiffness / elongation * normalized_direction
311  }
312 
313  // Compute acceleration
314  x_ddot = (sumSprings - damping*x_dot)/mass;
315 
316  // Print if debug level is exceeded
317  REXEC(6) {
318  std::cout << "diff to goal (DSMassSpringDamper) =" << std::endl << diff << std::endl;
319  std::cout << "x_ddot (DSMassSpringDamper) =" << std::endl << x_ddot << std::endl;
320  }
321 }
322 
323 Eigen::VectorXd DSMassSpringDamper::getGoal() const
324 {
326 }
327 
328 void DSMassSpringDamper::setGoal(const Eigen::VectorXd& x_des)
329 {
331 }
332 
333 
334 /**
335  * DSMassSpringDamperNonlinear
336  */
338  const DSMassSpringDamper::Spring& attractor,
339  const std::vector<DSMassSpringDamperNonlinear::Spring>& repellers,
340  const double damping, const double mass)
341  : DSMassSpringDamper::DSMassSpringDamper(attractor, repellers, damping, mass) {}
342 
344 {
346 }
347 
349  Eigen::VectorXd& x_ddot, const Eigen::VectorXd& x_dot, const Eigen::VectorXd& x,
350  double dt) const
351 {
352  Eigen::VectorXd sumSprings = Eigen::VectorXd::Zero(x.size()); // sums attractors and repellers
353 
354  // One attractor
355  Eigen::VectorXd diff = attractorSpring.equilibriumPoint - x;
356  double dist = fmax(diff.norm(), 5e-2); // never go below the force that is caused by a 5cm distance
357  double stiffness = attractorSpring.stiffness; // stiffness > 0
358 
359  sumSprings += stiffness*pow(dist, 0.5)*diff/dist; // force = stiff* sqrt(elong) * norm_dir
360 
361  // Iterate over all repellers
362  for (auto spring : repellerSprings) {
363  diff = spring.equilibriumPoint - x;
364  dist = fmax(diff.norm(), 5e-2); // never go below the force that is caused by a 5cm distance
365  stiffness = spring.stiffness; // stiffness > 0
366 
367  sumSprings += stiffness*pow(dist, 0.5)*diff/dist; // force = stiff * sqrt(elong) * norm_dir
368  }
369 
370  // Compute acceleration
371  x_ddot = (sumSprings - damping*x_dot)/mass;
372 
373  // Print if debug level is exceeded
374  REXEC(6) {
375  std::cout << "diff to goal (DSMassSpringDamperNonlinear) =" << std::endl << diff << std::endl;
376  std::cout << "x_ddot (DSMassSpringDamperNonlinear) =" << std::endl << x_ddot << std::endl;
377  }
378 }
379 
380 
381 /**
382  * DSSlice
383  */
384 DSSlice::DSSlice(DynamicalSystem* wrapped, unsigned int offset, unsigned int length) : wrapped(wrapped), offset(offset),
385  length(length) {}
386 
388 {
389  delete wrapped;
390 }
391 
392 
394 {
395  return new DSSlice(wrapped->clone(), offset, length);
396 }
397 
398 void DSSlice::step(Eigen::VectorXd& x_dot, const Eigen::VectorXd& x, double dt) const
399 {
400  //select slice of current x_dot
401  Eigen::VectorXd x_dot_s = x_dot.segment(offset, length);
402 
403  // use slice in wrapped
404  wrapped->step(x_dot_s, x.segment(offset, length), dt);
405 
406  // We want all unselected entries of x_dot to be 0. Since there is no
407  // slice complement in eigen, we set the whole vector to 0 and override
408  // the selected slice.
409  x_dot.setZero(x.size());
410  x_dot.segment(offset, length) = x_dot_s;
411 }
412 
413 void DSSlice::setGoal(const Eigen::VectorXd& x_des)
414 {
415  goal = x_des;
416  wrapped->setGoal(x_des.segment(offset, length));
417 }
418 
419 
420 Eigen::VectorXd DSSlice::getGoal() const
421 {
422  return goal;
423 }
424 
425 double DSSlice::goalDistance(const Eigen::VectorXd& x_cur) const
426 {
427  return wrapped->goalDistance(x_cur.segment(offset, length));
428 }
429 
431 {
432  return wrapped;
433 }
434 
435 } /* namespace Rcs */
unsigned int getStateDim() const
virtual bool getProperty(std::string &out, const char *property)=0
unsigned int length
void setGoal(const Eigen::VectorXd &x_des)
void copyMatNd2Eigen(M &dst, const MatNd *src)
Definition: eigen_matnd.h:120
Eigen::VectorXd goal
DSMassSpringDamper(const Spring &attractor, const std::vector< Spring > &repellers, const double damping, const double mass=1.0)
DSSlice(DynamicalSystem *wrapped, unsigned int offset, unsigned int length)
constructor
virtual DynamicalSystem * clone() const =0
void step(Eigen::VectorXd &x_ddot, const Eigen::VectorXd &x_dot, const Eigen::VectorXd &x, double dt) const
DSConst(const Eigen::VectorXd &constVelocity)
static bool getMatrixProperty(Eigen::MatrixXd &out, PropertySource *ps, const char *property, unsigned int expectedRows, unsigned int expectedCols)
virtual double goalDistance(const Eigen::VectorXd &x_cur) const
std::vector< Spring > repellerSprings
repeller springs pushing the mass away from points in space (e.g. obstacles)
void step(Eigen::VectorXd &x_dot, const Eigen::VectorXd &x, double dt) const
virtual DynamicalSystem * clone() const
virtual void step(Eigen::VectorXd &x_dot, const Eigen::VectorXd &x, double dt) const
DSLinear(const Eigen::MatrixXd &errorDynamics, const Eigen::VectorXd &equilibriumPoint)
void setGoal(const Eigen::VectorXd &x_des)
virtual void setGoal(const Eigen::VectorXd &x_des)
virtual void step(Eigen::VectorXd &x_dot, const Eigen::VectorXd &x, double dt) const =0
Eigen::VectorXd equilibriumPoint
Zero position of the spring, force is applied to reach this state.
DynamicalSystem * getWrapped() const
Eigen::VectorXd x_dot_des
virtual DynamicalSystem * clone() const
virtual DynamicalSystem * clone() const
Eigen::MatrixXd errorDynamics
virtual DynamicalSystem * clone() const
virtual DynamicalSystem * clone() const
virtual const std::vector< PropertySource * > & getChildList(const char *prefix)=0
Eigen::VectorXd constVelocity
Eigen::VectorXd getGoal() const
static DynamicalSystem * create(PropertySource *properties, unsigned int innerTaskDim)
virtual void setGoal(const Eigen::VectorXd &x_des)=0
Spring attractorSpring
attractor spring pulling the mass to the goal poisiton (there is only one for every dynamical system)...
virtual Eigen::VectorXd getGoal() const =0
void step(Eigen::VectorXd &x_ddot, const Eigen::VectorXd &x_dot, const Eigen::VectorXd &x, double dt) const
virtual Eigen::VectorXd getGoal() const
Eigen::VectorXd getGoal() const
virtual void step(Eigen::VectorXd &x_dot, const Eigen::VectorXd &x, double dt) const
void step(Eigen::VectorXd &x_dot, const Eigen::VectorXd &x, double dt) const
double stiffness
Stiffness of the spring.
DSMassSpringDamperNonlinear(const Spring &attractor, const std::vector< Spring > &repellers, const double damping, const double mass=1.0)
void setGoal(const Eigen::VectorXd &x_des)
DynamicalSystem * wrapped
Eigen::VectorXd equilibriumPoint
virtual double goalDistance(const Eigen::VectorXd &x_curr) const
unsigned int offset
double damping
daming of the dynamical system (there is only one for every dynamical system)
Eigen::VectorXd getGoal() const
double mass
mass of the particle
static bool getVectorProperty(Eigen::VectorXd &out, PropertySource *ps, const char *property, unsigned int expectedSize)
Properties for one spring component.