TaskSpaceVMP.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package RobotAPI::ArmarXObjects::DMPController
17 * @author zhou ( you dot zhou at kit dot edu )
18 * @date 2018
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
23#include "TaskSpaceVMP.h"
24
25#include <boost/archive/text_iarchive.hpp>
26#include <boost/archive/text_oarchive.hpp>
27#include <boost/archive/xml_iarchive.hpp>
28#include <boost/archive/xml_oarchive.hpp>
29
30#include <Eigen/Geometry>
31
32#include <VirtualRobot/MathTools.h>
33#include <VirtualRobot/RobotNodeSet.h>
34
38
40
41#include <dmp/representation/dmp/umitsmp.h>
42
44{
45
46 void
48 const Eigen::Matrix4f& currentPose,
49 const Eigen::VectorXf& twist)
50 {
51 canVal = flow(canVal, deltaT, currentPose, twist);
52 }
53
54 double
56 double deltaT,
57 const Eigen::Matrix4f& currentPose,
58 const Eigen::VectorXf& twist)
59 {
60 if (paused)
61 {
62 targetVel.setZero();
63 return canVal;
64 }
65 if (canVal < 0.1 && config.DMPStyle == "Periodic")
66 {
67 canVal = config.motionTimeDuration;
68 }
69 if (canVal < 1e-8 && config.DMPStyle == "Discrete")
70 {
71 targetVel.setZero();
72 return canVal;
73 }
74
75 Eigen::Vector3f currentPosition;
76 Eigen::Quaterniond currentOrientation;
77 double posiError = 0;
78 double oriError = 0;
79
80 getError(currentPose, currentPosition, currentOrientation, posiError, oriError);
81
82 double poseError = posiError + config.phaseStopParas.mm2radi * oriError;
83
84 double phaseDist;
85 if (isDisturbance)
86 {
87 phaseDist = config.phaseStopParas.backDist;
88 }
89 else
90 {
91 phaseDist = config.phaseStopParas.goDist;
92 }
93 double phaseL = config.phaseStopParas.maxValue;
94 double phaseK = config.phaseStopParas.slop;
95
96 double phaseStop = phaseL / (1 + exp(-phaseK * (poseError - phaseDist)));
97 double mpcFactor = 1 - (phaseStop / phaseL);
98
99 if (mpcFactor < 0.1)
100 {
101 isDisturbance = true;
102 }
103
104 if (mpcFactor > 0.9)
105 {
106 isDisturbance = false;
107 }
108
109 double timeDuration = config.motionTimeDuration;
110 canVal -= tau * deltaT * 1; // / (1 + phaseStop) ;
111
112
113 DMP::Vec<DMP::DMPState> temporalState = dmpPtr->calculateDirectlyVelocity(
114 currentState, canVal / timeDuration, deltaT / timeDuration, targetPoseVec);
115
116 // scale translation velocity
117 for (size_t i = 0; i < 3; ++i)
118 {
119 currentState[i].vel = tau * temporalState[i].vel * config.DMPAmplitude / timeDuration;
120 currentState[i].pos += deltaT * currentState[i].vel;
121 }
122
123 // define the translation velocity
125 {
126 float vel0, vel1;
127
128 Eigen::Vector3f linearVel;
129 linearVel << twist(0), twist(1), twist(2);
130 for (size_t i = 0; i < 3; i++)
131 {
132 vel0 = currentState[i].vel;
133 vel1 = config.phaseStopParas.Kpos * (targetPoseVec[i] - currentPosition(i)) -
134 config.phaseStopParas.Dpos * linearVel(i);
135 targetVel(i) = mpcFactor * vel0 + (1 - mpcFactor) * vel1;
136 }
137 }
138 else
139 {
140 for (size_t i = 0; i < 3; i++)
141 {
142 targetVel(i) = currentState[i].vel;
143 }
144 }
145
146
147 // define the rotation velocity
148 Eigen::Quaterniond dmpQuaternionVel;
149 dmpQuaternionVel.w() = temporalState[3].vel;
150 dmpQuaternionVel.x() = temporalState[4].vel;
151 dmpQuaternionVel.y() = temporalState[5].vel;
152 dmpQuaternionVel.z() = temporalState[6].vel;
153
154 Eigen::Quaterniond dmpQuaternionPosi;
155 dmpQuaternionPosi.w() = currentState[3].pos;
156 dmpQuaternionPosi.x() = currentState[4].pos;
157 dmpQuaternionPosi.y() = currentState[5].pos;
158 dmpQuaternionPosi.z() = currentState[6].pos;
159
160
161 Eigen::Quaterniond angularVel0 = dmpQuaternionVel * dmpQuaternionPosi.inverse();
162 angularVel0.w() *= 2;
163 angularVel0.x() *= 2;
164 angularVel0.y() *= 2;
165 angularVel0.z() *= 2;
166
167
168 double angularChange = angularVel0.angularDistance(oldDMPAngularVelocity);
169 if (angularVel0.w() * oldDMPAngularVelocity.w() < 0 &&
170 angularVel0.x() * oldDMPAngularVelocity.x() < 0 &&
171 angularVel0.y() * oldDMPAngularVelocity.y() < 0 &&
172 angularVel0.z() * oldDMPAngularVelocity.z() < 0 && angularChange < 1e-2)
173 {
174 angularVel0.w() = -angularVel0.w();
175 angularVel0.x() = -angularVel0.x();
176 angularVel0.y() = -angularVel0.y();
177 angularVel0.z() = -angularVel0.z();
178 }
179 oldDMPAngularVelocity = angularVel0;
180
181 // scale orientation velocity
182 angularVel0.w() = 0;
183 angularVel0.x() = tau * angularVel0.x() * config.oriAmplitude / timeDuration;
184 angularVel0.y() = tau * angularVel0.y() * config.oriAmplitude / timeDuration;
185 angularVel0.z() = tau * angularVel0.z() * config.oriAmplitude / timeDuration;
186
187 // Eigen::Quaterniond scaledQuat = (angularVel0 * dmpQuaternionPosi);
188 // currentState[3].vel = 0.5 * scaledQuat.w();
189 // currentState[4].vel = 0.5 * scaledQuat.x();
190 // currentState[6].vel = 0.5 * scaledQuat.z();
191 // currentState[5].vel = 0.5 * scaledQuat.y();
192
193 for (size_t i = 3; i < 7; ++i)
194 {
195 currentState[i].vel = tau * temporalState[i].vel * config.oriAmplitude / timeDuration;
196 currentState[i].pos += currentState[i].vel * deltaT;
197 }
198
200 {
201 Eigen::Vector3f currentAngularVel;
202 currentAngularVel << twist(3), twist(4), twist(5);
203
204 Eigen::Quaternionf targetQuaternionf;
205 targetQuaternionf.w() = targetPoseVec[3];
206 targetQuaternionf.x() = targetPoseVec[4];
207 targetQuaternionf.y() = targetPoseVec[5];
208 targetQuaternionf.z() = targetPoseVec[6];
209
210 Eigen::Matrix3f desiredMat(targetQuaternionf);
211 Eigen::Matrix3f currentMat = currentPose.block<3, 3>(0, 0);
212 Eigen::Matrix3f diffMat = desiredMat * currentMat.inverse();
213 Eigen::Vector3f diffRPY = VirtualRobot::MathTools::eigen3f2rpy(diffMat);
214 Eigen::Vector3f angularVel1 = config.phaseStopParas.Kori * diffRPY -
215 config.phaseStopParas.Dori * currentAngularVel;
216
217 targetVel(3) =
218 mpcFactor * angularVel0.x() / timeDuration + (1 - mpcFactor) * angularVel1(0);
219 targetVel(4) =
220 mpcFactor * angularVel0.y() / timeDuration + (1 - mpcFactor) * angularVel1(1);
221 targetVel(5) =
222 mpcFactor * angularVel0.z() / timeDuration + (1 - mpcFactor) * angularVel1(2);
223 }
224 else
225 {
226 targetVel(3) = angularVel0.x();
227 targetVel(4) = angularVel0.y();
228 targetVel(5) = angularVel0.z();
229 }
230
231 debugData.canVal = canVal;
232 debugData.oriError = oriError;
233 debugData.posiError = posiError;
234 debugData.mpcFactor = mpcFactor;
235 debugData.poseError = poseError;
236
237 return canVal;
238 }
239
240 void
241 TaskSpaceDMPController::learnDMPFromFiles(const std::vector<std::string>& fileNames,
242 const std::vector<double>& ratios)
243 {
244 if (ratios.size() != fileNames.size())
245 {
246 ARMARX_ERROR << "ratios should have the same size with files";
247 return;
248 }
249
250
251 DMP::Vec<DMP::SampledTrajectoryV2> trajs;
252
253 double ratioSum = 0;
254 for (size_t i = 0; i < fileNames.size(); ++i)
255 {
256 DMP::SampledTrajectoryV2 traj;
257 std::string absPath;
258 ArmarXDataPath::getAbsolutePath(fileNames.at(i), absPath);
259 traj.readFromCSVFile(absPath);
260 traj = DMP::SampledTrajectoryV2::normalizeTimestamps(traj, 0, 1);
261 trajs.push_back(traj);
262
263 ratioSum += ratios.at(i);
264 }
265
266 if (ratioSum == 0)
267 {
268 ARMARX_ERROR << "ratios are invalid. The sum is equal to 0";
269 return;
270 }
271
272 DMP::DVec ratiosVec;
273 ratiosVec.resize(ratios.size());
274 for (size_t i = 0; i < ratios.size(); ++i)
275 {
276 ratiosVec.at(i) = ratios.at(i) / ratioSum;
277 }
278
279 dmpPtr->learnFromTrajectories(trajs);
280 dmpPtr->styleParas = dmpPtr->getStyleParasWithRatio(ratiosVec);
281 }
282
283 void
284 TaskSpaceDMPController::learnDMPFromFiles(const std::vector<std::string>& fileNames)
285 {
286 std::vector<double> ratios;
287 for (size_t i = 0; i < fileNames.size(); ++i)
288 {
289 if (i == 0)
290 {
291 ratios.push_back(1.0);
292 }
293 else
294 {
295 ratios.push_back(0.0);
296 }
297 }
298
299 learnDMPFromFiles(fileNames, ratios);
300 }
301
302 void
304 const DMP::Vec<DMP::SampledTrajectoryV2>& trajs,
305 const std::vector<double>& ratios)
306 {
307 dmpPtr->learnFromTrajectories(trajs);
308 dmpPtr->styleParas = dmpPtr->getStyleParasWithRatio(ratios);
309 }
310
311 void
312 TaskSpaceDMPController::setRatios(const std::vector<double>& ratios)
313 {
314 dmpPtr->styleParas = dmpPtr->getStyleParasWithRatio(ratios);
315 }
316
317 void
319 {
320 ARMARX_CHECK_EQUAL(traj->dim(), 7);
321 DMP::SampledTrajectoryV2 dmpTraj;
322
323 DMP::DVec timestamps(traj->getTimestamps());
324 for (size_t i = 0; i < traj->dim(); ++i)
325 {
326 DMP::DVec dimData(traj->getDimensionData(i, 0));
327 dmpTraj.addDimension(timestamps, dimData);
328 }
329
330 DMP::Vec<DMP::SampledTrajectoryV2> trajs;
331
332 dmpTraj = DMP::SampledTrajectoryV2::normalizeTimestamps(dmpTraj, 0, 1);
333 trajs.push_back(dmpTraj);
334 DMP::DVec ratiosVec;
335 ratiosVec.push_back(1.0);
336 dmpPtr->learnFromTrajectories(trajs);
337 dmpPtr->styleParas = dmpPtr->getStyleParasWithRatio(ratiosVec);
338 }
339
340 void
342 {
343 std::stringstream ss;
344 ss.str(dmpStr);
345 boost::archive::text_iarchive ia{ss};
346 DMP::UMITSMP* dmpPointer;
347 ia >> dmpPointer;
348 dmpPtr.reset(dmpPointer);
349 }
350
351 std::string
353 {
354 std::stringstream ss;
355 boost::archive::text_oarchive oa{ss};
356 oa << dmpPtr.get();
357 return ss.str();
358 }
359
360 void
361 TaskSpaceDMPController::loadDMPFromXML(const std::string& dmpXML)
362 {
363 std::string absPath;
364 ArmarXDataPath::getAbsolutePath(dmpXML, absPath);
365 DMP::UMITSMP* dmpPointer;
366 std::ifstream ifs(absPath);
367 boost::archive::xml_iarchive ai(ifs);
368 ai >> boost::serialization::make_nvp("dmp", dmpPointer);
369 dmpPtr.reset(dmpPointer);
370 }
371
372 void
373 TaskSpaceDMPController::saveDMPToXML(const std::string& dmpXML)
374 {
375 std::string absPath;
376 ArmarXDataPath::getAbsolutePath(dmpXML, absPath);
377 std::ofstream ofs(absPath);
378 boost::archive::xml_oarchive ar(ofs);
379 DMP::UMITSMP* dmpPointer = dmpPtr.get();
380 ar << boost::serialization::make_nvp("dmp", dmpPointer);
381 dmpPtr.reset(dmpPointer);
382 }
383
384 void
385 TaskSpaceDMPController::setViaPose(double canVal, const Eigen::Matrix4f& viaPose)
386 {
387
388 setViaPose(canVal, eigen4f2vec(viaPose));
389 }
390
391 void
393 const std::vector<double>& viaPoseWithQuaternion)
394 {
395 if (canVal <= dmpPtr->getUMin())
396 {
397 goalPoseVec = viaPoseWithQuaternion;
398 }
399 dmpPtr->setViaPoint(canVal, viaPoseWithQuaternion);
400 }
401
402 void
404 {
405 dmpPtr->removeViaPoints();
406 }
407
408 void
409 TaskSpaceDMPController::prepareExecution(const Eigen::Matrix4f& currentPose,
410 const Eigen::Matrix4f& goalPose)
411 {
412 std::vector<double> currentPoseVec = eigen4f2vec(currentPose);
413 std::vector<double> goalPoseVec = eigen4f2vec(goalPose);
414
415 prepareExecution(currentPoseVec, goalPoseVec);
416 }
417
418 void
419 TaskSpaceDMPController::prepareExecution(const std::vector<double>& currentPoseVec,
420 const std::vector<double>& goalPoseVec)
421 {
422 ARMARX_CHECK_EQUAL(currentPoseVec.size(), 7);
423 ARMARX_CHECK_EQUAL(goalPoseVec.size(), 7);
424
425 ARMARX_IMPORTANT << "prepareExecution: currentPoseVec: " << currentPoseVec;
426 for (size_t i = 0; i < currentPoseVec.size(); ++i)
427 {
428 currentState[i].pos = currentPoseVec.at(i);
429 currentState[i].vel = 0;
430 targetPoseVec.at(i) = currentPoseVec.at(i);
431 }
432
433 dmpPtr->prepareExecution(goalPoseVec, currentState, 1, 1);
434 this->goalPoseVec = goalPoseVec;
435 isDisturbance = false;
436 canVal = config.motionTimeDuration;
437 oldDMPAngularVelocity.setIdentity();
438 }
439
440 void
442 {
443 if (times <= 0)
444 {
445 ARMARX_WARNING << "TaskSpaceDMPController: cannot set non-positive speed times";
446 }
447
448 tau = times;
449 }
450
451 void
453 {
454 if (amp <= 0)
455 {
456 ARMARX_WARNING << "TaskSpaceDMPController: cannot set non-positive amplitude";
457 }
458 config.DMPAmplitude = amp;
459 }
460
461 std::vector<double>
462 TaskSpaceDMPController::eigen4f2vec(const Eigen::Matrix4f& pose)
463 {
464 std::vector<double> viaPoseVec;
465 viaPoseVec.resize(7);
466
467 for (size_t i = 0; i < 3; ++i)
468 {
469 viaPoseVec.at(i) = pose(i, 3);
470 }
471
472 VirtualRobot::MathTools::Quaternion quat = VirtualRobot::MathTools::eigen4f2quat(pose);
473
474 viaPoseVec.at(3) = quat.w;
475 viaPoseVec.at(4) = quat.x;
476 viaPoseVec.at(5) = quat.y;
477 viaPoseVec.at(6) = quat.z;
478
479 return viaPoseVec;
480 }
481
482 void
483 TaskSpaceDMPController::getError(const Eigen::Matrix4f& currentPose,
484 Eigen::Vector3f& currentPosition,
485 Eigen::Quaterniond& currentOrientation,
486 double& posiError,
487 double& oriError)
488 {
489 currentPosition.setZero();
490 currentPosition << currentPose(0, 3), currentPose(1, 3), currentPose(2, 3);
491
492 VirtualRobot::MathTools::Quaternion quat =
493 VirtualRobot::MathTools::eigen4f2quat(currentPose);
494 currentOrientation.w() = quat.w;
495 currentOrientation.x() = quat.x;
496 currentOrientation.y() = quat.y;
497 currentOrientation.z() = quat.z;
498
499 posiError = 0;
500 for (size_t i = 0; i < 3; ++i)
501 {
502 posiError += pow(currentPosition(i) - targetPoseVec[i], 2);
503 }
504 posiError = sqrt(posiError);
505
506 Eigen::Quaterniond targetQuaternion;
507 targetQuaternion.w() = targetPoseVec[3];
508 targetQuaternion.x() = targetPoseVec[4];
509 targetQuaternion.y() = targetPoseVec[5];
510 targetQuaternion.z() = targetPoseVec[6];
511
512 oriError = targetQuaternion.angularDistance(currentOrientation);
513 if (oriError > M_PI)
514 {
515 oriError = 2 * M_PI - oriError;
516 }
517 }
518
522 {
523 this->config = config;
524
525 int ModeInd;
526 if (config.DMPMode == "MinimumJerk")
527 {
528 ModeInd = 2;
529 }
530 else
531 {
532 ModeInd = 1;
533 }
534
535
536 dmpPtr.reset(new DMP::UMITSMP(config.DMPKernelSize, ModeInd));
537 canVal = config.motionTimeDuration;
538
539 targetPoseVec.resize(7);
540 targetVel.resize(6);
541 targetVel.setZero();
542 currentState.resize(7);
543
544 this->isPhaseStopControl = isPhaseStopControl;
545 dmpName = name;
546 this->paused = false;
547 tau = 1;
548 }
549
550 std::string
552 {
553 return dmpName;
554 }
555
556 Eigen::VectorXf
558 {
559 return targetVel;
560 }
561
562 std::vector<double>
564 {
565 return targetPoseVec;
566 }
567
568 Eigen::Matrix4f
570 {
571 Eigen::Matrix4f res = VirtualRobot::MathTools::quat2eigen4f(
572 targetPoseVec.at(4), targetPoseVec.at(5), targetPoseVec.at(6), targetPoseVec.at(3));
573 res(0, 3) = targetPoseVec.at(0);
574 res(1, 3) = targetPoseVec.at(1);
575 res(2, 3) = targetPoseVec.at(2);
576
577 return res;
578 }
579
580 Eigen::Matrix4f
582 {
583 Eigen::Matrix4f res = VirtualRobot::MathTools::quat2eigen4f(currentState.at(4).pos,
584 currentState.at(5).pos,
585 currentState.at(6).pos,
586 currentState.at(3).pos);
587 res(0, 3) = currentState.at(0).pos;
588 res(1, 3) = currentState.at(1).pos;
589 res(2, 3) = currentState.at(2).pos;
590
591 return res;
592 }
593
594 void
595 TaskSpaceDMPController::setGoalPose(const Eigen::Matrix4f& goalPose)
596 {
597 setViaPose(dmpPtr->getUMin(), goalPose);
598 }
599
600 void
601 TaskSpaceDMPController::setGoalPoseVec(const std::vector<double> goalPoseVec)
602 {
603 setViaPose(dmpPtr->getUMin(), goalPoseVec);
604 }
605
608 {
609 return dmpPtr;
610 }
611
612 void
614 {
615 this->paused = true;
616 }
617
618 void
620 {
621 this->paused = false;
622 }
623
624 void
625 TaskSpaceDMPController::setWeights(const std::vector<std::vector<double>>& weights)
626 {
627 dmpPtr->setWeights(weights);
628 }
629
630 void
631 TaskSpaceDMPController::setTranslWeights(const std::vector<std::vector<double>>& weights)
632 {
633 ARMARX_CHECK_EQUAL(weights.size(), 3);
634
635 for (size_t i = 0; i < 3; ++i)
636 {
637 dmpPtr->setWeights(i, weights[i]);
638 }
639 }
640
641 void
642 TaskSpaceDMPController::setRotWeights(const std::vector<std::vector<double>>& weights)
643 {
644 ARMARX_CHECK_EQUAL(weights.size(), 4);
645
646 for (size_t i = 0; i < 4; ++i)
647 {
648 dmpPtr->setWeights(3 + i, weights[i]);
649 }
650 }
651
652 DMP::DVec2d
654 {
655 return dmpPtr->getWeights();
656 }
657
658 DMP::DVec2d
660 {
661 DMP::DVec2d res;
662 DMP::DVec2d weights = getWeights();
663 for (size_t i = 0; i < 3; ++i)
664 {
665 res.push_back(weights[i]);
666 }
667 return res;
668 }
669
670 DMP::DVec2d
672 {
673 DMP::DVec2d res;
674 DMP::DVec2d weights = getWeights();
675 for (size_t i = 3; i < 7; ++i)
676 {
677 res.push_back(weights[i]);
678 }
679 return res;
680 }
681} // namespace armarx::control::deprecated_njoint_mp_controller::tsvmp
#define M_PI
Definition MathTools.h:17
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
void learnDMPFromSampledTrajectory(const DMP::Vec< DMP::SampledTrajectoryV2 > &trajs)
void prepareExecution(const Eigen::Matrix4f &currentPose, const Eigen::Matrix4f &goalPose)
TaskSpaceDMPController(std::string name, const TaskSpaceDMPControllerConfig &config, bool isPhaseStopControl=true)
void setTranslWeights(const std::vector< std::vector< double > > &weights)
void learnDMPFromFiles(const std::vector< std::string > &fileNames, const std::vector< double > &ratios)
void setRotWeights(const std::vector< std::vector< double > > &weights)
void setWeights(const std::vector< std::vector< double > > &weights)
void flow(double deltaT, const Eigen::Matrix4f &currentPose, const Eigen::VectorXf &twist)
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
boost::shared_ptr< class UMITSMP > UMITSMPPtr
Quaternion< double, 0 > Quaterniond
Quaternion< float, 0 > Quaternionf
IceInternal::Handle< Trajectory > TrajectoryPtr
Definition Trajectory.h:52