RcsPySim
A robot control and simulation library
ECPlanar3Link.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 "ExperimentConfig.h"
32 #include "action/ActionModelIK.h"
40 #include "observation/OMCombined.h"
42 #include "observation/OMPartial.h"
53 #include "physics/ForceDisturber.h"
54 #include "util/string_format.h"
55 
56 #include <Rcs_Mat3d.h>
57 #include <Rcs_Vec3d.h>
58 #include <Rcs_typedef.h>
59 #include <Rcs_macros.h>
60 #include <TaskPosition1D.h>
61 #include <TaskVelocity1D.h>
62 #include <TaskDistance.h>
63 
64 #ifdef GRAPHICS_AVAILABLE
65 
66 #include <RcsViewer.h>
67 
68 #endif
69 
70 #include <sstream>
71 #include <stdexcept>
72 #include <cmath>
73 #include <memory>
74 
75 namespace Rcs
76 {
77 
79 {
81  {
82  std::string actionModelType = "unspecified";
83  properties->getProperty(actionModelType, "actionModelType");
84 
85  // Common for the action models
86  RcsBody* effector = RcsGraph_getBodyByName(graph, "Effector");
87  RCHECK(effector);
88 
89  if (actionModelType == "joint_pos") {
90  return new AMJointControlPosition(graph);
91  }
92 
93  else if (actionModelType == "joint_vel") {
94  double max_action = 90*M_PI/180; // [rad/s]
95  return new AMIntegrate1stOrder(new AMJointControlPosition(graph), max_action);
96  }
97 
98  else if (actionModelType == "joint_acc") {
99  double max_action = RCS_DEG2RAD(120); // [rad/s^2]
100  return new AMIntegrate2ndOrder(new AMJointControlPosition(graph), max_action);
101  }
102 
103  else if (actionModelType == "ik") {
104  // Create the action model
105  auto amIK = new AMIKGeneric(graph);
106  if (properties->getPropertyBool("positionTasks", true)) {
107  throw std::invalid_argument("Position tasks are not implemented for AMIKGeneric in this environment.");
108  }
109  else {
110  amIK->addTask(new TaskVelocity1D("Xd", graph, effector, nullptr, nullptr));
111  amIK->addTask(new TaskVelocity1D("Zd", graph, effector, nullptr, nullptr));
112  return amIK;
113  }
114  }
115 
116  else if (actionModelType == "ik_activation") {
117  // Get the method how to combine the movement primitives / tasks given their activation (not used in every case)
118  std::string taskCombinationMethod = "unspecified";
119  properties->getProperty(taskCombinationMethod, "taskCombinationMethod");
121 
122  // Create the action model
123  auto amIK = new AMIKControllerActivation(graph, tcm);
124  std::vector<Task*> tasks;
125 
126  // Check if the tasks are defined on position or velocity level. Adapt their parameters if desired.
127  if (properties->getPropertyBool("positionTasks", true)) {
128  // Define the Rcs controller tasks
129  RcsBody* goal1 = RcsGraph_getBodyByName(graph, "Goal1");
130  RcsBody* goal2 = RcsGraph_getBodyByName(graph, "Goal2");
131  RcsBody* goal3 = RcsGraph_getBodyByName(graph, "Goal3");
132  RCHECK(goal1);
133  RCHECK(goal2);
134  RCHECK(goal3);
135  int i = 0;
136 
137 // tasks.emplace_back(new TaskDistance(graph, effector, goal1));
138 // tasks.emplace_back(new TaskDistance(graph, effector, goal2));
139 // tasks.emplace_back(new TaskDistance(graph, effector, goal3));
140 // for (auto task : tasks) {
141 // std::stringstream taskName;
142 // taskName << "Distance " << i++ << " [m]";
143 // task->resetParameter(Task::Parameters(0., 1.5, 1.0, taskName.str()));
144 // }
145  tasks.emplace_back(new TaskPosition3D(graph, effector, goal1, nullptr));
146  tasks.emplace_back(new TaskPosition3D(graph, effector, goal2, nullptr));
147  tasks.emplace_back(new TaskPosition3D(graph, effector, goal3, nullptr));
148  for (auto task : tasks) {
149  std::stringstream taskName;
150  taskName << " Position " << i++ << " [m]";
151  task->resetParameter(
152  Task::Parameters(-0.9, 0.9, 1.0, "X" + taskName.str()));
153  task->addParameter(
154  Task::Parameters(-0.01, 0.01, 1.0, "Y" + taskName.str()));
155  task->addParameter(Task::Parameters(0., 1.4, 1.0, "Z" + taskName.str()));
156  }
157  }
158  else {
159  throw std::invalid_argument("Velocity tasks are not supported for AMIKControllerActivation.");
160  }
161 
162  // Add the tasks
163  for (auto t : tasks) { amIK->addTask(t); }
164 
165  // Set the tasks' desired states
166  std::vector<PropertySource*> taskSpec = properties->getChildList("taskSpecIK");
167  amIK->setXdesFromTaskSpec(taskSpec);
168 
169  // Incorporate collision costs into IK
170  if (properties->getPropertyBool("collisionAvoidanceIK", true)) {
171  REXEC(4) {
172  std::cout << "IK considers the provided collision model" << std::endl;
173  }
174  amIK->setupCollisionModel(collisionMdl);
175  }
176 
177  return amIK;
178  }
179 
180  else if (actionModelType == "ds_activation") {
181  // Obtain the inner action model
182  std::unique_ptr<AMIKGeneric> innerAM(new AMIKGeneric(graph));
183 
184  // Check if the MPs are defined on position or velocity level
185  if (properties->getPropertyBool("positionTasks", true)) {
186  innerAM->addTask(new TaskPosition1D("X", graph, effector, nullptr, nullptr));
187  innerAM->addTask(new TaskPosition1D("Z", graph, effector, nullptr, nullptr));
188  }
189  else {
190  innerAM->addTask(new TaskVelocity1D("Xd", graph, effector, nullptr, nullptr));
191  innerAM->addTask(new TaskVelocity1D("Zd", graph, effector, nullptr, nullptr));
192  }
193 
194  // Obtain the task data
195  auto& taskSpec = properties->getChildList("tasks");
196  std::vector<std::unique_ptr<DynamicalSystem>> tasks;
197  for (auto ts : taskSpec) {
198  // All tasks cover the x and the z coordinate, thus no DSSlice is necessary
199  tasks.emplace_back(DynamicalSystem::create(ts, innerAM->getDim()));
200  }
201  if (tasks.empty()) {
202  throw std::invalid_argument("No tasks specified!");
203  }
204 
205  // Incorporate collision costs into IK
206  if (properties->getPropertyBool("collisionAvoidanceIK", true)) {
207  REXEC(4) {
208  std::cout << "IK considers the provided collision model" << std::endl;
209  }
210  innerAM->setupCollisionModel(collisionMdl);
211  }
212 
213  // Setup task-based action model
214  std::vector<DynamicalSystem*> taskRel;
215  for (auto& task : tasks) {
216  taskRel.push_back(task.release());
217  }
218 
219  // Get the method how to combine the movement primitives / tasks given their activation (not used in every case)
220  std::string taskCombinationMethod = "unspecified";
221  properties->getProperty(taskCombinationMethod, "taskCombinationMethod");
223 
224  // Create the action model
225  return new AMDynamicalSystemActivation(innerAM.release(), taskRel, tcm);
226  }
227 
228  else {
229  std::ostringstream os;
230  os << "Unsupported action model type: " << actionModelType;
231  throw std::invalid_argument(os.str());
232  }
233  }
234 
236  {
237  auto fullState = new OMCombined();
238 
239  if (properties->getPropertyBool("observeVelocities", true)) {
240  // Observe effector position and velocities
241  auto omLin = new OMBodyStateLinear(graph, "Effector"); // in world coordinates
242  omLin->setMinState(-1.7); // [m]
243  omLin->setMaxState(1.7); // [m]
244  omLin->setMaxVelocity(5.0); // [m/s]
245  fullState->addPart(OMPartial::fromMask(omLin, {true, false, true})); // mask out y axis
246  }
247  else {
248  auto omLin = new OMBodyStateLinearPositions(graph, "Effector"); // in world coordinates
249  omLin->setMinState(-1.7); // [m]
250  omLin->setMaxState(1.7); // [m]
251  fullState->addPart(OMPartial::fromMask(omLin, {true, false, true}));
252  }
253 
254  // Add force/torque measurements
255  if (properties->getPropertyBool("observeForceTorque", true)) {
256  RcsSensor* fts = RcsGraph_getSensorByName(graph, "EffectorLoadCell");
257  if (fts) {
258  auto omForceTorque = new OMForceTorque(graph, fts->name, 500);
259  fullState->addPart(OMPartial::fromMask(omForceTorque, {true, false, true, false, false, false}));
260  }
261  }
262 
263  // Add current collision cost
264  if (properties->getPropertyBool("observeCollisionCost", false) & (collisionMdl != nullptr)) {
265  // Add the collision cost observation model
266  auto omColl = new OMCollisionCost(collisionMdl);
267  fullState->addPart(omColl);
268  }
269 
270  // Add collision prediction
271  if (properties->getPropertyBool("observePredictedCollisionCost", false) && collisionMdl != nullptr) {
272  // Get horizon from config
273  int horizon = 20;
274  properties->getChild("collisionConfig")->getProperty(horizon, "predCollHorizon");
275  // Add collision model
276  auto omCollisionCost = new OMCollisionCostPrediction(graph, collisionMdl, actionModel, 20);
277  fullState->addPart(omCollisionCost);
278  }
279 
280  // Add manipulability index
281  auto ikModel = actionModel->unwrap<ActionModelIK>();
282  if (properties->getPropertyBool("observeManipulabilityIndex", false) && ikModel) {
283  bool ocm = properties->getPropertyBool("observeCurrentManipulability", true);
284  fullState->addPart(new OMManipulabilityIndex(ikModel, ocm));
285  }
286 
287  // Add the task space discrepancy observation model
288  if (properties->getPropertyBool("observeTaskSpaceDiscrepancy", false)) {
289  auto wamIK = actionModel->unwrap<ActionModelIK>();
290  if (wamIK) {
291  auto omTSDescr = new OMTaskSpaceDiscrepancy("Effector", graph, wamIK->getController()->getGraph());
292  fullState->addPart(OMPartial::fromMask(omTSDescr, {true, false, true}));
293  }
294  else {
295  delete fullState;
296  throw std::invalid_argument("The action model needs to be of type ActionModelIK!");
297  }
298  }
299 
300  std::string actionModelType = "unspecified";
301  properties->getProperty(actionModelType, "actionModelType");
302  bool haveJointPos = actionModelType == "joint_pos";
303 
304  if (haveJointPos) {
305  fullState->addPart(OMJointState::observeUnconstrainedJoints(graph));
306  }
307 
308  else if (actionModelType == "ds_activation") {
309  if (properties->getPropertyBool("observeDynamicalSystemGoalDistance", false)) {
310  // Add goal distances
311  auto castedAM = actionModel->unwrap<AMDynamicalSystemActivation>();
312  if (castedAM) {
313  auto omGoalDist = new OMDynamicalSystemGoalDistance(castedAM);
314  fullState->addPart(omGoalDist);
315  }
316  else {
317  delete fullState;
318  std::ostringstream os;
319  os << "The action model needs to be of type AMDynamicalSystemActivation but is: " << castedAM;
320  throw std::invalid_argument(os.str());
321  }
322  }
323 
324  if (properties->getPropertyBool("observeDynamicalSystemDiscrepancy", false)) {
325  // Add the discrepancies between commanded and executed the task space changes
326  auto castedAM = dynamic_cast<AMDynamicalSystemActivation*>(actionModel);
327  if (castedAM) {
328  auto omDescr = new OMDynamicalSystemDiscrepancy(castedAM);
329  fullState->addPart(omDescr);
330  }
331  else {
332  delete fullState;
333  std::ostringstream os;
334  os << "The action model needs to be of type AMDynamicalSystemActivation but is: " << castedAM;
335  throw std::invalid_argument(os.str());
336  }
337  }
338  }
339 
340  return fullState;
341  }
342 
344  {
345  manager->addParam("Link1", new PPDMassProperties());
346  manager->addParam("Link2", new PPDMassProperties());
347  manager->addParam("Link3", new PPDMassProperties());
348  }
349 
351  {
352  return new ISSPlanar3Link(graph);
353  }
354 
356  {
357  RcsBody* link3 = RcsGraph_getBodyByName(graph, "Link3");
358  RCHECK(link3);
359 // RcsBody* base = RcsGraph_getBodyByName(graph, "Base");
360 // RCHECK(base);
361 // return new ForceDisturber(link3, base);
362  return new ForceDisturber(link3, link3);
363  }
364 
365  virtual void initViewer(Rcs::Viewer* viewer)
366  {
367 #ifdef GRAPHICS_AVAILABLE
368  // Set camera next to base
369  RcsBody* base = RcsGraph_getBodyByName(graph, "Base");
370  double cameraCenter[3];
371  Vec3d_copy(cameraCenter, base->A_BI->org);
372  cameraCenter[1] -= 0.5;
373  cameraCenter[2] += 0.3;
374 
375  // Set the camera position
376  double cameraLocation[3];
377  cameraLocation[0] = 0.;
378  cameraLocation[1] = 4.;
379  cameraLocation[2] = 2.5;
380 
381  // Camera up vector defaults to z
382  double cameraUp[3];
383  Vec3d_setUnitVector(cameraUp, 2);
384 
385  // Apply camera position
386  viewer->setCameraHomePosition(osg::Vec3d(cameraLocation[0], cameraLocation[1], cameraLocation[2]),
387  osg::Vec3d(cameraCenter[0], cameraCenter[1], cameraCenter[2]),
388  osg::Vec3d(cameraUp[0], cameraUp[1], cameraUp[2]));
389 #endif
390  }
391 
392  void
394  std::vector<std::string>& linesOut,
395  double currentTime,
396  const MatNd* obs,
397  const MatNd* currentAction,
398  PhysicsBase* simulator,
399  PhysicsParameterManager* physicsManager,
400  ForceDisturber* forceDisturber) override
401  {
402  // Obtain simulator name
403  const char* simName = "None";
404  if (simulator != nullptr) {
405  simName = simulator->getClassName();
406  }
407 
408  linesOut.emplace_back(
409  string_format("physics engine: %s sim time: %2.3f s", simName, currentTime));
410 
411  unsigned int numPosCtrlJoints = 0;
412  unsigned int numTrqCtrlJoints = 0;
413  // Iterate over unconstrained joints
414  RCSGRAPH_TRAVERSE_JOINTS(graph) {
415  if (JNT->jacobiIndex != -1) {
416  if (JNT->ctrlType == RCSJOINT_CTRL_TYPE::RCSJOINT_CTRL_POSITION) {
417  numPosCtrlJoints++;
418  }
419  else if (JNT->ctrlType == RCSJOINT_CTRL_TYPE::RCSJOINT_CTRL_TORQUE) {
420  numTrqCtrlJoints++;
421  }
422  }
423  }
424  linesOut.emplace_back(
425  string_format("num joints: %d total, %d pos ctrl, %d trq ctrl", graph->nJ, numPosCtrlJoints,
426  numTrqCtrlJoints));
427 
428  unsigned int sd = observationModel->getStateDim();
429 
432  if (omLin) {
433  linesOut.emplace_back(
434  string_format("end-eff pos: [% 1.3f,% 1.3f] m end-eff vel: [% 1.2f,% 1.2f] m/s",
435  obs->ele[omLin.pos], obs->ele[omLin.pos + 1],
436  obs->ele[sd + omLin.vel], obs->ele[sd + omLin.vel + 1]));
437  }
438  else if (omLinPos) {
439  linesOut.emplace_back(
440  string_format("end-eff pos: [% 1.3f,% 1.3f] m",
441  obs->ele[omLinPos.pos], obs->ele[omLinPos.pos + 1]));
442  }
443 
445  if (omGD) {
446  linesOut.emplace_back(
447  string_format("goal dist pos: [% 1.3f,% 1.3f,% 1.3f] m",
448  obs->ele[omGD.pos + 0], obs->ele[omGD.pos + 1], obs->ele[omGD.pos + 2]));
449  }
450 
452  if (omFT) {
453  linesOut.emplace_back(
454  string_format("forces: [% 3.1f,% 3.1f] N", obs->ele[omFT.pos + 0], obs->ele[omFT.pos + 1]));
455  }
456 
457  if (forceDisturber) {
458  const double* distForce = forceDisturber->getLastForce();
459  linesOut.emplace_back(
460  string_format("disturbances: [% 3.1f,% 3.1f,% 3.1f] N", distForce[0], distForce[1], distForce[2]));
461  }
462 
463  linesOut.emplace_back(
464  string_format("actions: [% 1.3f,% 1.3f,% 1.3f]",
465  currentAction->ele[0], currentAction->ele[1], currentAction->ele[2]));
466 
467  if (physicsManager != nullptr) {
468  // Get the parameters that are not stored in the Rcs graph
469  BodyParamInfo* link1_bpi = physicsManager->getBodyInfo("Link1");
470  BodyParamInfo* link2_bpi = physicsManager->getBodyInfo("Link2");
471  BodyParamInfo* link3_bpi = physicsManager->getBodyInfo("Link3");
472 
473  linesOut.emplace_back(
474  string_format("link masses: [% 1.3f,% 1.3f,% 1.3f] kg", link1_bpi->body->m, link2_bpi->body->m,
475  link3_bpi->body->m));
476  }
477 
479  if (omManip) {
480  linesOut.emplace_back(string_format("manipulability: % 5.3f", obs->ele[omManip.pos + 0]));
481  }
482  }
483 };
484 
485 // Register
487 
488 }
virtual bool getProperty(std::string &out, const char *property)=0
virtual bool getPropertyBool(const char *property, bool def=false)=0
RcsBody * body
The body.
Definition: BodyParamInfo.h:64
static ExperimentConfigRegistration< ECPlanar3Link > RegPlanar3Link("Planar3Link")
virtual PropertySource * getChild(const char *prefix)=0
void addParam(const char *bodyName, PhysicsParameterDescriptor *desc)
const double * getLastForce() const
static TaskCombinationMethod checkTaskCombinationMethod(std::string tcmName)
TaskCombinationMethod
Definition: ActionModel.h:46
static ObservationModel * observeUnconstrainedJoints(RcsGraph *graph)
virtual const std::vector< PropertySource * > & getChildList(const char *prefix)=0
virtual unsigned int getStateDim() const =0
BodyParamInfo * getBodyInfo(const char *bodyName)
static DynamicalSystem * create(PropertySource *properties, unsigned int innerTaskDim)
static OMPartial * fromMask(ObservationModel *wrapped, const std::vector< bool > &mask, bool exclude=false)
Definition: OMPartial.cpp:116
std::string string_format(const std::string &format, Args ... args)
Definition: string_format.h:45
ObservationModel * observationModel
Observation model (pluggable) used to create the observation which will be returned from step() ...
PropertySource * properties
Property source (owned)
const AM * unwrap() const
Definition: ActionModel.h:142
ActionModel * actionModel
Action model (pluggable)
RcsCollisionMdl * collisionMdl
Collision model to use. Is based on the graph, so take care when using with IK etc.
RcsGraph * graph
Graph description.