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