KBMComponent.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2012-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @package MemoryX::ArmarXObjects::KBMComponent
19 * @author Jonas Rauber ( kit at jonasrauber dot de )
20 * @date 2014
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
24
25#include "KBMComponent.h"
26
28
29#include <cmath>
30#include <fstream>
31
32#include <VirtualRobot/Import/RobotImporterFactory.h>
33#include <VirtualRobot/KinematicChain.h>
34#include <VirtualRobot/MathTools.h>
35#include <VirtualRobot/Nodes/RobotNode.h>
36#include <VirtualRobot/Robot.h>
37#include <VirtualRobot/RuntimeEnvironment.h>
38
39#include <RobotAPI/interface/core/RobotState.h>
41
44
45namespace armarx
46{
47 void
49 {
50 usingProxy(getProperty<std::string>("LongtermMemoryName").getValue());
51 usingProxy(getProperty<std::string>("RobotStateComponentName").getValue());
52 }
53
54 void
56 {
57 this->longtermMemoryPrx = getProxy<memoryx::LongtermMemoryInterfacePrx>(
58 getProperty<std::string>("LongtermMemoryName").getValue());
59 this->robotStateComponentPrx = getProxy<armarx::RobotStateComponentInterfacePrx>(
60 getProperty<std::string>("RobotStateComponentName").getValue());
61 }
62
63 void
67
68 void
72
79
80 /*
81 *
82 *
83 * Helper methods
84 *
85 *
86 */
87
88 Eigen::Map<const memoryx::KBM::Vector>
90 const std::vector<memoryx::KBM::Real>& position)
91 {
92 VR_ASSERT_MESSAGE((int)(position.size()) == kbm.getNDim(),
93 "position size doesn't match KBM dimensions");
94 return Eigen::Map<const memoryx::KBM::Vector>(position.data(), kbm.getNDim());
95 }
96
97 Eigen::Map<const memoryx::KBM::Vector>
99 const std::vector<memoryx::KBM::Real>& proprioception)
100 {
101 VR_ASSERT_MESSAGE((int)(proprioception.size()) == kbm.getNDoF(),
102 "proprioception size doesn't match KBM DoF");
103 return Eigen::Map<const memoryx::KBM::Vector>(proprioception.data(), kbm.getNDoF());
104 }
105
106 Eigen::Map<const memoryx::KBM::Matrix>
108 const std::vector<memoryx::KBM::Real>& position)
109 {
110 int nDim = kbm.getNDim();
111
112 if (position.size() % nDim != 0)
113 {
114 ARMARX_ERROR << "position has an invalid length";
115 }
116
117 return mapMatrix(nDim, position);
118 }
119
120 Eigen::Map<const memoryx::KBM::Matrix>
122 const std::vector<memoryx::KBM::Real>& proprioception)
123 {
124 int nDoF = kbm.getNDoF();
125
126 if (proprioception.size() % nDoF != 0)
127 {
128 ARMARX_ERROR << "proprioception has an invalid length";
129 }
130
131 return mapMatrix(nDoF, proprioception);
132 }
133
134 Eigen::Map<const memoryx::KBM::Matrix>
135 KBMComponent::mapMatrix(int rows, const std::vector<memoryx::KBM::Real>& data)
136 {
137 if (data.size() % rows != 0)
138 {
139 ARMARX_ERROR << "data has an invalid length";
140 }
141
142 VR_ASSERT(data.size() % rows == 0);
143 int cols = data.size() / rows;
144 return Eigen::Map<const memoryx::KBM::Matrix>(data.data(), rows, cols);
145 }
146
148 KBMComponent::getKBM(const std::string& name)
149 {
150 auto it = kbms.find(name);
151
152 if (it == kbms.end())
153 {
154 ARMARX_WARNING << "a KBM with the name '" << name << "' does not exist";
155 return memoryx::KBM::Models::KBM_ptr(); // null pointer
156 }
157
158 return it->second;
159 }
160
161 /*
162 *
163 *
164 * KBM Component interface methods
165 *
166 *
167 */
168
169 void
170 KBMComponent::createKBM(const std::string& name,
171 Ice::Int nDoF,
172 Ice::Int dim,
173 Ice::Float spreadAngle,
174 const Ice::Current& c)
175 {
176 /*
177 * Check if a KBM with the given name already exists
178 */
179 if (kbms.find(name) != kbms.end())
180 {
181 ARMARX_ERROR << "a KBM with the name '" << name << "' already exists";
182 return;
183 }
184
185 /*
186 * Create the KBM
187 */
188 memoryx::KBM::Models::KBM_ptr kbm(new memoryx::KBM::Models::KBM(nDoF, dim, spreadAngle));
189 kbms[name] = kbm;
190 createdFromVirtualRobot[name] = false;
191 ARMARX_INFO << "created a KBM with the name '" << name << "'";
192 }
193
194 void
195 KBMComponent::createArmar3KBM(const std::string& name,
196 const Ice::StringSeq& robotNodeNames,
197 const std::vector<memoryx::KBM::Real>& jointValueAverages,
198 const std::vector<memoryx::KBM::Real>& spreadAnglesSequence,
199 const std::string& tcpName,
200 bool useOrientation,
201 const Ice::Current& c)
202 {
203 /*
204 * check parameters
205 */
206 if (robotNodeNames.size() != 7)
207 {
208 ARMARX_WARNING << "createArmar3KBM should only be used with all 7 joints";
209 }
210
211 if (jointValueAverages.size() != robotNodeNames.size())
212 {
213 ARMARX_INFO << "robotNodeNames: " << robotNodeNames.size();
214 ARMARX_INFO << "jointValueAverages: " << jointValueAverages.size();
215 ARMARX_ERROR << "jointValueAverages should have the same size as robotNodeNames";
216 }
217
218 if (spreadAnglesSequence.size() != robotNodeNames.size())
219 {
220 ARMARX_INFO << "robotNodeNames: " << robotNodeNames.size();
221 ARMARX_INFO << "spreadAngles: " << spreadAnglesSequence.size();
222 ARMARX_ERROR << "spreadAnglesSequence should have the same size as robotNodeNames";
223 }
224
225 /*
226 * Check if a KBM with the given name already exists
227 */
228 if (kbms.find(name) != kbms.end())
229 {
230 ARMARX_ERROR << "a KBM with the name '" << name << "' already exists";
231 return;
232 }
233
234 /*
235 * spread angles
236 */
237 Eigen::Map<const memoryx::KBM::Vector> spreadAngles(spreadAnglesSequence.data(),
238 spreadAnglesSequence.size());
239 ARMARX_IMPORTANT << "spreadAngles: " << spreadAngles.transpose();
240
241 /*
242 * Save meta data
243 */
244 std::time_t now = std::time(0);
245 std::tm* date = std::localtime(&now);
246 char when[256] = {0};
247 std::strftime(when, sizeof(when), "%Y-%m-%d-%H-%M-%S", date);
248 std::string baseFilename = "data/evaluation/" + std::string(when) + "-" + name;
249
250 std::ofstream metaFile(baseFilename + "-create.txt", std::ios_base::app);
251 Eigen::Map<const memoryx::KBM::Vector> _jointValueAverages(jointValueAverages.data(),
252 jointValueAverages.size());
253
254 if (metaFile.is_open())
255 {
256 metaFile << "kbm name: " << name << std::endl;
257 metaFile << "joint offsets: " << _jointValueAverages << std::endl;
258 metaFile << "spread angles: " << spreadAngles << std::endl;
259 }
260
261 metaFile.close();
262
263 /*
264 * Load VirtualRobot
265 */
266 VirtualRobot::RobotImporterFactoryPtr importer =
267 VirtualRobot::RobotImporterFactory::fromFileExtension("xml", NULL);
268 std::string filename("robots/ArmarIII/ArmarIII.xml");
269 VirtualRobot::RuntimeEnvironment::getDataFileAbsolute(filename);
271 importer->loadFromFile(filename, VirtualRobot::RobotIO::eStructure);
272
273 if (!robot)
274 {
275 ARMARX_ERROR << "the ArmarIII.xml file could not be loaded";
276 }
277
278 VR_ASSERT(robot);
279
280 /*
281 * Create a robot node set with the names of the active joints
282 */
283 //VirtualRobot::RobotNodeSetPtr nodeSet = robot->getRobotNodeSet("TorsoRightArm");
284 VirtualRobot::RobotNodeSetPtr nodeSet = VirtualRobot::RobotNodeSet::createRobotNodeSet(
285 robot, "ToolUseRightArm", robotNodeNames, "Right Arm Base", tcpName, true);
286
287 if (!nodeSet)
288 {
289 ARMARX_ERROR << "the nodeSet could not be created";
290 }
291
292 VR_ASSERT(nodeSet);
293
294 /*
295 * Create a kinematic chain from the robot node set
296 */
297 VirtualRobot::KinematicChainPtr chain;
298 //chain.reset(new VirtualRobot::KinematicChain("TorsoRightArmChain",robot,nodeSet->getAllRobotNodes(),robot->getRobotNode("Wrist 2 R")));
299 chain.reset(new VirtualRobot::KinematicChain("ToolUseRightArmChain",
300 robot,
301 nodeSet->getAllRobotNodes(),
302 robot->getRobotNode(tcpName)));
303
304 if (!chain)
305 {
306 ARMARX_ERROR << "chain could not be created";
307 }
308
309 VR_ASSERT(chain);
310
311 /*
312 * Set joint values to their average position,
313 * so that samples are distributed around zero
314 */
315 ARMARX_IMPORTANT << "TCP GlobalPose before setting averages: "
316 << chain->getTCP()->getGlobalPose();
317 ARMARX_IMPORTANT << "setting averages: " << jointValueAverages;
318 Ice::FloatSeq jointValues;
319
320 for (auto& value : jointValueAverages)
321 {
322 jointValues.push_back(value);
323 }
324
325 chain->setJointValues(jointValues);
326 ARMARX_VERBOSE << "node " << chain->getNode(0)->getName()
327 << " is at: " << chain->getNode(0)->getJointValue();
328 ARMARX_IMPORTANT << "TCP GlobalPose after setting averages: "
329 << chain->getTCP()->getGlobalPose();
330
331 VirtualRobot::RobotNodePtr FoR = chain->getKinematicRoot();
332
333 VR_ASSERT(FoR);
334
335 /*
336 * TODO: print warning if not
337 */
338 ARMARX_IMPORTANT << "FoR GlobalPose should be the identity matrix: "
339 << FoR->getGlobalPose();
340
341 unsigned int nDoF = chain->getSize();
342 ARMARX_IMPORTANT << "degrees of freedom of the VirtualRobot's kinematic chain: " << nDoF;
343
344 /*
345 * Create the KBM
346 */
348 chain, FoR, spreadAngles, useOrientation));
349 kbms[name] = kbm;
350 createdFromVirtualRobot[name] = true;
351 ARMARX_INFO << "created a KBM with the name '" << name << "'";
352 }
353
354 void
355 KBMComponent::batch(const std::string& name,
356 const std::vector<memoryx::KBM::Real>& proprioceptionSequence,
357 const std::vector<memoryx::KBM::Real>& positionSequence,
358 Ice::Int method,
359 Ice::Float threshold,
360 const Ice::Current& c)
361 {
363
364 if (!kbm)
365 {
366 return;
367 }
368
369 /*
370 * It doesn't make sense to do batch learning on KBMs created from a VirtualRobot.
371 */
372 if (createdFromVirtualRobot[name])
373 {
374 ARMARX_ERROR << "the KBM " << name
375 << " was created from a VirtualRobot, it does not support batch learning";
376 return;
377 }
378
379 /*
380 * This maps the data column wise.
381 */
382 Eigen::Map<const memoryx::KBM::Matrix> proprioception =
383 mapProprioceptions(*kbm, proprioceptionSequence);
384 Eigen::Map<const memoryx::KBM::Matrix> position = mapPositions(*kbm, positionSequence);
385
386 if (proprioception.cols() != position.cols())
387 {
388 ARMARX_ERROR << "proprioception and position contain different number of samples";
389 return;
390 }
391
392 /*
393 * Start batch learning
394 */
395 kbm->batch(
396 proprioception, position, (memoryx::KBM::Models::KBM::Optimization)method, threshold);
397 }
398
399 void
400 KBMComponent::online(const std::string& name,
401 const std::vector<memoryx::KBM::Real>& proprioceptionSequence,
402 const std::vector<memoryx::KBM::Real>& positionSequence,
403 Ice::Float learningRate,
404 const Ice::Current& c)
405 {
407
408 if (!kbm)
409 {
410 return;
411 }
412
413 /*
414 * This maps the data column wise.
415 */
416 Eigen::Map<const memoryx::KBM::Matrix> proprioception =
417 mapProprioceptions(*kbm, proprioceptionSequence);
418 Eigen::Map<const memoryx::KBM::Matrix> position = mapPositions(*kbm, positionSequence);
419
420 if (proprioception.cols() != position.cols())
421 {
422 ARMARX_ERROR << "proprioception and position contain different number of samples";
423 return;
424 }
425
426 if (createdFromVirtualRobot[name])
427 {
428 /*
429 * support VirtualRobot KBMs by reversing joint order
430 */
432 << "reversing proprioception order because KBM was created from VirtualRobot";
433 kbm->online(proprioception.colwise().reverse(), position, learningRate);
434 }
435 else
436 {
437 // TODO: why does this work: KBM expects a Matrix and should not be
438 // compatible with a Map of Matrix without additional modifications
439 kbm->online(proprioception, position, learningRate);
440 }
441 }
442
443 void
445 const std::string& name,
446 const std::vector<memoryx::KBM::Real>& proprioceptionSequence,
447 const std::vector<memoryx::KBM::Real>& positionSequence,
448 Ice::Float learningRate,
449 const std::vector<memoryx::KBM::Real>& evaluationProprioceptionSequence,
450 const std::vector<memoryx::KBM::Real>& evaluationPositionSequence,
451 const Ice::Current& c)
452 {
454
455 if (!kbm)
456 {
457 return;
458 }
459
460 /*
461 * This maps the data column wise.
462 */
463 Eigen::Map<const memoryx::KBM::Matrix> _proprioception =
464 mapProprioceptions(*kbm, proprioceptionSequence);
465 Eigen::Map<const memoryx::KBM::Matrix> position = mapPositions(*kbm, positionSequence);
466
467 Eigen::Map<const memoryx::KBM::Matrix> _evaluationProprioception =
468 mapProprioceptions(*kbm, evaluationProprioceptionSequence);
469 Eigen::Map<const memoryx::KBM::Matrix> evaluationPosition =
470 mapPositions(*kbm, evaluationPositionSequence);
471
472 if (_proprioception.cols() != position.cols())
473 {
474 ARMARX_ERROR << "proprioception and position contain different number of samples";
475 return;
476 }
477
478 if (_evaluationProprioception.cols() != evaluationPosition.cols())
479 {
480 ARMARX_ERROR << "evaluation proprioception and evaluation position contain different "
481 "number of samples";
482 return;
483 }
484
485 memoryx::KBM::Matrix proprioception;
486 memoryx::KBM::Matrix evaluationProprioception;
487
488 if (createdFromVirtualRobot[name])
489 {
490 /*
491 * support VirtualRobot KBMs by reversing joint order
492 */
494 << "reversing proprioception order because KBM was created from VirtualRobot";
495 proprioception = _proprioception.colwise().reverse();
496 evaluationProprioception = _evaluationProprioception.colwise().reverse();
497 }
498 else
499 {
500 proprioception = _proprioception;
501 evaluationProprioception = _evaluationProprioception;
502 }
503
504 ARMARX_IMPORTANT << "training samples: " << proprioception.cols();
505 ARMARX_IMPORTANT << "evaluation samples: " << evaluationProprioception.cols();
506
507 /*
508 * Prepare file
509 */
510
511 std::time_t now = std::time(0);
512 std::tm* date = std::localtime(&now);
513 char when[256] = {0};
514 std::strftime(when, sizeof(when), "%Y-%m-%d-%H-%M-%S", date);
515 std::string baseFilename = "data/evaluation/" + std::string(when) + "-" + name;
516
517 std::ofstream metaFile(baseFilename + "-meta.txt", std::ios_base::app);
518 std::string evaluationFilename(baseFilename + "-evaluation.txt");
519 std::ofstream evaluationFile(evaluationFilename, std::ios_base::app);
520 std::string trainingProprioceptionsFilename(baseFilename + "-training-proprioceptions.txt");
521 std::ofstream trainingProprioceptionsFile(trainingProprioceptionsFilename,
522 std::ios_base::app);
523 std::string trainingPositionsFilename(baseFilename + "-training-positions.txt");
524 std::ofstream trainingPositionsFile(trainingPositionsFilename, std::ios_base::app);
525 std::string testProprioceptionsFilename(baseFilename + "-test-proprioceptions.txt");
526 std::ofstream testProprioceptionsFile(testProprioceptionsFilename, std::ios_base::app);
527 std::string testPositionsFilename(baseFilename + "-test-positions.txt");
528 std::ofstream testPositionsFile(testPositionsFilename, std::ios_base::app);
529
530 if (metaFile.is_open())
531 {
532 metaFile << "kbm name: " << name << std::endl;
533 metaFile << "learning rate: " << learningRate << std::endl;
534 metaFile << "degress of freedom: " << kbm->getNDoF() << std::endl;
535 metaFile << "dimensions: " << kbm->getNDim() << std::endl;
536 metaFile << "training samples: " << proprioception.cols() << std::endl;
537 metaFile << "test samples: " << evaluationProprioception.cols() << std::endl;
538 metaFile << "evaluation columns: mean, std, median, iqr, max" << std::endl;
539 }
540
541 metaFile.close();
542
543 if (!trainingProprioceptionsFile.is_open())
544 {
545 ARMARX_ERROR << "file " << trainingProprioceptionsFilename
546 << " could not be opened / created";
547 }
548
549 if (!trainingPositionsFile.is_open())
550 {
551 ARMARX_ERROR << "file " << trainingPositionsFilename
552 << " could not be opened / created";
553 }
554
555 if (!testProprioceptionsFile.is_open())
556 {
557 ARMARX_ERROR << "file " << testProprioceptionsFilename
558 << " could not be opened / created";
559 }
560
561 if (!testPositionsFile.is_open())
562 {
563 ARMARX_ERROR << "file " << testPositionsFilename << " could not be opened / created";
564 }
565
566 trainingProprioceptionsFile << proprioception.transpose() << std::endl;
567 trainingPositionsFile << position.transpose() << std::endl;
568 testProprioceptionsFile << evaluationProprioception.transpose() << std::endl;
569 testPositionsFile << evaluationPosition.transpose() << std::endl;
570
571 trainingProprioceptionsFile.close();
572 trainingPositionsFile.close();
573 testProprioceptionsFile.close();
574 testPositionsFile.close();
575
576 /*
577 * Learn, evaluate and write to file
578 */
579 if (!evaluationFile.is_open())
580 {
581 ARMARX_ERROR << "file " << evaluationFilename << " could not be opened / created";
582 }
583
585 kbm->getErrors(evaluationProprioception, evaluationPosition);
586 ARMARX_IMPORTANT << "online learning before learning: " << errors;
587 evaluationFile << errors.Mean << ", " << errors.STD << ", " << errors.Median << ", "
588 << errors.IQR << ", " << errors.Max << std::endl;
589
590 for (int i = 0; i < proprioception.cols(); i++)
591 {
592 kbm->online(proprioception.col(i), position.col(i), learningRate);
593 /*
594 * TODO: maybe use my own error calculation, including standard deviation, etc.
595 */
596 errors = kbm->getErrors(evaluationProprioception, evaluationPosition);
597 ARMARX_IMPORTANT << "online learning error after step " << (i + 1) << ": " << errors;
598 evaluationFile << errors.Mean << ", " << errors.STD << ", " << errors.Median << ", "
599 << errors.IQR << ", " << errors.Max << std::endl;
600 }
601
602 evaluationFile.close();
603 }
604
605 std::vector<memoryx::KBM::Real>
606 KBMComponent::predict(const std::string& name,
607 const std::vector<memoryx::KBM::Real>& proprioceptionSequence,
608 const Ice::Current& c)
609 {
611
612 if (!kbm)
613 {
614 return std::vector<memoryx::KBM::Real>();
615 }
616
617 Eigen::Map<const memoryx::KBM::Matrix> proprioception =
618 mapProprioceptions(*kbm, proprioceptionSequence);
619
620 int nDim = kbm->getNDim();
621 int nSamples = proprioception.cols();
622 std::vector<memoryx::KBM::Real> predictionSequence(nDim * nSamples);
623 Eigen::Map<memoryx::KBM::Matrix> prediction(predictionSequence.data(), nDim, nSamples);
624
625 if (createdFromVirtualRobot[name])
626 {
627 /*
628 * support VirtualRobot KBMs by reversing joint order
629 */
630 prediction = kbm->predict(proprioception.colwise().reverse());
631 }
632 else
633 {
634 prediction = kbm->predict(proprioception);
635 }
636
637 ARMARX_INFO << "KBM '" << name << "' predicted: " << prediction.transpose();
638 return predictionSequence;
639 }
640
641 void
642 KBMComponent::printErrors(const std::string& name,
643 const std::vector<memoryx::KBM::Real>& proprioceptionSequence,
644 const std::vector<memoryx::KBM::Real>& positionSequence,
645 const Ice::Current& c)
646 {
648
649 if (!kbm)
650 {
651 return;
652 }
653
654 /*
655 * This maps the data column wise.
656 */
657 Eigen::Map<const memoryx::KBM::Matrix> proprioception =
658 mapProprioceptions(*kbm, proprioceptionSequence);
659 Eigen::Map<const memoryx::KBM::Matrix> position = mapPositions(*kbm, positionSequence);
660
661 if (proprioception.cols() != position.cols())
662 {
663 ARMARX_ERROR << "proprioception and position contain different number of samples";
664 return;
665 }
666
667 ARMARX_IMPORTANT << "samples: " << proprioception.cols();
668
670
671 if (createdFromVirtualRobot[name])
672 {
673 /*
674 * support VirtualRobot KBMs by reversing joint order
675 */
676 errors = kbm->getErrors(proprioception.colwise().reverse(), position);
677 }
678 else
679 {
680 errors = kbm->getErrors(proprioception, position);
681 }
682
683 ARMARX_IMPORTANT << "KBM '" << name << "' error: " << errors;
684 }
685
686 void
687 KBMComponent::printAccumulatorStatistics(int nDim, int nDoF, const Ice::Current& c)
688 {
689 Eigen::Map<const memoryx::KBM::Matrix> proprioception =
690 mapMatrix(nDoF, proprioceptionAccumulator);
691 Eigen::Map<const memoryx::KBM::Matrix> position = mapMatrix(nDim, positionAccumulator);
692
693 ARMARX_VERBOSE << "proprioception samples: " << proprioception.cols();
694 ARMARX_VERBOSE << "position samples: " << position.cols();
695
696 ARMARX_VERBOSE << "proprioception minima: "
697 << proprioception.rowwise().minCoeff().transpose();
698 ARMARX_VERBOSE << "proprioception maxima: "
699 << proprioception.rowwise().maxCoeff().transpose();
700 ARMARX_VERBOSE << "proprioception average: " << proprioception.rowwise().mean().transpose();
701 ARMARX_VERBOSE << "position minima: " << position.rowwise().minCoeff().transpose();
702 ARMARX_VERBOSE << "position maxima: " << position.rowwise().maxCoeff().transpose();
703 ARMARX_VERBOSE << "position average: " << position.rowwise().mean().transpose();
704 }
705
706 Ice::DoubleSeq
707 KBMComponent::getRawJointValuesAverages(int nDoF, const Ice::Current& c)
708 {
709 Eigen::Map<const memoryx::KBM::Matrix> rawJointValues =
710 mapMatrix(nDoF, rawJointValuesAccumulator);
711
712 ARMARX_VERBOSE << "raw jointValue samples: " << rawJointValues.cols();
713
714 std::vector<memoryx::KBM::Real> result(nDoF);
715 Eigen::Map<memoryx::KBM::Vector> averages(result.data(), nDoF);
716 averages = rawJointValues.rowwise().mean();
717
718 Ice::DoubleSeq resultFloat(result.begin(), result.end());
719 return resultFloat;
720 }
721
722 Ice::DoubleSeq
723 KBMComponent::getSpreadAngles(int nDoF, const Ice::Current& c)
724 {
725 Eigen::Map<const memoryx::KBM::Matrix> proprioceptions =
726 mapMatrix(nDoF, proprioceptionAccumulator);
727
728 ARMARX_VERBOSE << "proprioception samples: " << proprioceptions.cols();
729
730 std::vector<memoryx::KBM::Real> result(nDoF);
731 Eigen::Map<memoryx::KBM::Vector> spreadAngles(result.data(), nDoF);
732
733 memoryx::KBM::Vector leftSpreadAngles = proprioceptions.rowwise().minCoeff();
734 memoryx::KBM::Vector rightSpreadAngles = proprioceptions.rowwise().maxCoeff();
735 ARMARX_VERBOSE << "leftSpreadAngles: " << leftSpreadAngles;
736 ARMARX_VERBOSE << "rightSpreadAngles: " << rightSpreadAngles;
737
738 for (int i = 0; i < nDoF; i++)
739 {
740 spreadAngles[i] = fmin(fabs(leftSpreadAngles[i]), fabs(rightSpreadAngles[i]));
741 }
742
743 ARMARX_VERBOSE << "data spreadAngles: " << spreadAngles;
744
745 Ice::DoubleSeq resultFloat(result.begin(), result.end());
746 return resultFloat;
747 }
748
749 void
751 const std::vector<memoryx::KBM::Real>& rawJointValuesAverages,
752 const Ice::Current& c)
753 {
754 if (proprioceptionAccumulator.size() != 0)
755 {
756 ARMARX_ERROR << "proprioception accumulator should be empty, when using setting it "
757 "from raw joint accumulator";
758 }
759
760 if (rawJointValuesAccumulator.size() % rawJointValuesAverages.size() != 0)
761 {
762 ARMARX_ERROR << "rawJointValuesAverages size is incompatible to accumulator size";
763 }
764
765 ARMARX_INFO << "rawJointValuesAccumualtor: " << rawJointValuesAccumulator;
766 std::vector<memoryx::KBM::Real> proprioceptionsFromRawJointAccumulator;
767 unsigned int i = 0;
768
769 for (auto jointValue : rawJointValuesAccumulator)
770 {
771 if (i == rawJointValuesAverages.size())
772 {
773 i = 0;
774 }
775
776 float offset = rawJointValuesAverages[i];
777 i++;
778
779 /*
780 * Convert to proprioception
781 */
782 proprioceptionsFromRawJointAccumulator.push_back(jointValue - offset);
783 }
784
785 ARMARX_INFO << "proprioceptionsFromRawJointAccumulator: "
786 << proprioceptionsFromRawJointAccumulator;
787 proprioceptionAccumulator = proprioceptionsFromRawJointAccumulator;
788
789 /*
790 * TODO: move to separate method
791 */
792 }
793
794 void
795 KBMComponent::setPositionLimits(int nDim, const Ice::Current& c)
796 {
797 Eigen::Map<const memoryx::KBM::Matrix> positions = mapMatrix(nDim, positionAccumulator);
798 minima = positions.rowwise().minCoeff();
799 maxima = positions.rowwise().maxCoeff();
800 ARMARX_IMPORTANT << "setting position minima: " << minima.transpose();
801 ARMARX_IMPORTANT << "setting position maxima: " << maxima.transpose();
802 }
803
804 void
805 KBMComponent::onlineAccumulator(const std::string& name,
806 Ice::Float learningRate,
807 const Ice::Current& c)
808 {
809 online(name, proprioceptionAccumulator, positionAccumulator, learningRate, c);
810 }
811
812 void
814 Ice::Float learningRate,
815 const Ice::Current& c)
816 {
817 onlineVerbose(name,
818 proprioceptionAccumulator,
819 positionAccumulator,
820 learningRate,
821 evaluationProprioceptionAccumulator,
822 evaluationPositionAccumulator,
823 c);
824 }
825
826 void
827 KBMComponent::batchAccumulator(const std::string& name,
828 Ice::Int method,
829 Ice::Float threshold,
830 const Ice::Current& c)
831 {
832 batch(name, proprioceptionAccumulator, positionAccumulator, method, threshold, c);
833 }
834
835 void
836 KBMComponent::printErrorsAccumulator(const std::string& name, const Ice::Current& c)
837 {
838 printErrors(name, proprioceptionAccumulator, positionAccumulator, c);
839 }
840
841 void
842 KBMComponent::predictAccumulator(const std::string& name, const Ice::Current& c)
843 {
844 predict(name, proprioceptionAccumulator, c);
845 }
846
847 /*
848 *
849 *
850 * The following methods should alredady have orientation support
851 *
852 *
853 */
854
855 void
856 KBMComponent::accumulatePositions(const std::vector<memoryx::KBM::Real>& position,
857 const Ice::Current& c)
858 {
859 /*
860 * accumulate positions
861 */
862 positionAccumulator.insert(positionAccumulator.end(), position.begin(), position.end());
863
864 //ARMARX_VERBOSE << "Positions Accumulator: " << positionAccumulator;
865 }
866
867 void
868 KBMComponent::accumulateProprioceptions(const std::vector<memoryx::KBM::Real>& proprioception,
869 const Ice::Current& c)
870 {
871 /*
872 * accumulate proprioceptions
873 */
874 proprioceptionAccumulator.insert(
875 proprioceptionAccumulator.end(), proprioception.begin(), proprioception.end());
876
877 //ARMARX_VERBOSE << "Proprioception Accumulator: " << proprioceptionAccumulator;
878 }
879
880 void
882 const std::vector<memoryx::KBM::Real>& rawJointValuesSequence,
883 const Ice::Current& c)
884 {
885 /*
886 * accumulate raw joint values
887 */
888 rawJointValuesAccumulator.insert(rawJointValuesAccumulator.end(),
889 rawJointValuesSequence.begin(),
890 rawJointValuesSequence.end());
891
892 //ARMARX_VERBOSE << "Raw Joint Values Accumulator: " << rawJointValuesAccumulator;
893 }
894
895 void
896 KBMComponent::accumulateEvaluationPositions(const std::vector<memoryx::KBM::Real>& position,
897 const Ice::Current& c)
898 {
899 /*
900 * accumulate positions
901 */
902 evaluationPositionAccumulator.insert(
903 evaluationPositionAccumulator.end(), position.begin(), position.end());
904
905 //ARMARX_VERBOSE << "Positions Accumulator: " << positionAccumulator;
906 }
907
908 void
910 const std::vector<memoryx::KBM::Real>& proprioception,
911 const Ice::Current& c)
912 {
913 /*
914 * accumulate proprioceptions
915 */
916 evaluationProprioceptionAccumulator.insert(evaluationProprioceptionAccumulator.end(),
917 proprioception.begin(),
918 proprioception.end());
919
920 //ARMARX_VERBOSE << "Proprioception Accumulator: " << proprioceptionAccumulator;
921 }
922
923 std::vector<memoryx::KBM::Real>
924 KBMComponent::solveGlobalIK(const std::string& name,
925 const std::vector<memoryx::KBM::Real>& targetPosition,
926 const Ice::Current& c)
927 {
928 /*
929 * Check input variables
930 */
932
933 if (!kbm)
934 {
935 ARMARX_WARNING << "returning empty solution";
936 return std::vector<memoryx::KBM::Real>();
937 }
938
939 Eigen::Map<const memoryx::KBM::Vector> target(mapPosition(*kbm, targetPosition));
940
941 /*
942 * Create output variables
943 */
944 std::vector<memoryx::KBM::Real> result(kbm->getNDoF());
945 Eigen::Map<memoryx::KBM::Vector> solution(result.data(), kbm->getNDoF());
946
947 /*
948 * Solve Global IK
949 */
951 memoryx::KBM::Inverse::solveGlobalIK(kbm, target, target));
952 ARMARX_IMPORTANT << "Global IK found " << solutions.size() << " solutions";
953
954 for (unsigned int i = 0; i < solutions.size(); ++i)
955 {
956 memoryx::KBM::Inverse::Solution solution = solutions[i];
957 ARMARX_IMPORTANT << i << ". solution: " << solution.spreadAngles;
958 }
959
960 // TODO: support VirtualRobot KBMs by switching proprioceptions
961 if (createdFromVirtualRobot[name])
962 {
963 ARMARX_ERROR << "solveGlobalIK does not yet support KBMs created from Virtual Robot!";
964 }
965
966 return result; // TODO: currently always 0
967 }
968
969 std::vector<memoryx::KBM::Real>
971 const std::string& name,
972 const std::vector<memoryx::KBM::Real>& targetPosition,
973 const std::vector<memoryx::KBM::Real>& currentPosition,
974 const std::vector<memoryx::KBM::Real>& currentProprioception,
975 const std::vector<memoryx::KBM::Real>& lowerProprioceptionLimitsSequence,
976 const std::vector<memoryx::KBM::Real>& upperProprioceptionLimitsSequence,
977 const Ice::Current& c)
978 {
979 /*
980 * Check input variables
981 */
983
984 if (!kbm)
985 {
986 ARMARX_WARNING << "returning empty solution";
987 return std::vector<memoryx::KBM::Real>();
988 }
989
990 Eigen::Map<const memoryx::KBM::Vector> target(mapPosition(*kbm, targetPosition));
991 Eigen::Map<const memoryx::KBM::Vector> current(mapPosition(*kbm, currentPosition));
992 Eigen::Map<const memoryx::KBM::Vector> proprioception(
993 mapProprioception(*kbm, currentProprioception));
994 Eigen::Map<const memoryx::KBM::Vector> lowerProprioceptionLimits(
995 mapProprioception(*kbm, lowerProprioceptionLimitsSequence));
996 Eigen::Map<const memoryx::KBM::Vector> upperProprioceptionLimits(
997 mapProprioception(*kbm, upperProprioceptionLimitsSequence));
998
999 /*
1000 * Check position limits
1001 */
1002 if (minima.size() != target.size())
1003 {
1004 ARMARX_ERROR << "minima and target have different size";
1005 return std::vector<memoryx::KBM::Real>();
1006 }
1007
1008 if (maxima.size() != target.size())
1009 {
1010
1011 ARMARX_ERROR << "maxima and target have different size";
1012 return std::vector<memoryx::KBM::Real>();
1013 }
1014
1015 for (int i = 0; i < kbm->getNDim(); i++)
1016 {
1017 if (target[i] < minima[i] - 90)
1018 {
1019 ARMARX_ERROR << "target[" << i << "] = " << target[i] << " is smaller than minima "
1020 << minima[i];
1021 return std::vector<memoryx::KBM::Real>();
1022 }
1023
1024 if (target[i] > maxima[i] + 90)
1025 {
1026 ARMARX_ERROR << "target[" << i << "] = " << target[i] << " is greater than maxima "
1027 << maxima[i];
1028 return std::vector<memoryx::KBM::Real>();
1029 }
1030 }
1031
1032 /*
1033 * Create output variables
1034 */
1035 std::vector<memoryx::KBM::Real> result(kbm->getNDoF());
1036 Eigen::Map<memoryx::KBM::Vector> solution(result.data(), kbm->getNDoF());
1037
1038 /*
1039 * Solve Differential IK
1040 */
1041 bool solved;
1042
1043 if (createdFromVirtualRobot[name])
1044 {
1045 /*
1046 * support VirtualRobot KBMs by reversing joint order
1047 */
1048 ARMARX_IMPORTANT << "reversing proprioception and limits order because KBM was created "
1049 "from VirtualRobot";
1050 solved = KBMDifferentialIK::solveMany(*kbm,
1051 target,
1052 current,
1053 proprioception.reverse(),
1054 lowerProprioceptionLimits.reverse(),
1055 upperProprioceptionLimits.reverse(),
1056 solution);
1057 }
1058 else
1059 {
1060 solved = KBMDifferentialIK::solveMany(*kbm,
1061 target,
1062 current,
1063 proprioception,
1064 lowerProprioceptionLimits,
1065 upperProprioceptionLimits,
1066 solution);
1067 }
1068
1069 if (createdFromVirtualRobot[name])
1070 {
1071 /*
1072 * support VirtualRobot KBMs by reversing joint order
1073 */
1074 ARMARX_IMPORTANT << "solution before reverse: " << solution.transpose();
1075 solution = solution.colwise().reverse();
1076 ARMARX_IMPORTANT << "solution after reverse: " << solution.transpose();
1077 }
1078
1079 /*
1080 * Output
1081 */
1082 ARMARX_IMPORTANT << "solved = " << solved;
1083 ARMARX_IMPORTANT << "solution = " << solution;
1084 ARMARX_IMPORTANT << "result (check if equal to solution!) = " << result;
1085
1086 return result;
1087 }
1088
1089 /*
1090 *
1091 *
1092 * KBM Differential Inverse Kinematics
1093 *
1094 *
1095 */
1096
1097 float
1099 {
1100 return LO + static_cast<float>(rand()) / (static_cast<float>(RAND_MAX / (HI - LO)));
1101 }
1102
1105 const memoryx::KBM::Vector& target,
1106 const memoryx::KBM::Vector& position,
1107 const memoryx::KBM::Vector& proprioception,
1108 float stepSizeFactor,
1109 float maxStepSize)
1110 {
1111
1112 /*
1113 * Calculate postion deltas
1114 */
1115 memoryx::KBM::Vector positionDeltas = stepSizeFactor * (target - position);
1116
1117 if (positionDeltas.norm() > maxStepSize)
1118 {
1119 positionDeltas *= maxStepSize / positionDeltas.norm();
1120 }
1121
1122 /*
1123 * Calcualte the jacobian matrix
1124 */
1125 memoryx::KBM::Matrix jacobian = kbm.getJacobian(proprioception);
1126
1127 /*
1128 * Calculate pseudo inverse using function from simox
1129 * simox uses an algorithm that is numerically stable
1130 */
1131#ifdef KBM_USE_DOUBLE_PRECISION
1132 memoryx::KBM::Matrix pseudoInverse = VirtualRobot::MathTools::getPseudoInverseD(jacobian);
1133#else
1134 memoryx::KBM::Matrix pseudoInverse = VirtualRobot::MathTools::getPseudoInverse(jacobian);
1135#endif
1136
1137 /*
1138 * Calculate joint deltas
1139 */
1140 memoryx::KBM::Vector jointDeltas = pseudoInverse * positionDeltas;
1141 return jointDeltas;
1142 }
1143
1146 const memoryx::KBM::Vector& upperProprioceptionLimits)
1147 {
1148 if (lowerProprioceptionLimits.size() != upperProprioceptionLimits.size())
1149 {
1150 ARMARX_ERROR_S << "limits must have same length";
1151 }
1152
1153 int n = lowerProprioceptionLimits.size();
1154 memoryx::KBM::Vector result(n);
1155
1156 for (int i = 0; i < n; i++)
1157 {
1158 float l = lowerProprioceptionLimits(i);
1159 float u = upperProprioceptionLimits(i);
1160 result(i) = randomFloat(l, u);
1161 }
1162
1163 return result;
1164 }
1165
1166 bool
1168 const memoryx::KBM::Vector& solution,
1169 const memoryx::KBM::Vector& lowerProprioceptionLimits,
1170 const memoryx::KBM::Vector& upperProprioceptionLimits)
1171 {
1172 /*
1173 * Check parameters
1174 */
1175 int n = solution.size();
1176
1177 if (lowerProprioceptionLimits.size() != n)
1178 {
1179 ARMARX_ERROR_S << "lower proprioception limits have wrong length: "
1180 << lowerProprioceptionLimits.size() << ", should be " << n;
1181 }
1182
1183 if (upperProprioceptionLimits.size() != n)
1184 {
1185 ARMARX_ERROR_S << "upper proprioception limits have wrong length: "
1186 << upperProprioceptionLimits.size() << ", should be " << n;
1187 }
1188
1189 /*
1190 * Check limits
1191 */
1192 for (int i = 0; i < n; i++)
1193 {
1194 if ((solution(i) < lowerProprioceptionLimits(i)) ||
1195 (solution(i) > upperProprioceptionLimits(i)))
1196 {
1197 return false;
1198 }
1199 }
1200
1201 return true;
1202 }
1203
1204 bool
1206 Eigen::Map<memoryx::KBM::Vector>& solution,
1207 const memoryx::KBM::Vector& lowerProprioceptionLimits,
1208 const memoryx::KBM::Vector& upperProprioceptionLimits)
1209 {
1210 /*
1211 * Check parameters
1212 */
1213 int n = solution.size();
1214
1215 if (lowerProprioceptionLimits.size() != n)
1216 {
1217 ARMARX_ERROR_S << "lower proprioception limits have wrong length: "
1218 << lowerProprioceptionLimits.size() << ", should be " << n;
1219 }
1220
1221 if (upperProprioceptionLimits.size() != n)
1222 {
1223 ARMARX_ERROR_S << "upper proprioception limits have wrong length: "
1224 << upperProprioceptionLimits.size() << ", should be " << n;
1225 }
1226
1227 /*
1228 * Check limits
1229 */
1230 bool inLimits = true;
1231
1232 for (int i = 0; i < n; i++)
1233 {
1234 if (solution(i) < lowerProprioceptionLimits(i))
1235 {
1236 solution(i) = lowerProprioceptionLimits(i);
1237 inLimits = false;
1238 }
1239
1240 if (solution(i) > upperProprioceptionLimits(i))
1241 {
1242 solution(i) = upperProprioceptionLimits(i);
1243 inLimits = false;
1244 }
1245 }
1246
1247 return inLimits;
1248 }
1249
1250 bool
1252 const memoryx::KBM::Vector& targetPosition,
1253 const memoryx::KBM::Vector& currentPosition,
1254 const memoryx::KBM::Vector& currentProprioception,
1255 const memoryx::KBM::Vector& lowerProprioceptionLimits,
1256 const memoryx::KBM::Vector& upperProprioceptionLimits,
1257 Eigen::Map<memoryx::KBM::Vector>& solution,
1258 bool applyLimits,
1259 int maxSolves,
1260 float positionTolerance,
1261 float minimumDelta,
1262 bool requireImprovment,
1263 int maxSteps,
1264 float stepSizeFactor,
1265 float maxStepSize)
1266 {
1267
1268 float bestDistance = FLT_MAX;
1269 bool inLimits = false;
1270
1271 std::vector<memoryx::KBM::Real> newSolutionSequence(kbm.getNDoF());
1272 Eigen::Map<memoryx::KBM::Vector> newSolution(newSolutionSequence.data(), kbm.getNDoF());
1273
1274 for (int i = 0; i < maxSolves; i++)
1275 {
1276 memoryx::KBM::Vector startProprioception;
1277
1278 if (i == 0)
1279 {
1280 startProprioception = currentProprioception;
1281 }
1282 else
1283 {
1284 startProprioception =
1285 randomProprioception(lowerProprioceptionLimits, upperProprioceptionLimits);
1286 }
1287
1288 ARMARX_IMPORTANT_S << "iteration " << (i + 1)
1289 << " proprioception: " << startProprioception.transpose();
1290 memoryx::KBM::Vector startPosition = kbm.predict(startProprioception);
1291
1292 /*
1293 * TODO: it's probably much more efficient, to use the limits
1294 * in the solve method itself and consider the limits right away.
1295 */
1297 targetPosition,
1298 startPosition,
1299 startProprioception,
1300 newSolution,
1301 positionTolerance,
1302 minimumDelta,
1303 requireImprovment,
1304 maxSteps,
1305 stepSizeFactor,
1306 maxStepSize);
1307
1308 /*
1309 * When using applyLimits, all solutions will be in limits, but solutions
1310 * that were outside the limits will probably get worse.
1311 *
1312 * Note: when using this option, the following limits check and solution ignoring
1313 * doesn't have any effect.
1314 */
1315 if (applyLimits)
1316 {
1317 /*
1318 * distance before enforcing limits
1319 */
1320 memoryx::KBM::Vector testPosition = kbm.predict(newSolution);
1321 float distance = (targetPosition - testPosition).norm();
1322 ARMARX_INFO_S << "distance before enforcing limits: " << distance;
1323
1324 /*
1325 * modify the solution to be within the limits
1326 */
1327 ARMARX_VERBOSE_S << "solution before apply limits: " << newSolution;
1328 bool wasInLimits = applyProprioceptionLimits(
1329 newSolution, lowerProprioceptionLimits, upperProprioceptionLimits);
1330 ARMARX_VERBOSE_S << "solution after apply limits: " << newSolution;
1331
1332 if (!wasInLimits)
1333 {
1335 << "solution was not within limits, but enforced limits so now it is";
1336 }
1337
1338 /*
1339 * distance after enforcing limits
1340 */
1341 testPosition = kbm.predict(newSolution);
1342 distance = (targetPosition - testPosition).norm();
1343 ARMARX_INFO_S << "distance after enforcing limits: " << distance;
1344 }
1345
1346 /*
1347 * If we already found a solution in joint limits,
1348 * only accept other solutions in joint limits.
1349 */
1350 bool currentInLimits = checkProprioceptionLimits(
1351 newSolution, lowerProprioceptionLimits, upperProprioceptionLimits);
1352 ARMARX_INFO_S << "is in limits: " << currentInLimits;
1353
1354 if (applyLimits && !currentInLimits)
1355 {
1356 ARMARX_ERROR_S << "this should not be possible";
1357 ARMARX_ERROR_S << "problematic solution: " << newSolution;
1358 ARMARX_ERROR_S << "lower proprioception limits: " << lowerProprioceptionLimits;
1359 ARMARX_ERROR_S << "upper proprioception limits: " << upperProprioceptionLimits;
1360 }
1361
1362 if (inLimits && !currentInLimits)
1363 {
1365 << "ignoring solution because we already found one that is within the limits";
1366 continue;
1367 }
1368
1369 memoryx::KBM::Vector newPosition = kbm.predict(newSolution);
1370 float distance = (targetPosition - newPosition).norm();
1371
1372 ARMARX_INFO_S << "distance to target: " << distance << ", best known distance "
1373 << bestDistance;
1374
1375 if ((distance < bestDistance) || (currentInLimits && !inLimits))
1376 {
1377 bestDistance = distance;
1378 inLimits = currentInLimits;
1379 solution = newSolution;
1380 ARMARX_IMPORTANT_S << "found new solution: " << solution.transpose();
1381
1382 if (inLimits && (bestDistance < positionTolerance))
1383 {
1384 ARMARX_IMPORTANT_S << "found a solution within the limits with small distance, "
1385 "aborting search";
1386 break;
1387 }
1388 }
1389 }
1390
1391 ARMARX_IMPORTANT_S << "final distance: " << bestDistance << ", in limits: " << inLimits;
1392
1393 if (bestDistance < positionTolerance)
1394 {
1395 return true;
1396 }
1397 else
1398 {
1399 return false;
1400 }
1401 }
1402
1403 bool
1405 const memoryx::KBM::Vector& targetPosition,
1406 const memoryx::KBM::Vector& currentPosition,
1407 const memoryx::KBM::Vector& currentProprioception,
1408 Eigen::Map<memoryx::KBM::Vector>& solution,
1409 float positionTolerance,
1410 float minimumDelta,
1411 bool requireImprovment,
1412 int maxSteps,
1413 float stepSizeFactor,
1414 float maxStepSize)
1415 {
1416
1417 assert(targetPosition.size() == kbm.getNDim());
1418 assert(currentPosition.size() == kbm.getNDim());
1419 assert(currentProprioception.size() == kbm.getNDoF());
1420 assert(solution.size() == kbm.getNDoF());
1421
1422 /*
1423 * In the beginning, our best guess it the current proprioception
1424 */
1425 solution = currentProprioception;
1426
1427 /*
1428 * The best known distance
1429 */
1430 float bestDistance = (targetPosition - currentPosition).norm();
1431
1432 /*
1433 * Check if the initial position is already a solution
1434 */
1435 if (bestDistance <= positionTolerance)
1436 {
1437 return true;
1438 }
1439
1440 /*
1441 * Current joint values
1442 */
1443 memoryx::KBM::Vector simulatedProprioception(currentProprioception);
1444
1445 /*
1446 * Current position
1447 */
1448 memoryx::KBM::Vector predictedPosition(currentPosition);
1449
1450 /*
1451 * Compare this to the predicted position
1452 */
1453 ARMARX_IMPORTANT_S << "current predicted position: " << kbm.predict(currentProprioception);
1454
1455 for (int step = 0; step < maxSteps; ++step)
1456 {
1457 /*
1458 * Calculate joint deltas
1459 */
1461 targetPosition,
1462 predictedPosition,
1463 simulatedProprioception,
1464 stepSizeFactor,
1465 maxStepSize);
1466
1467 /*
1468 * Calculate new joint values
1469 */
1470 simulatedProprioception += deltas;
1471
1472 /*
1473 * Predict new position
1474 */
1475 predictedPosition = kbm.predict(simulatedProprioception);
1476 ARMARX_IMPORTANT_S << "predicted position: " << predictedPosition;
1477
1478 /*
1479 * Check if the predicted position is a solution
1480 */
1481 float distance = (targetPosition - predictedPosition).norm();
1482 ARMARX_IMPORTANT_S << "distance to target: " << distance;
1483
1484 if (distance <= positionTolerance)
1485 {
1486 solution = simulatedProprioception;
1487 return true;
1488 }
1489
1490 /*
1491 * Stop calculation if result doesn't get better in each iteration
1492 */
1493 if (requireImprovment && (distance >= bestDistance))
1494 {
1495 return false;
1496 }
1497
1498 /*
1499 * If the new solution is better than the best known solution,
1500 * set it as the new solution!
1501 */
1502 if (distance < bestDistance)
1503 {
1504 solution = simulatedProprioception;
1505 bestDistance = distance;
1506 }
1507
1508 /*
1509 * Stop calculation if deltas are small
1510 */
1511 if (deltas.norm() < minimumDelta)
1512 {
1513 return false;
1514 }
1515 }
1516
1517 return false;
1518 }
1519
1520 Ice::StringSeq
1521 KBMComponent::kbmNames(const Ice::Current& c)
1522 {
1523 std::vector<std::string> kbmKeys;
1524
1525 for (auto it : kbms)
1526 {
1527 kbmKeys.push_back(it.first);
1528 }
1529
1530 return kbmKeys;
1531 }
1532
1533 void
1535 {
1536 positionAccumulator.clear();
1537 proprioceptionAccumulator.clear();
1538 rawJointValuesAccumulator.clear();
1539 }
1540
1541 void
1542 KBMComponent::storeToMemory(const std::string& kbmName,
1543 const std::string& nodeSetName,
1544 const std::string& referenceFrameName,
1545 const std::string& robotName,
1546 const Ice::Current& c)
1547 {
1548 memoryx::KBM::Models::KBM_ptr kbm = kbms[kbmName];
1549 if (kbm)
1550 {
1551 // get KBM segment from longterm memory
1552 auto kbmSeg = this->longtermMemoryPrx->getKBMSegment();
1553
1554 if (kbmSeg)
1555 {
1557 new memoryx::KBMData(new armarx::MatrixVariant(kbm->getControlNet()),
1558 nodeSetName,
1559 referenceFrameName,
1560 robotName));
1561 kbmSeg->addEntity(kbmData);
1562 }
1563 }
1564 else
1565 {
1566 ARMARX_WARNING << "Could not store KBM! No KBM segment in longterm memory!";
1567 }
1568
1569 return;
1570 }
1571
1572 void
1573 KBMComponent::restoreFromMemory(const std::string& kbmName,
1574 const std::string& nodeSetName,
1575 const std::string& referenceFrameName,
1576 const std::string& robotName,
1577 const Ice::Current& c)
1578 {
1579
1580 auto kbmSeg = this->longtermMemoryPrx->getKBMSegment();
1581 std::string kbmEntityName = robotName + "_" + referenceFrameName + "_" + nodeSetName;
1582
1583 if (kbmSeg && kbmSeg->hasEntityByName(kbmEntityName))
1584 {
1585 auto entity = kbmSeg->getEntityByName(kbmEntityName);
1586 memoryx::KBMDataPtr kbmData = memoryx::KBMDataPtr::dynamicCast(entity);
1587
1588 if (kbmData)
1589 {
1591 armarx::RemoteRobot::createLocalClone(this->robotStateComponentPrx);
1592 int nDoF = robot->getRobotNodeSet(nodeSetName)->getSize();
1593
1594 armarx::MatrixVariantPtr controlNet =
1595 armarx::MatrixVariantPtr::dynamicCast(kbmData->getControlNet());
1596
1598 nDoF,
1599 controlNet->rows,
1600 Eigen::VectorXd::Zero(nDoF),
1601 Eigen::VectorXd::Constant(nDoF, M_PI / 4.0f),
1602 controlNet->toEigen().cast<double>());
1603 createdFromVirtualRobot[kbmName] =
1604 false; //hacky, because also KBMs created from VirtualRobot could be stored to the longterm memory
1605 }
1606 else
1607 {
1608 ARMARX_WARNING << "Could not restore KBM entity specified by: " << kbmEntityName;
1609 }
1610 }
1611
1612 return;
1613 }
1614
1615 bool
1616 KBMComponent::isInMemory(const std::string& nodeSetName,
1617 const std::string& referenceFrameName,
1618 const std::string& robotName,
1619 const Ice::Current& c)
1620 {
1621 std::string kbmEntityName = robotName + "_" + referenceFrameName + "_" + nodeSetName;
1622
1623 return (this->longtermMemoryPrx->getKBMSegment() &&
1624 this->longtermMemoryPrx->getKBMSegment()->hasEntityByName(kbmEntityName));
1625 }
1626
1627 std::string
1629 {
1630 return "KBMComponent";
1631 }
1632} // namespace armarx
1633
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
#define M_PI
Definition MathTools.h:17
constexpr T c
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
Property< PropertyType > getProperty(const std::string &name)
Wrapper for the KBM class.
void onlineAccumulatorVerbose(const std::string &name, Ice::Float learningRate, const Ice::Current &c=Ice::emptyCurrent) override
onlineAccumulatorVerbose same as onlineAccumulator, but evaluates after each learning step
void predictAccumulator(const std::string &name, const Ice::Current &c=Ice::emptyCurrent) override
the same as predict but with the data from the accumulator, however the results are currently just pr...
void onInitComponent() override
Eigen::Map< const memoryx::KBM::Matrix > mapPositions(const memoryx::KBM::Models::KBM &kbm, const std::vector< memoryx::KBM::Real > &position)
mapPositions converts the position sequence into an Eigen::Map of a Matrix
void accumulateEvaluationPositions(const std::vector< memoryx::KBM::Real > &positionSequence, const Ice::Current &c=Ice::emptyCurrent) override
accumulates the given position in the evaluation accumulator
void setProprioceptionAccumulatorFromRawJointAccumulator(const std::vector< memoryx::KBM::Real > &rawJointValuesAverages, const Ice::Current &c=Ice::emptyCurrent) override
setProprioceptionAccumulatorFromRawJointAccumulator updates the proprioception accumulator
void printAccumulatorStatistics(int nDim, int nDoF, const Ice::Current &c=Ice::emptyCurrent) override
printAccumulatorStatistics prints information about the values in the accumulators
void accumulateEvaluationProprioceptions(const std::vector< memoryx::KBM::Real > &proprioceptions, const Ice::Current &c=Ice::emptyCurrent) override
accumualteProprioceptions accumulates proprioceptions in the evaluate accumulator
void onDisconnectComponent() override
Eigen::Map< const memoryx::KBM::Vector > mapPosition(const memoryx::KBM::Models::KBM &kbm, const std::vector< memoryx::KBM::Real > &position)
mapPosition converts the position sequence into an Eigen::Map of a Vector
void onlineVerbose(const std::string &name, const std::vector< memoryx::KBM::Real > &proprioceptionSequence, const std::vector< memoryx::KBM::Real > &positionSequence, Ice::Float learningRate, const std::vector< memoryx::KBM::Real > &evaluationProprioceptionSequence, const std::vector< memoryx::KBM::Real > &evaluationPositionSequence, const Ice::Current &c=Ice::emptyCurrent) override
same as online, but evaluates after each learning iteration
std::vector< memoryx::KBM::Real > solveGlobalIK(const std::string &name, const std::vector< memoryx::KBM::Real > &targetPosition, const Ice::Current &c=Ice::emptyCurrent) override
solveGlobalIK solves the global inverse kinematics for the given position
std::vector< memoryx::KBM::Real > solveDifferentialIK(const std::string &name, const std::vector< memoryx::KBM::Real > &targetPosition, const std::vector< memoryx::KBM::Real > &currentPosition, const std::vector< memoryx::KBM::Real > &proprioceptionSequence, const std::vector< memoryx::KBM::Real > &lowerProprioceptionLimitsSequence, const std::vector< memoryx::KBM::Real > &upperProprioceptionLimitsSequence, const Ice::Current &c=Ice::emptyCurrent) override
solveDifferentialIK solves the differential inverse kinematics for the given position
bool isInMemory(const std::string &nodeSetName, const std::string &referenceFrameName, const std::string &robotName, const Ice::Current &c) override
Ice::StringSeq kbmNames(const Ice::Current &c) override
Returns the names of the existing KBMs.
void onlineAccumulator(const std::string &name, Ice::Float learningRate, const Ice::Current &c=Ice::emptyCurrent) override
onlineAccumulator calls online after transforming the raw joint accumulator to proprioceptions
Ice::DoubleSeq getRawJointValuesAverages(int nDoF, const Ice::Current &c=Ice::emptyCurrent) override
getRawJointValuesAverages calculates the average raw joint values for each joint from the data stored...
void createArmar3KBM(const std::string &name, const Ice::StringSeq &robotNodeNames, const std::vector< memoryx::KBM::Real > &jointValueAverages, const std::vector< memoryx::KBM::Real > &spreadAnglesSequence, const std::string &tcpName, bool useOrientation, const Ice::Current &c=Ice::emptyCurrent) override
createArmar3KBM creates a KBM from a VirtualRobot model
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void clearAccumulators(const Ice::Current &c) override
Clears the accumulators.
std::vector< memoryx::KBM::Real > predict(const std::string &name, const std::vector< memoryx::KBM::Real > &proprioceptionSequence, const Ice::Current &c=Ice::emptyCurrent) override
predict the position for n samples of proprioception (forward kinematics)
void printErrors(const std::string &name, const std::vector< memoryx::KBM::Real > &proprioceptionSequence, const std::vector< memoryx::KBM::Real > &positionSequence, const Ice::Current &c=Ice::emptyCurrent) override
calls getErrors on the KBM and prints the result
void accumulateProprioceptions(const std::vector< memoryx::KBM::Real > &proprioceptions, const Ice::Current &c=Ice::emptyCurrent) override
accumualteProprioceptions accumulates proprioceptions
Ice::DoubleSeq getSpreadAngles(int nDoF, const Ice::Current &c=Ice::emptyCurrent) override
getSpreadAngles returns the spreadAngles calculated using the data in the proprioceptionAccumulator
void online(const std::string &name, const std::vector< memoryx::KBM::Real > &proprioceptionSequence, const std::vector< memoryx::KBM::Real > &positionSequence, Ice::Float learningRate, const Ice::Current &c=Ice::emptyCurrent) override
online learning of n training samples
void accumulateRawJointValues(const std::vector< memoryx::KBM::Real > &rawJointValuesSequence, const Ice::Current &c=Ice::emptyCurrent) override
accumualteRawJointValues accumulates raw joint values that will be transformed to proprioceptions lat...
void createKBM(const std::string &name, Ice::Int nDoF, Ice::Int dim, Ice::Float spreadAngle, const Ice::Current &c=Ice::emptyCurrent) override
createKBM creates a new KBM and makes it available under the given name
void batchAccumulator(const std::string &name, Ice::Int method, Ice::Float threshold, const Ice::Current &c=Ice::emptyCurrent) override
the same as batch but with the data from the accumulator
memoryx::KBM::Models::KBM_ptr getKBM(const std::string &name)
returns a pointer to the KBM with the given name if it exists or a nullptr if it doesn't
void onConnectComponent() override
static std::string GetDefaultName()
void storeToMemory(const std::string &, const std::string &, const std::string &, const std::string &, const Ice::Current &) override
void printErrorsAccumulator(const std::string &name, const Ice::Current &c=Ice::emptyCurrent) override
the same as printErrors but with the data from the accumulator
void restoreFromMemory(const std::string &, const std::string &, const std::string &, const std::string &, const Ice::Current &) override
Eigen::Map< const memoryx::KBM::Matrix > mapProprioceptions(const memoryx::KBM::Models::KBM &kbm, const std::vector< memoryx::KBM::Real > &proprioception)
mapProprioceptions converts the proprioception sequence into an Eigen::Map of a Matrix
void accumulatePositions(const std::vector< memoryx::KBM::Real > &positionSequence, const Ice::Current &c=Ice::emptyCurrent) override
accumulate adds the given position to the accumulator to use it later
Eigen::Map< const memoryx::KBM::Vector > mapProprioception(const memoryx::KBM::Models::KBM &kbm, const std::vector< memoryx::KBM::Real > &proprioception)
mapProprioception converts the proprioception sequence into an Eigen::Map of a Vector
void batch(const std::string &name, const std::vector< memoryx::KBM::Real > &proprioceptionSequence, const std::vector< memoryx::KBM::Real > &positionSequence, Ice::Int method, Ice::Float threshold, const Ice::Current &c=Ice::emptyCurrent) override
batch learning of n training samples
void onExitComponent() override
void setPositionLimits(int nDim, const Ice::Current &c=Ice::emptyCurrent) override
setPositionLimits sets the position limits using the data in the position accumulator
Eigen::Map< const memoryx::KBM::Matrix > mapMatrix(int rows, const std::vector< memoryx::KBM::Real > &data)
maps a sequence to an Eigen::Map
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
VirtualRobot::RobotPtr createLocalClone()
Clones the structure of this remote robot to a local instance.
The Kinematic B\'ezier Maps.
Definition kbm.h:82
Matrix predict(const Matrix &proprioception, int dim=0) const
Predicts a value of the FK given a set of joint angles.
Definition kbm.cpp:343
Matrix getJacobian(const Vector &proprioception) const
Computes the partial derivative with respect to a configuration.
Definition kbm.cpp:412
Optimization
Enum for the preferred optimization method during batch learning.
Definition kbm.h:86
static KBM_ptr createKBM(int nDoF, int dim, const Vector &center, const Vector &spreadAngles, const Matrix &controlNet)
Factory methods.
Definition kbm.cpp:455
static KBM_ptr createFromVirtualRobot(VirtualRobot::KinematicChainPtr chain, VirtualRobot::SceneObjectPtr FoR, const Vector &spreadAngles, bool useOrientation=false)
Definition kbm.cpp:494
int getNDoF() const
Get the number of degrees of freedom (DoF).
Definition kbm.cpp:437
int getNDim() const
Definition kbm.cpp:443
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_ERROR_S
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:216
#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_INFO_S
Definition Logging.h:202
#define ARMARX_VERBOSE_S
Definition Logging.h:207
#define ARMARX_IMPORTANT_S
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:210
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
std::shared_ptr< class Robot > RobotPtr
Definition Bus.h:19
bool applyProprioceptionLimits(Eigen::Map< memoryx::KBM::Vector > &solution, const memoryx::KBM::Vector &lowerProprioceptionLimits, const memoryx::KBM::Vector &upperProprioceptionLimits)
apply limits to solution and return true, if solution has been changed = solution was not in limits
bool solveMany(const memoryx::KBM::Models::KBM &kbm, const memoryx::KBM::Vector &targetPosition, const memoryx::KBM::Vector &currentPosition, const memoryx::KBM::Vector &currentProprioception, const memoryx::KBM::Vector &lowerProprioceptionLimits, const memoryx::KBM::Vector &upperProprioceptionLimits, Eigen::Map< memoryx::KBM::Vector > &solution, bool applyLimits=true, int maxSolves=25, float positionTolerance=5.0f, float minimumDelta=0.0f, bool requireImprovment=false, int maxSteps=50, float stepSizeFactor=0.2f, float maxStepSize=FLT_MAX)
KBMDifferentialIK::solveMany runs solve many times.
bool solve(const memoryx::KBM::Models::KBM &kbm, const memoryx::KBM::Vector &targetPosition, const memoryx::KBM::Vector &currentPostion, const memoryx::KBM::Vector &currentProprioception, Eigen::Map< memoryx::KBM::Vector > &solution, float positionTolerance=5.0f, float minimumDelta=0.0f, bool requireImprovment=false, int maxSteps=50, float stepSizeFactor=0.2f, float maxStepSize=FLT_MAX)
solves the inverse kinematics
bool checkProprioceptionLimits(const memoryx::KBM::Vector &solution, const memoryx::KBM::Vector &lowerProprioceptionLimits, const memoryx::KBM::Vector &upperProprioceptionLimits)
check if solution is within the limits
memoryx::KBM::Vector calculateJointDeltas(const memoryx::KBM::Models::KBM &kbm, const memoryx::KBM::Vector &target, const memoryx::KBM::Vector &position, const memoryx::KBM::Vector &proprioception, float stepSizeFactor, float maxStepSize)
calculateJointDeltas is in internal function called by solve
float randomFloat(float LO, float HI)
randomFloat creates a random float between LO and HI
memoryx::KBM::Vector randomProprioception(const memoryx::KBM::Vector &lowerProprioceptionLimits, const memoryx::KBM::Vector &upperProprioceptionLimits)
Creates a vector of random values between the limits.
This file offers overloads of toIce() and fromIce() functions for STL container types.
armarx::MatrixDouble MatrixVariant
Definition KBMData.h:36
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
armarx::MatrixDoublePtr MatrixVariantPtr
Definition KBMData.h:37
std::vector< Solution > SolutionSet
Return type of the global inverse kinematics solvers.
Definition kbm.h:434
void solveGlobalIK(unsigned int recursion, int side, SolutionSet &solutionSet, Models::KBM_ptr kbm, const Vector &lower, const Vector &upper, Real resolution, Vector spreadAngles, Vector center)
Definition inverse.cpp:365
std::shared_ptr< KBM > KBM_ptr
Definition kbm.h:71
Eigen::MatrixXd Matrix
Definition kbm.h:40
Eigen::VectorXd Vector
Definition kbm.h:41
IceInternal::Handle< KBMData > KBMDataPtr
Definition KBMData.h:83
Eigen::MatrixXd pseudoInverse(const Eigen::MatrixXd &a, double epsilon=std::numeric_limits< double >::epsilon())
Definition pinv.hh:6
double norm(const Point &a)
Definition point.hpp:102
double distance(const Point &a, const Point &b)
Definition point.hpp:95
Return type of the evaluation (KBM::getErrors()) function providing insight in the statistics of the ...
Definition kbm.h:109
Real Median
Median of the error.
Definition kbm.h:115
Real STD
Standard deviation.
Definition kbm.h:113
Real IQR
Inerquartile range (50% of all errors): .
Definition kbm.h:117